2014-04-10 20:20:58 +02:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-05-29 09:17:15 +02:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 19:20:29 +01:00
// SPDX-License-Identifier: MIT
2014-04-10 20:20:58 +02:00
2014-05-26 02:11:25 +02:00
package setting
2014-04-10 20:20:58 +02:00
import (
2016-12-26 02:16:37 +01:00
"encoding/base64"
2019-04-02 09:48:31 +02:00
"fmt"
2020-04-23 00:47:23 +02:00
"math"
2017-10-02 15:55:09 +02:00
"net"
2014-09-14 19:35:22 +02:00
"net/url"
2014-04-10 20:20:58 +02:00
"os"
"os/exec"
"path"
"path/filepath"
2019-04-28 21:48:46 +02:00
"runtime"
2016-08-11 23:46:33 +02:00
"strconv"
2014-04-10 20:20:58 +02:00
"strings"
2021-06-15 03:15:12 +02:00
"text/template"
2014-07-24 22:31:59 +02:00
"time"
2014-04-10 20:20:58 +02:00
2022-10-12 07:18:26 +02:00
"code.gitea.io/gitea/modules/container"
2022-10-28 11:58:32 +02:00
"code.gitea.io/gitea/modules/generate"
2021-07-24 18:03:58 +02:00
"code.gitea.io/gitea/modules/json"
2016-12-22 19:12:23 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
2020-11-28 03:42:08 +01:00
"code.gitea.io/gitea/modules/util"
2017-01-23 23:44:23 +01:00
2020-10-11 02:38:09 +02:00
gossh "golang.org/x/crypto/ssh"
2019-01-09 18:22:57 +01:00
ini "gopkg.in/ini.v1"
2014-04-10 20:20:58 +02:00
)
2016-11-27 11:14:25 +01:00
// Scheme describes protocol types
2014-05-26 02:11:25 +02:00
type Scheme string
2014-04-14 00:12:07 +02:00
2016-11-27 11:14:25 +01:00
// enumerates all the scheme types
2014-05-26 02:11:25 +02:00
const (
2021-12-06 05:46:11 +01:00
HTTP Scheme = "http"
HTTPS Scheme = "https"
FCGI Scheme = "fcgi"
FCGIUnix Scheme = "fcgi+unix"
HTTPUnix Scheme = "http+unix"
2014-05-26 02:11:25 +02:00
)
2014-04-10 20:20:58 +02:00
2016-11-27 11:14:25 +01:00
// LandingPage describes the default page
2014-11-25 00:47:59 +01:00
type LandingPage string
2016-11-27 11:14:25 +01:00
// enumerates all the landing page types
2014-11-25 00:47:59 +01:00
const (
2017-11-20 08:48:09 +01:00
LandingPageHome LandingPage = "/"
LandingPageExplore LandingPage = "/explore"
LandingPageOrganizations LandingPage = "/explore/organizations"
2020-01-06 17:50:44 +01:00
LandingPageLogin LandingPage = "/user/login"
2014-11-25 00:47:59 +01:00
)
2018-07-05 06:13:05 +02:00
// enumerates all the types of captchas
const (
ImageCaptcha = "image"
ReCaptcha = "recaptcha"
2020-10-03 05:37:53 +02:00
HCaptcha = "hcaptcha"
2022-08-10 15:20:10 +02:00
MCaptcha = "mcaptcha"
2018-07-05 06:13:05 +02:00
)
2016-11-27 11:14:25 +01:00
// settings
2014-04-10 20:20:58 +02:00
var (
2021-02-19 22:36:43 +01:00
// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
AppVer string
// AppBuiltWith represents a human readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
AppBuiltWith string
// AppStartTime store time gitea has started
AppStartTime time . Time
// AppName is the Application name, used in the page title.
// It maps to ini:"APP_NAME"
AppName string
// AppURL is the Application ROOT_URL. It always has a '/' suffix
// It maps to ini:"ROOT_URL"
AppURL string
// AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
AppSubURL string
// AppPath represents the path to the gitea binary
AppPath string
// AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
// If that is not set it is the default set here by the linker or failing that the directory of AppPath.
//
// AppWorkPath is used as the base path for several other paths.
AppWorkPath string
// AppDataPath is the default path for storing data.
2022-11-10 02:22:31 +01:00
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
2021-02-19 22:36:43 +01:00
AppDataPath string
2022-03-22 17:59:57 +01:00
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
2022-11-10 02:22:31 +01:00
// It maps to ini:"LOCAL_ROOT_URL" in [server]
2022-03-22 17:59:57 +01:00
LocalURL string
2022-08-23 14:58:04 +02:00
// AssetVersion holds a opaque value that is used for cache-busting assets
AssetVersion string
2014-05-26 02:11:25 +02:00
2015-12-18 04:31:34 +01:00
// Server settings
2022-08-21 20:20:43 +02:00
Protocol Scheme
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
ProxyProtocolHeaderTimeout time . Duration
ProxyProtocolAcceptUnknown bool
Domain string
HTTPAddr string
HTTPPort string
LocalUseProxyProtocol bool
RedirectOtherPort bool
RedirectorUseProxyProtocol bool
PortToRedirect string
OfflineMode bool
CertFile string
KeyFile string
StaticRootPath string
StaticCacheTime time . Duration
EnableGzip bool
LandingPageURL LandingPage
LandingPageCustom string
UnixSocketPermission uint32
EnablePprof bool
PprofDataPath string
EnableAcme bool
AcmeTOS bool
AcmeLiveDirectory string
AcmeEmail string
AcmeURL string
AcmeCARoot string
SSLMinimumVersion string
SSLMaximumVersion string
SSLCurvePreferences [ ] string
SSLCipherSuites [ ] string
GracefulRestartable bool
GracefulHammerTime time . Duration
StartupTimeout time . Duration
PerWriteTimeout = 30 * time . Second
PerWritePerKbTimeout = 10 * time . Second
StaticURLPrefix string
AbsoluteAssetURL string
2014-05-26 02:11:25 +02:00
2016-12-29 11:51:15 +01:00
SSH = struct {
2021-06-15 03:15:12 +02:00
Disabled bool ` ini:"DISABLE_SSH" `
StartBuiltinServer bool ` ini:"START_SSH_SERVER" `
BuiltinServerUser string ` ini:"BUILTIN_SSH_SERVER_USER" `
2022-08-21 20:20:43 +02:00
UseProxyProtocol bool ` ini:"SSH_SERVER_USE_PROXY_PROTOCOL" `
2021-06-15 03:15:12 +02:00
Domain string ` ini:"SSH_DOMAIN" `
Port int ` ini:"SSH_PORT" `
2022-02-07 22:56:45 +01:00
User string ` ini:"SSH_USER" `
2021-06-15 03:15:12 +02:00
ListenHost string ` ini:"SSH_LISTEN_HOST" `
ListenPort int ` ini:"SSH_LISTEN_PORT" `
RootPath string ` ini:"SSH_ROOT_PATH" `
ServerCiphers [ ] string ` ini:"SSH_SERVER_CIPHERS" `
ServerKeyExchanges [ ] string ` ini:"SSH_SERVER_KEY_EXCHANGES" `
ServerMACs [ ] string ` ini:"SSH_SERVER_MACS" `
ServerHostKeys [ ] string ` ini:"SSH_SERVER_HOST_KEYS" `
KeyTestPath string ` ini:"SSH_KEY_TEST_PATH" `
KeygenPath string ` ini:"SSH_KEYGEN_PATH" `
AuthorizedKeysBackup bool ` ini:"SSH_AUTHORIZED_KEYS_BACKUP" `
AuthorizedPrincipalsBackup bool ` ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP" `
AuthorizedKeysCommandTemplate string ` ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE" `
AuthorizedKeysCommandTemplateTemplate * template . Template ` ini:"-" `
MinimumKeySizeCheck bool ` ini:"-" `
MinimumKeySizes map [ string ] int ` ini:"-" `
CreateAuthorizedKeysFile bool ` ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE" `
CreateAuthorizedPrincipalsFile bool ` ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE" `
ExposeAnonymous bool ` ini:"SSH_EXPOSE_ANONYMOUS" `
AuthorizedPrincipalsAllow [ ] string ` ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW" `
AuthorizedPrincipalsEnabled bool ` ini:"-" `
TrustedUserCAKeys [ ] string ` ini:"SSH_TRUSTED_USER_CA_KEYS" `
TrustedUserCAKeysFile string ` ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME" `
TrustedUserCAKeysParsed [ ] gossh . PublicKey ` ini:"-" `
PerWriteTimeout time . Duration ` ini:"SSH_PER_WRITE_TIMEOUT" `
PerWritePerKbTimeout time . Duration ` ini:"SSH_PER_WRITE_PER_KB_TIMEOUT" `
2016-12-29 11:51:15 +01:00
} {
2021-06-15 03:15:12 +02:00
Disabled : false ,
StartBuiltinServer : false ,
Domain : "" ,
Port : 22 ,
2022-02-10 17:04:16 +01:00
ServerCiphers : [ ] string { "chacha20-poly1305@openssh.com" , "aes128-ctr" , "aes192-ctr" , "aes256-ctr" , "aes128-gcm@openssh.com" , "aes256-gcm@openssh.com" } ,
2022-03-16 02:59:53 +01:00
ServerKeyExchanges : [ ] string { "curve25519-sha256" , "ecdh-sha2-nistp256" , "ecdh-sha2-nistp384" , "ecdh-sha2-nistp521" , "diffie-hellman-group14-sha256" , "diffie-hellman-group14-sha1" } ,
2022-02-10 17:04:16 +01:00
ServerMACs : [ ] string { "hmac-sha2-256-etm@openssh.com" , "hmac-sha2-256" , "hmac-sha1" } ,
2021-06-15 03:15:12 +02:00
KeygenPath : "ssh-keygen" ,
MinimumKeySizeCheck : true ,
2022-07-06 22:49:27 +02:00
MinimumKeySizes : map [ string ] int { "ed25519" : 256 , "ed25519-sk" : 256 , "ecdsa" : 256 , "ecdsa-sk" : 256 , "rsa" : 2047 } ,
2021-06-15 03:15:12 +02:00
ServerHostKeys : [ ] string { "ssh/gitea.rsa" , "ssh/gogs.rsa" } ,
AuthorizedKeysCommandTemplate : "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}" ,
PerWriteTimeout : PerWriteTimeout ,
PerWritePerKbTimeout : PerWritePerKbTimeout ,
2016-02-28 02:48:39 +01:00
}
2015-12-18 04:31:34 +01:00
// Security settings
2019-11-14 23:39:48 +01:00
InstallLock bool
SecretKey string
LogInRememberDays int
CookieUserName string
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
2022-08-16 08:30:27 +02:00
ReverseProxyAuthFullName string
2021-03-15 23:27:28 +01:00
ReverseProxyLimit int
ReverseProxyTrustedProxies [ ] string
2019-11-14 23:39:48 +01:00
MinPasswordLength int
ImportLocalPaths bool
DisableGitHooks bool
2021-02-11 18:34:34 +01:00
DisableWebhooks bool
2019-11-14 23:39:48 +01:00
OnlyAllowPushIfGiteaEnvironmentSet bool
PasswordComplexity [ ] string
PasswordHashAlgo string
2020-09-09 00:06:39 +02:00
PasswordCheckPwn bool
2021-08-17 20:30:42 +02:00
SuccessfulTokensCacheSize int
2014-04-10 20:20:58 +02:00
2022-03-29 10:27:37 +02:00
Camo = struct {
Enabled bool
ServerURL string ` ini:"SERVER_URL" `
HMACKey string ` ini:"HMAC_KEY" `
Allways bool
} { }
2015-12-18 04:31:34 +01:00
// UI settings
2016-12-23 08:18:05 +01:00
UI = struct {
2019-08-25 19:06:36 +02:00
ExplorePagingNum int
2022-06-25 19:06:01 +02:00
SitemapPagingNum int
2019-08-25 19:06:36 +02:00
IssuePagingNum int
RepoSearchPagingNum int
2019-12-06 06:34:54 +01:00
MembersPagingNum int
2019-08-25 19:06:36 +02:00
FeedMaxCommitNum int
2020-08-11 16:48:13 +02:00
FeedPagingNum int
2022-03-30 10:42:47 +02:00
PackagesPagingNum int
2019-08-25 19:06:36 +02:00
GraphMaxCommitNum int
CodeCommentLines int
ReactionMaxUserNum int
ThemeColorMetaTag string
MaxDisplayFileSize int64
ShowUserEmail bool
DefaultShowFullName bool
DefaultTheme string
Themes [ ] string
2019-12-01 23:57:24 +01:00
Reactions [ ] string
2022-10-12 07:18:26 +02:00
ReactionsLookup container . Set [ string ] ` ini:"-" `
2021-06-29 16:28:38 +02:00
CustomEmojis [ ] string
CustomEmojisMap map [ string ] string ` ini:"-" `
2019-08-25 19:06:36 +02:00
SearchRepoDescription bool
2019-11-21 21:06:23 +01:00
UseServiceWorker bool
2022-08-25 20:38:41 +02:00
OnlyShowRelevantRepos bool
2016-07-23 18:23:54 +02:00
2020-04-24 05:57:38 +02:00
Notification struct {
2020-05-07 23:49:00 +02:00
MinTimeout time . Duration
TimeoutStep time . Duration
MaxTimeout time . Duration
EventSourceUpdateTime time . Duration
2020-04-24 05:57:38 +02:00
} ` ini:"ui.notification" `
2021-01-13 04:45:19 +01:00
SVG struct {
Enabled bool ` ini:"ENABLE_RENDER" `
} ` ini:"ui.svg" `
2021-03-29 22:44:28 +02:00
CSV struct {
MaxFileSize int64
} ` ini:"ui.csv" `
2016-07-23 18:23:54 +02:00
Admin struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} ` ini:"ui.admin" `
User struct {
RepoPagingNum int
} ` ini:"ui.user" `
2017-04-01 03:03:01 +02:00
Meta struct {
Author string
Description string
Keywords string
} ` ini:"ui.meta" `
2016-12-23 08:18:05 +01:00
} {
2017-10-27 08:10:54 +02:00
ExplorePagingNum : 20 ,
2022-06-25 19:06:01 +02:00
SitemapPagingNum : 20 ,
2022-08-06 12:43:40 +02:00
IssuePagingNum : 20 ,
RepoSearchPagingNum : 20 ,
2019-12-06 06:34:54 +01:00
MembersPagingNum : 20 ,
2017-10-27 08:10:54 +02:00
FeedMaxCommitNum : 5 ,
2020-08-11 16:48:13 +02:00
FeedPagingNum : 20 ,
2022-03-30 10:42:47 +02:00
PackagesPagingNum : 20 ,
2018-07-23 16:12:06 +02:00
GraphMaxCommitNum : 100 ,
2018-08-06 06:43:22 +02:00
CodeCommentLines : 4 ,
2017-12-04 00:14:26 +01:00
ReactionMaxUserNum : 10 ,
2017-10-27 08:10:54 +02:00
ThemeColorMetaTag : ` #6cc644 ` ,
MaxDisplayFileSize : 8388608 ,
2021-09-27 16:47:44 +02:00
DefaultTheme : ` auto ` ,
Themes : [ ] string { ` auto ` , ` gitea ` , ` arc-green ` } ,
2019-12-01 23:57:24 +01:00
Reactions : [ ] string { ` +1 ` , ` -1 ` , ` laugh ` , ` hooray ` , ` confused ` , ` heart ` , ` rocket ` , ` eyes ` } ,
2021-07-02 16:04:57 +02:00
CustomEmojis : [ ] string { ` git ` , ` gitea ` , ` codeberg ` , ` gitlab ` , ` github ` , ` gogs ` } ,
CustomEmojisMap : map [ string ] string { "git" : ":git:" , "gitea" : ":gitea:" , "codeberg" : ":codeberg:" , "gitlab" : ":gitlab:" , "github" : ":github:" , "gogs" : ":gogs:" } ,
2020-04-24 05:57:38 +02:00
Notification : struct {
2020-05-07 23:49:00 +02:00
MinTimeout time . Duration
TimeoutStep time . Duration
MaxTimeout time . Duration
EventSourceUpdateTime time . Duration
2020-04-24 05:57:38 +02:00
} {
2020-05-07 23:49:00 +02:00
MinTimeout : 10 * time . Second ,
TimeoutStep : 10 * time . Second ,
MaxTimeout : 60 * time . Second ,
EventSourceUpdateTime : 10 * time . Second ,
2020-04-24 05:57:38 +02:00
} ,
2021-01-13 04:45:19 +01:00
SVG : struct {
Enabled bool ` ini:"ENABLE_RENDER" `
} {
Enabled : true ,
} ,
2021-03-29 22:44:28 +02:00
CSV : struct {
MaxFileSize int64
} {
MaxFileSize : 524288 ,
} ,
2016-12-23 08:18:05 +01:00
Admin : struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} {
UserPagingNum : 50 ,
RepoPagingNum : 50 ,
NoticePagingNum : 25 ,
OrgPagingNum : 50 ,
} ,
User : struct {
RepoPagingNum int
} {
RepoPagingNum : 15 ,
} ,
2017-04-01 03:03:01 +02:00
Meta : struct {
Author string
Description string
Keywords string
} {
Author : "Gitea - Git with a cup of tea" ,
Description : "Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go" ,
Keywords : "go,git,self-hosted,gitea" ,
} ,
2016-07-23 18:23:54 +02:00
}
2014-04-10 20:20:58 +02:00
2017-03-15 01:52:01 +01:00
// Markdown settings
2016-12-23 08:18:05 +01:00
Markdown = struct {
2020-05-24 10:14:26 +02:00
EnableHardLineBreakInComments bool
EnableHardLineBreakInDocuments bool
CustomURLSchemes [ ] string ` ini:"CUSTOM_URL_SCHEMES" `
FileExtensions [ ] string
2022-09-13 18:33:37 +02:00
EnableMath bool
2016-12-23 08:18:05 +01:00
} {
2020-05-24 10:14:26 +02:00
EnableHardLineBreakInComments : true ,
EnableHardLineBreakInDocuments : false ,
FileExtensions : strings . Split ( ".md,.markdown,.mdown,.mkd" , "," ) ,
2022-09-13 18:33:37 +02:00
EnableMath : true ,
2015-09-01 14:32:02 +02:00
}
2017-02-14 13:16:00 +01:00
// Admin settings
Admin struct {
DisableRegularOrgCreation bool
2019-08-29 16:05:42 +02:00
DefaultEmailNotification string
2017-02-14 13:16:00 +01:00
}
2015-12-18 04:31:34 +01:00
// Log settings
2021-03-28 21:08:19 +02:00
LogLevel log . Level
2019-04-02 09:48:31 +02:00
StacktraceLogLevel string
2019-02-06 04:06:41 +01:00
LogRootPath string
2021-05-21 23:37:16 +02:00
EnableSSHLog bool
2019-04-02 09:48:31 +02:00
EnableXORMLog bool
2014-04-10 20:20:58 +02:00
2022-01-20 12:41:25 +01:00
DisableRouterLog bool
EnableAccessLog bool
AccessLogTemplate string
2015-12-18 04:31:34 +01:00
// Time settings
2014-07-24 22:31:59 +02:00
TimeFormat string
2019-08-15 16:46:21 +02:00
// UILocation is the location on the UI, so that we can display the time on UI.
DefaultUILocation = time . Local
2014-07-24 22:31:59 +02:00
2019-07-12 15:57:31 +02:00
CSRFCookieName = "_csrf"
CSRFCookieHTTPOnly = true
2014-04-10 20:20:58 +02:00
2020-12-23 20:09:54 +01:00
ManifestData string
2020-12-22 12:13:50 +01:00
2016-07-04 11:27:06 +02:00
// API settings
2016-12-23 08:18:05 +01:00
API = struct {
2019-02-06 19:19:26 +01:00
EnableSwagger bool
2019-06-12 23:07:24 +02:00
SwaggerURL string
2019-02-06 19:19:26 +01:00
MaxResponseItems int
DefaultPagingNum int
DefaultGitTreesPerPage int
2019-04-17 18:06:35 +02:00
DefaultMaxBlobSize int64
2016-12-23 08:18:05 +01:00
} {
2019-02-06 19:19:26 +01:00
EnableSwagger : true ,
2019-06-12 23:07:24 +02:00
SwaggerURL : "" ,
2019-02-06 19:19:26 +01:00
MaxResponseItems : 50 ,
DefaultPagingNum : 30 ,
DefaultGitTreesPerPage : 1000 ,
2019-04-17 18:06:35 +02:00
DefaultMaxBlobSize : 10485760 ,
2016-07-04 11:27:06 +02:00
}
2019-03-08 17:42:50 +01:00
OAuth2 = struct {
Enable bool
AccessTokenExpirationTime int64
RefreshTokenExpirationTime int64
2019-04-12 09:50:21 +02:00
InvalidateRefreshTokens bool
2021-06-17 23:56:46 +02:00
JWTSigningAlgorithm string ` ini:"JWT_SIGNING_ALGORITHM" `
2019-03-08 17:42:50 +01:00
JWTSecretBase64 string ` ini:"JWT_SECRET" `
2021-06-17 23:56:46 +02:00
JWTSigningPrivateKeyFile string ` ini:"JWT_SIGNING_PRIVATE_KEY_FILE" `
2020-04-23 00:47:23 +02:00
MaxTokenLength int
2019-03-08 17:42:50 +01:00
} {
Enable : true ,
AccessTokenExpirationTime : 3600 ,
RefreshTokenExpirationTime : 730 ,
2019-04-12 09:50:21 +02:00
InvalidateRefreshTokens : false ,
2021-06-17 23:56:46 +02:00
JWTSigningAlgorithm : "RS256" ,
JWTSigningPrivateKeyFile : "jwt/private.pem" ,
2020-04-23 00:47:23 +02:00
MaxTokenLength : math . MaxInt16 ,
2019-03-08 17:42:50 +01:00
}
2018-11-05 04:20:00 +01:00
// Metrics settings
Metrics = struct {
2021-10-05 20:39:37 +02:00
Enabled bool
Token string
EnabledIssueByLabel bool
EnabledIssueByRepository bool
2018-11-05 04:20:00 +01:00
} {
2021-10-05 20:39:37 +02:00
Enabled : false ,
Token : "" ,
EnabledIssueByLabel : false ,
EnabledIssueByRepository : false ,
2018-11-05 04:20:00 +01:00
}
2015-12-18 04:31:34 +01:00
// I18n settings
2020-06-10 20:35:27 +02:00
Langs [ ] string
Names [ ] string
2014-09-17 20:22:51 +02:00
2017-03-15 01:52:01 +01:00
// Highlight settings are loaded in modules/template/highlight.go
2015-12-18 04:31:34 +01:00
// Other settings
2016-09-01 07:01:32 +02:00
ShowFooterBranding bool
ShowFooterVersion bool
ShowFooterTemplateLoadTime bool
2022-11-21 06:14:58 +01:00
EnableFeed bool
2015-03-23 15:19:19 +01:00
2015-12-18 04:31:34 +01:00
// Global setting objects
2019-08-24 11:24:45 +02:00
Cfg * ini . File
CustomPath string // Custom directory path
CustomConf string
2020-08-18 13:21:24 +02:00
PIDFile = "/run/gitea.pid"
2020-08-15 22:15:27 +02:00
WritePIDFile bool
2020-11-17 23:44:52 +01:00
RunMode string
2021-10-20 16:37:19 +02:00
IsProd bool
2019-08-24 11:24:45 +02:00
RunUser string
IsWindows bool
HasRobotsTxt bool
2022-10-28 17:17:38 +02:00
EnableSitemap bool
2019-08-24 11:24:45 +02:00
InternalToken string // internal access token
2014-04-10 20:20:58 +02:00
)
2017-11-03 09:56:20 +01:00
func getAppPath ( ) ( string , error ) {
var appPath string
var err error
if IsWindows && filepath . IsAbs ( os . Args [ 0 ] ) {
appPath = filepath . Clean ( os . Args [ 0 ] )
} else {
appPath , err = exec . LookPath ( os . Args [ 0 ] )
2017-09-12 14:27:44 +02:00
}
2017-11-03 09:56:20 +01:00
2022-12-14 07:15:11 +01:00
if err != nil {
// FIXME: Once we switch to go 1.19 use !errors.Is(err, exec.ErrDot)
if ! strings . Contains ( err . Error ( ) , "cannot run executable found relative to current directory" ) {
return "" , err
}
appPath , err = filepath . Abs ( os . Args [ 0 ] )
}
2014-05-26 02:11:25 +02:00
if err != nil {
return "" , err
}
2017-11-03 09:56:20 +01:00
appPath , err = filepath . Abs ( appPath )
if err != nil {
return "" , err
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
2020-10-11 22:27:20 +02:00
return strings . ReplaceAll ( appPath , "\\" , "/" ) , err
2017-11-03 09:56:20 +01:00
}
func getWorkPath ( appPath string ) string {
2019-04-29 20:08:21 +02:00
workPath := AppWorkPath
2017-11-03 09:56:20 +01:00
2019-04-29 20:08:21 +02:00
if giteaWorkPath , ok := os . LookupEnv ( "GITEA_WORK_DIR" ) ; ok {
2017-11-03 09:56:20 +01:00
workPath = giteaWorkPath
2019-04-29 20:08:21 +02:00
}
if len ( workPath ) == 0 {
2017-11-03 09:56:20 +01:00
i := strings . LastIndex ( appPath , "/" )
if i == - 1 {
workPath = appPath
} else {
workPath = appPath [ : i ]
}
}
2022-06-06 16:43:17 +02:00
workPath = strings . ReplaceAll ( workPath , "\\" , "/" )
if ! filepath . IsAbs ( workPath ) {
log . Info ( "Provided work path %s is not absolute - will be made absolute against the current working directory" , workPath )
absPath , err := filepath . Abs ( workPath )
if err != nil {
log . Error ( "Unable to absolute %s against the current working directory %v. Will absolute against the AppPath %s" , workPath , err , appPath )
workPath = filepath . Join ( appPath , workPath )
} else {
workPath = absPath
}
}
2020-10-11 22:27:20 +02:00
return strings . ReplaceAll ( workPath , "\\" , "/" )
2015-11-08 22:59:56 +01:00
}
func init ( ) {
2019-04-28 21:48:46 +02:00
IsWindows = runtime . GOOS == "windows"
2019-04-02 09:48:31 +02:00
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
2021-06-27 02:56:58 +02:00
// By default set this logger at Info - we'll change it later but we need to start with something.
log . NewLogger ( 0 , "console" , "console" , fmt . Sprintf ( ` { "level": "info", "colorize": %t, "stacktraceLevel": "none"} ` , log . CanColorStdout ) )
2015-11-08 22:59:56 +01:00
var err error
2017-11-03 09:56:20 +01:00
if AppPath , err = getAppPath ( ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to get app path: %v" , err )
2014-05-26 02:11:25 +02:00
}
2017-11-03 09:56:20 +01:00
AppWorkPath = getWorkPath ( AppPath )
2014-05-26 02:11:25 +02:00
}
2015-03-18 09:25:55 +01:00
func forcePathSeparator ( path string ) {
if strings . Contains ( path , "\\" ) {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places" )
2015-03-18 09:25:55 +01:00
}
}
2016-08-10 02:41:18 +02:00
// IsRunUserMatchCurrentUser returns false if configured run user does not match
// actual user that runs the app. The first return value is the actual user name.
// This check is ignored under Windows since SSH remote login is not the main
// method to login on Windows.
func IsRunUserMatchCurrentUser ( runUser string ) ( string , bool ) {
2019-06-16 04:49:07 +02:00
if IsWindows || SSH . StartBuiltinServer {
2016-08-10 02:41:18 +02:00
return "" , true
}
currentUser := user . CurrentUsername ( )
return currentUser , runUser == currentUser
}
2017-01-09 12:54:57 +01:00
func createPIDFile ( pidPath string ) {
currentPid := os . Getpid ( )
if err := os . MkdirAll ( filepath . Dir ( pidPath ) , os . ModePerm ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create PID folder: %v" , err )
2017-01-09 12:54:57 +01:00
}
file , err := os . Create ( pidPath )
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to create PID file: %v" , err )
2017-01-09 12:54:57 +01:00
}
defer file . Close ( )
if _ , err := file . WriteString ( strconv . FormatInt ( int64 ( currentPid ) , 10 ) ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to write PID information: %v" , err )
2017-01-09 12:54:57 +01:00
}
}
2019-04-29 20:08:21 +02:00
// SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
// GITEA_CUSTOM environment variable and with provided overrides before stepping
// back to the default
2019-05-14 17:20:35 +02:00
func SetCustomPathAndConf ( providedCustom , providedConf , providedWorkPath string ) {
if len ( providedWorkPath ) != 0 {
AppWorkPath = filepath . ToSlash ( providedWorkPath )
}
2019-04-29 20:08:21 +02:00
if giteaCustom , ok := os . LookupEnv ( "GITEA_CUSTOM" ) ; ok {
CustomPath = giteaCustom
}
if len ( providedCustom ) != 0 {
CustomPath = providedCustom
}
2014-05-26 02:11:25 +02:00
if len ( CustomPath ) == 0 {
2017-11-03 09:56:20 +01:00
CustomPath = path . Join ( AppWorkPath , "custom" )
} else if ! filepath . IsAbs ( CustomPath ) {
CustomPath = path . Join ( AppWorkPath , CustomPath )
2014-05-26 02:11:25 +02:00
}
2019-04-29 20:08:21 +02:00
if len ( providedConf ) != 0 {
CustomConf = providedConf
2017-01-09 12:54:57 +01:00
}
2015-02-05 11:12:37 +01:00
if len ( CustomConf ) == 0 {
2017-11-03 09:56:20 +01:00
CustomConf = path . Join ( CustomPath , "conf/app.ini" )
2017-07-01 05:10:04 +02:00
} else if ! filepath . IsAbs ( CustomConf ) {
2017-11-03 09:56:20 +01:00
CustomConf = path . Join ( CustomPath , CustomConf )
2020-02-02 17:20:20 +01:00
log . Warn ( "Using 'custom' directory as relative origin for configuration file: '%s'" , CustomConf )
2015-02-05 11:12:37 +01:00
}
2019-04-29 20:08:21 +02:00
}
2021-12-01 08:50:01 +01:00
// LoadFromExisting initializes setting options from an existing config file (app.ini)
func LoadFromExisting ( ) {
2021-12-08 08:34:23 +01:00
loadFromConf ( false , "" )
2021-12-01 08:50:01 +01:00
}
// LoadAllowEmpty initializes setting options, it's also fine that if the config file (app.ini) doesn't exist
func LoadAllowEmpty ( ) {
2021-12-08 08:34:23 +01:00
loadFromConf ( true , "" )
2021-12-01 08:50:01 +01:00
}
// LoadForTest initializes setting options for tests
2021-12-08 08:34:23 +01:00
func LoadForTest ( extraConfigs ... string ) {
loadFromConf ( true , strings . Join ( extraConfigs , "\n" ) )
2021-12-01 08:50:01 +01:00
if err := PrepareAppDataPath ( ) ; err != nil {
log . Fatal ( "Can not prepare APP_DATA_PATH: %v" , err )
}
}
2022-01-20 18:00:38 +01:00
func deprecatedSetting ( oldSection , oldKey , newSection , newKey string ) {
if Cfg . Section ( oldSection ) . HasKey ( oldKey ) {
2022-11-27 11:08:40 +01:00
log . Error ( "Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0" , oldSection , oldKey , newSection , newKey )
2022-01-20 18:00:38 +01:00
}
}
2022-10-17 01:29:26 +02:00
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB ( oldSection , oldKey string ) {
if Cfg . Section ( oldSection ) . HasKey ( oldKey ) {
log . Error ( "Deprecated `[%s]` `%s` present which has been copied to database table sys_setting" , oldSection , oldKey )
}
}
2021-12-01 08:50:01 +01:00
// loadFromConf initializes configuration context.
2019-04-29 20:08:21 +02:00
// NOTE: do not print any log except error.
2021-12-08 08:34:23 +01:00
func loadFromConf ( allowEmpty bool , extraConfig string ) {
2019-04-29 20:08:21 +02:00
Cfg = ini . Empty ( )
2020-08-15 22:15:27 +02:00
if WritePIDFile && len ( PIDFile ) > 0 {
createPIDFile ( PIDFile )
2019-04-29 20:08:21 +02:00
}
2015-02-05 11:12:37 +01:00
2020-11-28 03:42:08 +01:00
isFile , err := util . IsFile ( CustomConf )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , CustomConf , err )
}
if isFile {
2017-11-03 09:56:20 +01:00
if err := Cfg . Append ( CustomConf ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to load custom conf '%s': %v" , CustomConf , err )
2014-05-26 02:11:25 +02:00
}
2021-12-01 08:50:01 +01:00
} else if ! allowEmpty {
log . Fatal ( "Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c." , CustomConf )
} // else: no config file, a config file might be created at CustomConf later (might not)
2021-12-08 08:34:23 +01:00
if extraConfig != "" {
if err = Cfg . Append ( [ ] byte ( extraConfig ) ) ; err != nil {
log . Fatal ( "Unable to append more config: %v" , err )
}
}
2019-10-15 17:45:39 +02:00
Cfg . NameMapper = ini . SnackCase
2014-05-26 02:11:25 +02:00
2022-04-01 10:47:50 +02:00
homeDir , err := util . HomeDir ( )
2015-12-20 03:43:32 +01:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to get home directory: %v" , err )
2015-12-20 03:43:32 +01:00
}
2020-10-11 22:27:20 +02:00
homeDir = strings . ReplaceAll ( homeDir , "\\" , "/" )
2015-12-20 03:43:32 +01:00
2021-03-28 21:08:19 +02:00
LogLevel = getLogLevel ( Cfg . Section ( "log" ) , "LEVEL" , log . INFO )
2019-04-02 09:48:31 +02:00
StacktraceLogLevel = getStacktraceLogLevel ( Cfg . Section ( "log" ) , "STACKTRACE_LEVEL" , "None" )
2017-11-03 09:56:20 +01:00
LogRootPath = Cfg . Section ( "log" ) . Key ( "ROOT_PATH" ) . MustString ( path . Join ( AppWorkPath , "log" ) )
2015-03-18 09:25:55 +01:00
forcePathSeparator ( LogRootPath )
2014-12-31 11:37:29 +01:00
sec := Cfg . Section ( "server" )
2016-12-02 06:23:37 +01:00
AppName = Cfg . Section ( "" ) . Key ( "APP_NAME" ) . MustString ( "Gitea: Git with a cup of tea" )
2014-09-14 19:35:22 +02:00
2021-11-27 21:22:03 +01:00
Domain = sec . Key ( "DOMAIN" ) . MustString ( "localhost" )
HTTPAddr = sec . Key ( "HTTP_ADDR" ) . MustString ( "0.0.0.0" )
HTTPPort = sec . Key ( "HTTP_PORT" ) . MustString ( "3000" )
2014-05-26 02:11:25 +02:00
Protocol = HTTP
2021-12-06 05:46:11 +01:00
protocolCfg := sec . Key ( "PROTOCOL" ) . String ( )
switch protocolCfg {
2019-06-12 21:41:28 +02:00
case "https" :
2014-05-26 02:11:25 +02:00
Protocol = HTTPS
2022-02-08 06:45:35 +01:00
// FIXME: DEPRECATED to be removed in v1.18.0
if sec . HasKey ( "ENABLE_ACME" ) {
EnableAcme = sec . Key ( "ENABLE_ACME" ) . MustBool ( false )
} else {
deprecatedSetting ( "server" , "ENABLE_LETSENCRYPT" , "server" , "ENABLE_ACME" )
EnableAcme = sec . Key ( "ENABLE_LETSENCRYPT" ) . MustBool ( false )
2020-01-19 20:07:44 +01:00
}
2022-02-08 06:45:35 +01:00
if EnableAcme {
AcmeURL = sec . Key ( "ACME_URL" ) . MustString ( "" )
AcmeCARoot = sec . Key ( "ACME_CA_ROOT" ) . MustString ( "" )
// FIXME: DEPRECATED to be removed in v1.18.0
if sec . HasKey ( "ACME_ACCEPTTOS" ) {
AcmeTOS = sec . Key ( "ACME_ACCEPTTOS" ) . MustBool ( false )
} else {
deprecatedSetting ( "server" , "LETSENCRYPT_ACCEPTTOS" , "server" , "ACME_ACCEPTTOS" )
AcmeTOS = sec . Key ( "LETSENCRYPT_ACCEPTTOS" ) . MustBool ( false )
}
if ! AcmeTOS {
log . Fatal ( "ACME TOS is not accepted (ACME_ACCEPTTOS)." )
}
// FIXME: DEPRECATED to be removed in v1.18.0
if sec . HasKey ( "ACME_DIRECTORY" ) {
AcmeLiveDirectory = sec . Key ( "ACME_DIRECTORY" ) . MustString ( "https" )
} else {
deprecatedSetting ( "server" , "LETSENCRYPT_DIRECTORY" , "server" , "ACME_DIRECTORY" )
AcmeLiveDirectory = sec . Key ( "LETSENCRYPT_DIRECTORY" ) . MustString ( "https" )
}
// FIXME: DEPRECATED to be removed in v1.18.0
if sec . HasKey ( "ACME_EMAIL" ) {
AcmeEmail = sec . Key ( "ACME_EMAIL" ) . MustString ( "" )
} else {
deprecatedSetting ( "server" , "LETSENCRYPT_EMAIL" , "server" , "ACME_EMAIL" )
AcmeEmail = sec . Key ( "LETSENCRYPT_EMAIL" ) . MustString ( "" )
}
} else {
CertFile = sec . Key ( "CERT_FILE" ) . String ( )
KeyFile = sec . Key ( "KEY_FILE" ) . String ( )
if len ( CertFile ) > 0 && ! filepath . IsAbs ( CertFile ) {
CertFile = filepath . Join ( CustomPath , CertFile )
}
if len ( KeyFile ) > 0 && ! filepath . IsAbs ( KeyFile ) {
KeyFile = filepath . Join ( CustomPath , KeyFile )
}
2020-01-19 20:07:44 +01:00
}
2022-02-08 06:45:35 +01:00
SSLMinimumVersion = sec . Key ( "SSL_MIN_VERSION" ) . MustString ( "" )
SSLMaximumVersion = sec . Key ( "SSL_MAX_VERSION" ) . MustString ( "" )
SSLCurvePreferences = sec . Key ( "SSL_CURVE_PREFERENCES" ) . Strings ( "," )
SSLCipherSuites = sec . Key ( "SSL_CIPHER_SUITES" ) . Strings ( "," )
2019-06-12 21:41:28 +02:00
case "fcgi" :
2014-11-04 02:46:53 +01:00
Protocol = FCGI
2021-12-06 05:46:11 +01:00
case "fcgi+unix" , "unix" , "http+unix" :
switch protocolCfg {
case "fcgi+unix" :
Protocol = FCGIUnix
case "unix" :
log . Warn ( "unix PROTOCOL value is deprecated, please use http+unix" )
fallthrough
case "http+unix" :
Protocol = HTTPUnix
2019-12-10 13:23:26 +01:00
}
2016-08-11 23:55:10 +02:00
UnixSocketPermissionRaw := sec . Key ( "UNIX_SOCKET_PERMISSION" ) . MustString ( "666" )
UnixSocketPermissionParsed , err := strconv . ParseUint ( UnixSocketPermissionRaw , 8 , 32 )
2022-01-20 18:46:10 +01:00
if err != nil || UnixSocketPermissionParsed > 0 o777 {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to parse unixSocketPermission: %s" , UnixSocketPermissionRaw )
2016-08-11 23:55:10 +02:00
}
2021-12-06 05:46:11 +01:00
2016-08-11 23:55:10 +02:00
UnixSocketPermission = uint32 ( UnixSocketPermissionParsed )
2021-11-27 21:22:03 +01:00
if ! filepath . IsAbs ( HTTPAddr ) {
HTTPAddr = filepath . Join ( AppWorkPath , HTTPAddr )
}
2014-11-04 02:46:53 +01:00
}
2022-08-21 20:20:43 +02:00
UseProxyProtocol = sec . Key ( "USE_PROXY_PROTOCOL" ) . MustBool ( false )
ProxyProtocolTLSBridging = sec . Key ( "PROXY_PROTOCOL_TLS_BRIDGING" ) . MustBool ( false )
ProxyProtocolHeaderTimeout = sec . Key ( "PROXY_PROTOCOL_HEADER_TIMEOUT" ) . MustDuration ( 5 * time . Second )
ProxyProtocolAcceptUnknown = sec . Key ( "PROXY_PROTOCOL_ACCEPT_UNKNOWN" ) . MustBool ( false )
2019-10-15 15:39:51 +02:00
GracefulRestartable = sec . Key ( "ALLOW_GRACEFUL_RESTARTS" ) . MustBool ( true )
GracefulHammerTime = sec . Key ( "GRACEFUL_HAMMER_TIME" ) . MustDuration ( 60 * time . Second )
2019-11-21 19:32:02 +01:00
StartupTimeout = sec . Key ( "STARTUP_TIMEOUT" ) . MustDuration ( 0 * time . Second )
2021-06-10 23:25:25 +02:00
PerWriteTimeout = sec . Key ( "PER_WRITE_TIMEOUT" ) . MustDuration ( PerWriteTimeout )
PerWritePerKbTimeout = sec . Key ( "PER_WRITE_PER_KB_TIMEOUT" ) . MustDuration ( PerWritePerKbTimeout )
2017-06-22 09:35:14 +02:00
2022-11-29 08:30:47 +01:00
defaultAppURL := string ( Protocol ) + "://" + Domain + ":" + HTTPPort
AppURL = sec . Key ( "ROOT_URL" ) . MustString ( defaultAppURL )
2017-06-22 09:35:14 +02:00
2022-11-29 08:30:47 +01:00
// Check validity of AppURL
2019-06-12 23:07:24 +02:00
appURL , err := url . Parse ( AppURL )
2017-06-22 09:35:14 +02:00
if err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Invalid ROOT_URL '%s': %s" , AppURL , err )
2017-06-22 09:35:14 +02:00
}
2022-11-29 08:30:47 +01:00
// Remove default ports from AppURL.
// (scheme-based URL normalization, RFC 3986 section 6.2.3)
if ( appURL . Scheme == string ( HTTP ) && appURL . Port ( ) == "80" ) || ( appURL . Scheme == string ( HTTPS ) && appURL . Port ( ) == "443" ) {
appURL . Host = appURL . Hostname ( )
}
// This should be TrimRight to ensure that there is only a single '/' at the end of AppURL.
AppURL = strings . TrimRight ( appURL . String ( ) , "/" ) + "/"
2017-06-22 09:35:14 +02:00
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
// This value is empty if site does not have sub-url.
2019-06-12 23:07:24 +02:00
AppSubURL = strings . TrimSuffix ( appURL . Path , "/" )
2019-10-22 14:11:01 +02:00
StaticURLPrefix = strings . TrimSuffix ( sec . Key ( "STATIC_URL_PREFIX" ) . MustString ( AppSubURL ) , "/" )
2021-02-19 22:36:43 +01:00
2017-10-02 15:55:09 +02:00
// Check if Domain differs from AppURL domain than update it to AppURL's domain
2020-07-27 02:16:22 +02:00
urlHostname := appURL . Hostname ( )
if urlHostname != Domain && net . ParseIP ( urlHostname ) == nil && urlHostname != "" {
2017-10-02 15:55:09 +02:00
Domain = urlHostname
}
2017-06-22 09:35:14 +02:00
2020-12-23 20:09:54 +01:00
AbsoluteAssetURL = MakeAbsoluteAssetURL ( AppURL , StaticURLPrefix )
2022-08-23 14:58:04 +02:00
AssetVersion = strings . ReplaceAll ( AppVer , "+" , "~" ) // make sure the version string is clear (no real escaping is needed)
2020-12-23 20:09:54 +01:00
manifestBytes := MakeManifestData ( AppName , AppURL , AbsoluteAssetURL )
ManifestData = ` application/json;base64, ` + base64 . StdEncoding . EncodeToString ( manifestBytes )
2017-08-03 17:32:13 +02:00
var defaultLocalURL string
switch Protocol {
2021-12-06 05:46:11 +01:00
case HTTPUnix :
2017-08-03 17:32:13 +02:00
defaultLocalURL = "http://unix/"
case FCGI :
defaultLocalURL = AppURL
2019-12-10 13:23:26 +01:00
case FCGIUnix :
defaultLocalURL = AppURL
2017-08-03 17:32:13 +02:00
default :
defaultLocalURL = string ( Protocol ) + "://"
if HTTPAddr == "0.0.0.0" {
2020-07-26 22:31:28 +02:00
defaultLocalURL += net . JoinHostPort ( "localhost" , HTTPPort ) + "/"
2017-08-03 17:32:13 +02:00
} else {
2020-07-26 22:31:28 +02:00
defaultLocalURL += net . JoinHostPort ( HTTPAddr , HTTPPort ) + "/"
2017-08-03 17:32:13 +02:00
}
}
LocalURL = sec . Key ( "LOCAL_ROOT_URL" ) . MustString ( defaultLocalURL )
2022-03-22 17:59:57 +01:00
LocalURL = strings . TrimRight ( LocalURL , "/" ) + "/"
2022-08-21 20:20:43 +02:00
LocalUseProxyProtocol = sec . Key ( "LOCAL_USE_PROXY_PROTOCOL" ) . MustBool ( UseProxyProtocol )
2017-12-25 23:23:43 +01:00
RedirectOtherPort = sec . Key ( "REDIRECT_OTHER_PORT" ) . MustBool ( false )
PortToRedirect = sec . Key ( "PORT_TO_REDIRECT" ) . MustString ( "80" )
2022-08-21 20:20:43 +02:00
RedirectorUseProxyProtocol = sec . Key ( "REDIRECTOR_USE_PROXY_PROTOCOL" ) . MustBool ( UseProxyProtocol )
2014-12-31 11:37:29 +01:00
OfflineMode = sec . Key ( "OFFLINE_MODE" ) . MustBool ( )
DisableRouterLog = sec . Key ( "DISABLE_ROUTER_LOG" ) . MustBool ( )
2020-08-08 16:02:22 +02:00
if len ( StaticRootPath ) == 0 {
StaticRootPath = AppWorkPath
}
StaticRootPath = sec . Key ( "STATIC_ROOT_PATH" ) . MustString ( StaticRootPath )
2019-10-15 00:05:57 +02:00
StaticCacheTime = sec . Key ( "STATIC_CACHE_TIME" ) . MustDuration ( 6 * time . Hour )
2017-11-03 09:56:20 +01:00
AppDataPath = sec . Key ( "APP_DATA_PATH" ) . MustString ( path . Join ( AppWorkPath , "data" ) )
2022-06-06 16:43:17 +02:00
if ! filepath . IsAbs ( AppDataPath ) {
log . Info ( "The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s" , AppDataPath , AppWorkPath )
AppDataPath = filepath . ToSlash ( filepath . Join ( AppWorkPath , AppDataPath ) )
}
2021-12-01 08:50:01 +01:00
2014-12-31 11:37:29 +01:00
EnableGzip = sec . Key ( "ENABLE_GZIP" ) . MustBool ( )
2017-02-05 14:06:25 +01:00
EnablePprof = sec . Key ( "ENABLE_PPROF" ) . MustBool ( false )
2018-08-07 20:49:18 +02:00
PprofDataPath = sec . Key ( "PPROF_DATA_PATH" ) . MustString ( path . Join ( AppWorkPath , "data/tmp/pprof" ) )
if ! filepath . IsAbs ( PprofDataPath ) {
PprofDataPath = filepath . Join ( AppWorkPath , PprofDataPath )
}
2014-12-31 11:37:29 +01:00
2022-04-05 18:16:01 +02:00
landingPage := sec . Key ( "LANDING_PAGE" ) . MustString ( "home" )
switch landingPage {
2014-11-25 00:47:59 +01:00
case "explore" :
2016-11-27 11:14:25 +01:00
LandingPageURL = LandingPageExplore
2017-11-20 08:48:09 +01:00
case "organizations" :
LandingPageURL = LandingPageOrganizations
2020-01-06 17:50:44 +01:00
case "login" :
LandingPageURL = LandingPageLogin
2022-04-05 18:16:01 +02:00
case "" :
case "home" :
2016-11-27 11:14:25 +01:00
LandingPageURL = LandingPageHome
2022-04-05 18:16:01 +02:00
default :
LandingPageURL = LandingPage ( landingPage )
2014-11-25 00:47:59 +01:00
}
2017-02-14 17:07:37 +01:00
if len ( SSH . Domain ) == 0 {
SSH . Domain = Domain
}
2016-02-28 02:48:39 +01:00
SSH . RootPath = path . Join ( homeDir , ".ssh" )
2017-10-23 17:20:44 +02:00
serverCiphers := sec . Key ( "SSH_SERVER_CIPHERS" ) . Strings ( "," )
if len ( serverCiphers ) > 0 {
SSH . ServerCiphers = serverCiphers
}
2017-11-02 16:26:41 +01:00
serverKeyExchanges := sec . Key ( "SSH_SERVER_KEY_EXCHANGES" ) . Strings ( "," )
if len ( serverKeyExchanges ) > 0 {
SSH . ServerKeyExchanges = serverKeyExchanges
}
serverMACs := sec . Key ( "SSH_SERVER_MACS" ) . Strings ( "," )
if len ( serverMACs ) > 0 {
SSH . ServerMACs = serverMACs
}
2016-02-28 02:48:39 +01:00
SSH . KeyTestPath = os . TempDir ( )
if err = Cfg . Section ( "server" ) . MapTo ( & SSH ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map SSH settings: %v" , err )
2016-02-28 02:48:39 +01:00
}
2021-03-08 03:43:59 +01:00
for i , key := range SSH . ServerHostKeys {
if ! filepath . IsAbs ( key ) {
SSH . ServerHostKeys [ i ] = filepath . Join ( AppDataPath , key )
}
}
2016-12-22 19:12:23 +01:00
SSH . KeygenPath = sec . Key ( "SSH_KEYGEN_PATH" ) . MustString ( "ssh-keygen" )
SSH . Port = sec . Key ( "SSH_PORT" ) . MustInt ( 22 )
2017-01-08 04:14:45 +01:00
SSH . ListenPort = sec . Key ( "SSH_LISTEN_PORT" ) . MustInt ( SSH . Port )
2022-08-21 20:20:43 +02:00
SSH . UseProxyProtocol = sec . Key ( "SSH_SERVER_USE_PROXY_PROTOCOL" ) . MustBool ( false )
2016-12-22 19:12:23 +01:00
2016-02-28 02:48:39 +01:00
// When disable SSH, start builtin server value is ignored.
if SSH . Disabled {
SSH . StartBuiltinServer = false
}
2022-07-10 08:50:26 +02:00
SSH . TrustedUserCAKeysFile = sec . Key ( "SSH_TRUSTED_USER_CA_KEYS_FILENAME" ) . MustString ( filepath . Join ( SSH . RootPath , "gitea-trusted-user-ca-keys.pem" ) )
for _ , caKey := range SSH . TrustedUserCAKeys {
2020-10-11 02:38:09 +02:00
pubKey , _ , _ , _ , err := gossh . ParseAuthorizedKey ( [ ] byte ( caKey ) )
if err != nil {
log . Fatal ( "Failed to parse TrustedUserCaKeys: %s %v" , caKey , err )
}
SSH . TrustedUserCAKeysParsed = append ( SSH . TrustedUserCAKeysParsed , pubKey )
}
2022-07-10 08:50:26 +02:00
if len ( SSH . TrustedUserCAKeys ) > 0 {
2020-10-11 02:38:09 +02:00
// Set the default as email,username otherwise we can leave it empty
sec . Key ( "SSH_AUTHORIZED_PRINCIPALS_ALLOW" ) . MustString ( "username,email" )
} else {
sec . Key ( "SSH_AUTHORIZED_PRINCIPALS_ALLOW" ) . MustString ( "off" )
}
SSH . AuthorizedPrincipalsAllow , SSH . AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow ( sec . Key ( "SSH_AUTHORIZED_PRINCIPALS_ALLOW" ) . Strings ( "," ) )
2020-10-09 08:52:57 +02:00
SSH . MinimumKeySizeCheck = sec . Key ( "MINIMUM_KEY_SIZE_CHECK" ) . MustBool ( SSH . MinimumKeySizeCheck )
2016-02-28 02:48:39 +01:00
minimumKeySizes := Cfg . Section ( "ssh.minimum_key_sizes" ) . Keys ( )
for _ , key := range minimumKeySizes {
if key . MustInt ( ) != - 1 {
SSH . MinimumKeySizes [ strings . ToLower ( key . Name ( ) ) ] = key . MustInt ( )
2020-05-28 15:29:15 +02:00
} else {
delete ( SSH . MinimumKeySizes , strings . ToLower ( key . Name ( ) ) )
2016-02-28 02:48:39 +01:00
}
}
2020-10-11 02:38:09 +02:00
2017-06-28 03:35:35 +02:00
SSH . AuthorizedKeysBackup = sec . Key ( "SSH_AUTHORIZED_KEYS_BACKUP" ) . MustBool ( true )
2018-11-01 14:41:07 +01:00
SSH . CreateAuthorizedKeysFile = sec . Key ( "SSH_CREATE_AUTHORIZED_KEYS_FILE" ) . MustBool ( true )
2020-10-11 02:38:09 +02:00
SSH . AuthorizedPrincipalsBackup = false
SSH . CreateAuthorizedPrincipalsFile = false
if SSH . AuthorizedPrincipalsEnabled {
SSH . AuthorizedPrincipalsBackup = sec . Key ( "SSH_AUTHORIZED_PRINCIPALS_BACKUP" ) . MustBool ( true )
SSH . CreateAuthorizedPrincipalsFile = sec . Key ( "SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE" ) . MustBool ( true )
}
2017-07-15 16:21:51 +02:00
SSH . ExposeAnonymous = sec . Key ( "SSH_EXPOSE_ANONYMOUS" ) . MustBool ( false )
2021-06-15 03:15:12 +02:00
SSH . AuthorizedKeysCommandTemplate = sec . Key ( "SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE" ) . MustString ( SSH . AuthorizedKeysCommandTemplate )
SSH . AuthorizedKeysCommandTemplateTemplate = template . Must ( template . New ( "" ) . Parse ( SSH . AuthorizedKeysCommandTemplate ) )
2021-06-10 23:25:25 +02:00
SSH . PerWriteTimeout = sec . Key ( "SSH_PER_WRITE_TIMEOUT" ) . MustDuration ( PerWriteTimeout )
SSH . PerWritePerKbTimeout = sec . Key ( "SSH_PER_WRITE_PER_KB_TIMEOUT" ) . MustDuration ( PerWritePerKbTimeout )
2016-02-28 02:48:39 +01:00
2019-03-08 17:42:50 +01:00
if err = Cfg . Section ( "oauth2" ) . MapTo ( & OAuth2 ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to OAuth2 settings: %v" , err )
2019-03-08 17:42:50 +01:00
return
}
2021-06-17 23:56:46 +02:00
if ! filepath . IsAbs ( OAuth2 . JWTSigningPrivateKeyFile ) {
2021-06-23 16:56:25 +02:00
OAuth2 . JWTSigningPrivateKeyFile = filepath . Join ( AppDataPath , OAuth2 . JWTSigningPrivateKeyFile )
2019-03-08 17:42:50 +01:00
}
2019-08-29 16:05:42 +02:00
sec = Cfg . Section ( "admin" )
Admin . DefaultEmailNotification = sec . Key ( "DEFAULT_EMAIL_NOTIFICATIONS" ) . MustString ( "enabled" )
2014-12-31 11:37:29 +01:00
sec = Cfg . Section ( "security" )
2016-12-23 08:18:05 +01:00
InstallLock = sec . Key ( "INSTALL_LOCK" ) . MustBool ( false )
LogInRememberDays = sec . Key ( "LOGIN_REMEMBER_DAYS" ) . MustInt ( 7 )
CookieUserName = sec . Key ( "COOKIE_USERNAME" ) . MustString ( "gitea_awesome" )
2022-10-01 19:26:33 +02:00
SecretKey = loadSecret ( sec , "SECRET_KEY_URI" , "SECRET_KEY" )
if SecretKey == "" {
// FIXME: https://github.com/go-gitea/gitea/issues/16832
// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
2022-12-08 09:21:37 +01:00
SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
2022-10-01 19:26:33 +02:00
}
2016-12-23 08:18:05 +01:00
CookieRememberName = sec . Key ( "COOKIE_REMEMBER_NAME" ) . MustString ( "gitea_incredible" )
2021-03-15 23:27:28 +01:00
2014-12-31 11:37:29 +01:00
ReverseProxyAuthUser = sec . Key ( "REVERSE_PROXY_AUTHENTICATION_USER" ) . MustString ( "X-WEBAUTH-USER" )
2018-12-18 18:05:48 +01:00
ReverseProxyAuthEmail = sec . Key ( "REVERSE_PROXY_AUTHENTICATION_EMAIL" ) . MustString ( "X-WEBAUTH-EMAIL" )
2022-08-16 08:30:27 +02:00
ReverseProxyAuthFullName = sec . Key ( "REVERSE_PROXY_AUTHENTICATION_FULL_NAME" ) . MustString ( "X-WEBAUTH-FULLNAME" )
2021-03-15 23:27:28 +01:00
ReverseProxyLimit = sec . Key ( "REVERSE_PROXY_LIMIT" ) . MustInt ( 1 )
ReverseProxyTrustedProxies = sec . Key ( "REVERSE_PROXY_TRUSTED_PROXIES" ) . Strings ( "," )
if len ( ReverseProxyTrustedProxies ) == 0 {
ReverseProxyTrustedProxies = [ ] string { "127.0.0.0/8" , "::1/128" }
}
2016-12-24 15:42:11 +01:00
MinPasswordLength = sec . Key ( "MIN_PASSWORD_LENGTH" ) . MustInt ( 6 )
2017-01-23 02:19:50 +01:00
ImportLocalPaths = sec . Key ( "IMPORT_LOCAL_PATHS" ) . MustBool ( false )
2020-10-07 20:24:14 +02:00
DisableGitHooks = sec . Key ( "DISABLE_GIT_HOOKS" ) . MustBool ( true )
2021-02-11 18:34:34 +01:00
DisableWebhooks = sec . Key ( "DISABLE_WEBHOOKS" ) . MustBool ( false )
2019-11-14 23:39:48 +01:00
OnlyAllowPushIfGiteaEnvironmentSet = sec . Key ( "ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET" ) . MustBool ( true )
2021-02-13 18:03:29 +01:00
PasswordHashAlgo = sec . Key ( "PASSWORD_HASH_ALGO" ) . MustString ( "pbkdf2" )
2019-07-12 15:57:31 +02:00
CSRFCookieHTTPOnly = sec . Key ( "CSRF_COOKIE_HTTP_ONLY" ) . MustBool ( true )
2020-09-09 00:06:39 +02:00
PasswordCheckPwn = sec . Key ( "PASSWORD_CHECK_PWN" ) . MustBool ( false )
2021-08-17 20:30:42 +02:00
SuccessfulTokensCacheSize = sec . Key ( "SUCCESSFUL_TOKENS_CACHE_SIZE" ) . MustInt ( 20 )
2019-07-12 15:57:31 +02:00
2022-10-01 19:26:33 +02:00
InternalToken = loadSecret ( sec , "INTERNAL_TOKEN_URI" , "INTERNAL_TOKEN" )
2022-10-28 11:58:32 +02:00
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
// some users do cluster deployment, they still depend on this auto-generating behavior.
generateSaveInternalToken ( )
}
2014-12-31 11:37:29 +01:00
2019-10-14 17:24:26 +02:00
cfgdata := sec . Key ( "PASSWORD_COMPLEXITY" ) . Strings ( "," )
2020-08-22 00:42:23 +02:00
if len ( cfgdata ) == 0 {
cfgdata = [ ] string { "off" }
}
2019-10-16 05:09:58 +02:00
PasswordComplexity = make ( [ ] string , 0 , len ( cfgdata ) )
for _ , name := range cfgdata {
name := strings . ToLower ( strings . Trim ( name , ` " ` ) )
if name != "" {
PasswordComplexity = append ( PasswordComplexity , name )
2019-10-14 17:24:26 +02:00
}
}
2020-08-18 06:23:45 +02:00
newAttachmentService ( )
2020-09-29 11:05:13 +02:00
newLFSService ( )
2014-07-23 21:15:47 +02:00
2019-08-15 16:46:21 +02:00
timeFormatKey := Cfg . Section ( "time" ) . Key ( "FORMAT" ) . MustString ( "" )
if timeFormatKey != "" {
TimeFormat = map [ string ] string {
"ANSIC" : time . ANSIC ,
"UnixDate" : time . UnixDate ,
"RubyDate" : time . RubyDate ,
"RFC822" : time . RFC822 ,
"RFC822Z" : time . RFC822Z ,
"RFC850" : time . RFC850 ,
"RFC1123" : time . RFC1123 ,
"RFC1123Z" : time . RFC1123Z ,
"RFC3339" : time . RFC3339 ,
"RFC3339Nano" : time . RFC3339Nano ,
"Kitchen" : time . Kitchen ,
"Stamp" : time . Stamp ,
"StampMilli" : time . StampMilli ,
"StampMicro" : time . StampMicro ,
"StampNano" : time . StampNano ,
} [ timeFormatKey ]
// When the TimeFormatKey does not exist in the previous map e.g.'2006-01-02 15:04:05'
if len ( TimeFormat ) == 0 {
TimeFormat = timeFormatKey
TestTimeFormat , _ := time . Parse ( TimeFormat , TimeFormat )
if TestTimeFormat . Format ( time . RFC3339 ) != "2006-01-02T15:04:05Z" {
2019-12-08 22:25:00 +01:00
log . Warn ( "Provided TimeFormat: %s does not create a fully specified date and time." , TimeFormat )
log . Warn ( "In order to display dates and times correctly please check your time format has 2006, 01, 02, 15, 04 and 05" )
2019-08-15 16:46:21 +02:00
}
log . Trace ( "Custom TimeFormat: %s" , TimeFormat )
}
}
zone := Cfg . Section ( "time" ) . Key ( "DEFAULT_UI_LOCATION" ) . String ( )
if zone != "" {
DefaultUILocation , err = time . LoadLocation ( zone )
if err != nil {
log . Fatal ( "Load time zone failed: %v" , err )
} else {
log . Info ( "Default UI Location is %v" , zone )
2017-02-04 13:37:50 +01:00
}
2019-08-15 16:46:21 +02:00
}
if DefaultUILocation == nil {
DefaultUILocation = time . Local
2017-02-04 13:37:50 +01:00
}
2014-07-24 22:31:59 +02:00
2016-12-22 19:12:23 +01:00
RunUser = Cfg . Section ( "" ) . Key ( "RUN_USER" ) . MustString ( user . CurrentUsername ( ) )
2021-10-07 10:52:08 +02:00
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
unsafeAllowRunAsRoot := Cfg . Section ( "" ) . Key ( "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT" ) . MustBool ( false )
2022-12-27 07:00:34 +01:00
RunMode = os . Getenv ( "GITEA_RUN_MODE" )
if RunMode == "" {
RunMode = Cfg . Section ( "" ) . Key ( "RUN_MODE" ) . MustString ( "prod" )
}
2021-10-20 16:37:19 +02:00
IsProd = strings . EqualFold ( RunMode , "prod" )
2014-05-26 02:11:25 +02:00
// Does not check run user when the install lock is off.
2016-08-10 02:41:18 +02:00
if InstallLock {
currentUser , match := IsRunUserMatchCurrentUser ( RunUser )
if ! match {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Expect user '%s' but current user is: %s" , RunUser , currentUser )
2016-08-10 02:41:18 +02:00
}
2014-05-26 02:11:25 +02:00
}
2021-10-07 10:52:08 +02:00
// check if we run as root
if os . Getuid ( ) == 0 {
if ! unsafeAllowRunAsRoot {
// Special thanks to VLC which inspired the wording of this messaging.
log . Fatal ( "Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission" )
}
log . Critical ( "You are running Gitea using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this." )
}
2017-10-14 17:51:00 +02:00
SSH . BuiltinServerUser = Cfg . Section ( "server" ) . Key ( "BUILTIN_SSH_SERVER_USER" ) . MustString ( RunUser )
2022-02-07 22:56:45 +01:00
SSH . User = Cfg . Section ( "server" ) . Key ( "SSH_USER" ) . MustString ( SSH . BuiltinServerUser )
2017-10-14 17:51:00 +02:00
2019-03-16 04:12:44 +01:00
newRepository ( )
2016-08-11 14:48:08 +02:00
2020-10-14 15:07:51 +02:00
newPictureService ( )
2014-07-26 06:24:27 +02:00
2022-03-30 10:42:47 +02:00
newPackages ( )
Implement actions (#21937)
Close #13539.
Co-authored by: @lunny @appleboy @fuxiaohei and others.
Related projects:
- https://gitea.com/gitea/actions-proto-def
- https://gitea.com/gitea/actions-proto-go
- https://gitea.com/gitea/act
- https://gitea.com/gitea/act_runner
### Summary
The target of this PR is to bring a basic implementation of "Actions",
an internal CI/CD system of Gitea. That means even though it has been
merged, the state of the feature is **EXPERIMENTAL**, and please note
that:
- It is disabled by default;
- It shouldn't be used in a production environment currently;
- It shouldn't be used in a public Gitea instance currently;
- Breaking changes may be made before it's stable.
**Please comment on #13539 if you have any different product design
ideas**, all decisions reached there will be adopted here. But in this
PR, we don't talk about **naming, feature-creep or alternatives**.
### ⚠️ Breaking
`gitea-actions` will become a reserved user name. If a user with the
name already exists in the database, it is recommended to rename it.
### Some important reviews
- What is `DEFAULT_ACTIONS_URL` in `app.ini` for?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1055954954
- Why the api for runners is not under the normal `/api/v1` prefix?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061173592
- Why DBFS?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061301178
- Why ignore events triggered by `gitea-actions` bot?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1063254103
- Why there's no permission control for actions?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1090229868
### What it looks like
<details>
#### Manage runners
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205870657-c72f590e-2e08-4cd4-be7f-2e0abb299bbf.png">
#### List runs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872794-50fde990-2b45-48c1-a178-908e4ec5b627.png">
#### View logs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872501-9b7b9000-9542-4991-8f55-18ccdada77c3.png">
</details>
### How to try it
<details>
#### 1. Start Gitea
Clone this branch and [install from
source](https://docs.gitea.io/en-us/install-from-source).
Add additional configurations in `app.ini` to enable Actions:
```ini
[actions]
ENABLED = true
```
Start it.
If all is well, you'll see the management page of runners:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205877365-8e30a780-9b10-4154-b3e8-ee6c3cb35a59.png">
#### 2. Start runner
Clone the [act_runner](https://gitea.com/gitea/act_runner), and follow
the
[README](https://gitea.com/gitea/act_runner/src/branch/main/README.md)
to start it.
If all is well, you'll see a new runner has been added:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205878000-216f5937-e696-470d-b66c-8473987d91c3.png">
#### 3. Enable actions for a repo
Create a new repo or open an existing one, check the `Actions` checkbox
in settings and submit.
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879705-53e09208-73c0-4b3e-a123-2dcf9aba4b9c.png">
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879383-23f3d08f-1a85-41dd-a8b3-54e2ee6453e8.png">
If all is well, you'll see a new tab "Actions":
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205881648-a8072d8c-5803-4d76-b8a8-9b2fb49516c1.png">
#### 4. Upload workflow files
Upload some workflow files to `.gitea/workflows/xxx.yaml`, you can
follow the [quickstart](https://docs.github.com/en/actions/quickstart)
of GitHub Actions. Yes, Gitea Actions is compatible with GitHub Actions
in most cases, you can use the same demo:
```yaml
name: GitHub Actions Demo
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
```
If all is well, you'll see a new run in `Actions` tab:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884473-79a874bc-171b-4aaf-acd5-0241a45c3b53.png">
#### 5. Check the logs of jobs
Click a run and you'll see the logs:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884800-994b0374-67f7-48ff-be9a-4c53f3141547.png">
#### 6. Go on
You can try more examples in [the
documents](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
of GitHub Actions, then you might find a lot of bugs.
Come on, PRs are welcome.
</details>
See also: [Feature Preview: Gitea
Actions](https://blog.gitea.io/2022/12/feature-preview-gitea-actions/)
---------
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2023-01-31 02:45:19 +01:00
newActions ( )
2016-07-23 18:23:54 +02:00
if err = Cfg . Section ( "ui" ) . MapTo ( & UI ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map UI settings: %v" , err )
2016-07-23 18:23:54 +02:00
} else if err = Cfg . Section ( "markdown" ) . MapTo ( & Markdown ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map Markdown settings: %v" , err )
2017-02-14 13:16:00 +01:00
} else if err = Cfg . Section ( "admin" ) . MapTo ( & Admin ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Fail to map Admin settings: %v" , err )
2016-08-10 08:58:15 +02:00
} else if err = Cfg . Section ( "api" ) . MapTo ( & API ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map API settings: %v" , err )
2018-11-05 04:20:00 +01:00
} else if err = Cfg . Section ( "metrics" ) . MapTo ( & Metrics ) ; err != nil {
2019-04-02 09:48:31 +02:00
log . Fatal ( "Failed to map Metrics settings: %v" , err )
2022-03-29 10:27:37 +02:00
} else if err = Cfg . Section ( "camo" ) . MapTo ( & Camo ) ; err != nil {
log . Fatal ( "Failed to map Camo settings: %v" , err )
}
if Camo . Enabled {
if Camo . ServerURL == "" || Camo . HMACKey == "" {
log . Fatal ( ` Camo settings require "SERVER_URL" and HMAC_KEY ` )
}
2016-08-10 08:47:16 +02:00
}
2019-06-12 23:07:24 +02:00
u := * appURL
u . Path = path . Join ( u . Path , "api" , "swagger" )
API . SwaggerURL = u . String ( )
2019-03-16 04:12:44 +01:00
newGit ( )
2019-02-09 23:44:24 +01:00
2021-09-07 17:49:36 +02:00
newMirror ( )
2014-09-17 06:03:03 +02:00
2014-12-31 11:37:29 +01:00
Langs = Cfg . Section ( "i18n" ) . Key ( "LANGS" ) . Strings ( "," )
2016-12-22 19:12:23 +01:00
if len ( Langs ) == 0 {
2021-12-01 08:50:01 +01:00
Langs = defaultI18nLangs ( )
2016-12-22 19:12:23 +01:00
}
2014-12-31 11:37:29 +01:00
Names = Cfg . Section ( "i18n" ) . Key ( "NAMES" ) . Strings ( "," )
2016-12-22 19:12:23 +01:00
if len ( Names ) == 0 {
2021-12-01 08:50:01 +01:00
Names = defaultI18nNames ( )
2016-12-22 19:12:23 +01:00
}
2014-09-22 01:39:10 +02:00
2016-12-23 08:18:05 +01:00
ShowFooterBranding = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_BRANDING" ) . MustBool ( false )
ShowFooterVersion = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_VERSION" ) . MustBool ( true )
ShowFooterTemplateLoadTime = Cfg . Section ( "other" ) . Key ( "SHOW_FOOTER_TEMPLATE_LOAD_TIME" ) . MustBool ( true )
2022-10-28 17:17:38 +02:00
EnableSitemap = Cfg . Section ( "other" ) . Key ( "ENABLE_SITEMAP" ) . MustBool ( true )
2022-11-21 06:14:58 +01:00
EnableFeed = Cfg . Section ( "other" ) . Key ( "ENABLE_FEED" ) . MustBool ( true )
2015-03-23 15:19:19 +01:00
2017-01-01 03:51:10 +01:00
UI . ShowUserEmail = Cfg . Section ( "ui" ) . Key ( "SHOW_USER_EMAIL" ) . MustBool ( true )
2019-05-08 10:41:35 +02:00
UI . DefaultShowFullName = Cfg . Section ( "ui" ) . Key ( "DEFAULT_SHOW_FULL_NAME" ) . MustBool ( false )
2019-08-25 19:06:36 +02:00
UI . SearchRepoDescription = Cfg . Section ( "ui" ) . Key ( "SEARCH_REPO_DESCRIPTION" ) . MustBool ( true )
2022-02-26 11:14:23 +01:00
UI . UseServiceWorker = Cfg . Section ( "ui" ) . Key ( "USE_SERVICE_WORKER" ) . MustBool ( false )
2022-08-25 20:38:41 +02:00
UI . OnlyShowRelevantRepos = Cfg . Section ( "ui" ) . Key ( "ONLY_SHOW_RELEVANT_REPOS" ) . MustBool ( false )
2017-01-01 03:51:10 +01:00
2020-11-28 03:42:08 +01:00
HasRobotsTxt , err = util . IsFile ( path . Join ( CustomPath , "robots.txt" ) )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , path . Join ( CustomPath , "robots.txt" ) , err )
}
2017-11-07 07:33:06 +01:00
2019-03-16 04:12:44 +01:00
newMarkup ( )
2019-02-19 15:39:39 +01:00
2022-10-12 07:18:26 +02:00
UI . ReactionsLookup = make ( container . Set [ string ] )
2019-12-07 23:04:19 +01:00
for _ , reaction := range UI . Reactions {
2022-10-12 07:18:26 +02:00
UI . ReactionsLookup . Add ( reaction )
2019-12-07 23:04:19 +01:00
}
2021-06-29 16:28:38 +02:00
UI . CustomEmojisMap = make ( map [ string ] string )
for _ , emoji := range UI . CustomEmojis {
UI . CustomEmojisMap [ emoji ] = ":" + emoji + ":"
}
2014-05-26 02:11:25 +02:00
}
2020-10-11 02:38:09 +02:00
func parseAuthorizedPrincipalsAllow ( values [ ] string ) ( [ ] string , bool ) {
anything := false
email := false
username := false
for _ , value := range values {
v := strings . ToLower ( strings . TrimSpace ( value ) )
switch v {
case "off" :
return [ ] string { "off" } , false
case "email" :
email = true
case "username" :
username = true
case "anything" :
anything = true
}
}
if anything {
return [ ] string { "anything" } , true
}
authorizedPrincipalsAllow := [ ] string { }
if username {
authorizedPrincipalsAllow = append ( authorizedPrincipalsAllow , "username" )
}
if email {
authorizedPrincipalsAllow = append ( authorizedPrincipalsAllow , "email" )
}
return authorizedPrincipalsAllow , true
}
2022-11-03 21:55:09 +01:00
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
2022-10-01 19:26:33 +02:00
func loadSecret ( sec * ini . Section , uriKey , verbatimKey string ) string {
// don't allow setting both URI and verbatim string
uri := sec . Key ( uriKey ) . String ( )
verbatim := sec . Key ( verbatimKey ) . String ( )
if uri != "" && verbatim != "" {
log . Fatal ( "Cannot specify both %s and %s" , uriKey , verbatimKey )
}
// if we have no URI, use verbatim
2021-12-01 08:50:01 +01:00
if uri == "" {
2022-10-01 19:26:33 +02:00
return verbatim
2019-03-13 23:49:43 +01:00
}
2022-10-01 19:26:33 +02:00
2019-03-13 23:49:43 +01:00
tempURI , err := url . Parse ( uri )
if err != nil {
2022-10-01 19:26:33 +02:00
log . Fatal ( "Failed to parse %s (%s): %v" , uriKey , uri , err )
2019-03-13 23:49:43 +01:00
}
switch tempURI . Scheme {
case "file" :
2022-02-08 02:34:37 +01:00
buf , err := os . ReadFile ( tempURI . RequestURI ( ) )
2022-10-01 19:26:33 +02:00
if err != nil {
log . Fatal ( "Failed to read %s (%s): %v" , uriKey , tempURI . RequestURI ( ) , err )
2019-03-13 23:49:43 +01:00
}
2022-11-03 21:55:09 +01:00
val := strings . TrimSpace ( string ( buf ) )
if val == "" {
// The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI
// For example: if INTERNAL_TOKEN_URI=file:///empty-file,
// Then if the token is re-generated during installation and saved to INTERNAL_TOKEN
// Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't)
log . Fatal ( "Failed to read %s (%s): the file is empty" , uriKey , tempURI . RequestURI ( ) )
}
return val
2022-10-01 19:26:33 +02:00
// only file URIs are allowed
2019-03-13 23:49:43 +01:00
default :
2019-04-02 09:48:31 +02:00
log . Fatal ( "Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)" , tempURI . Scheme , uri )
2022-10-01 19:26:33 +02:00
return ""
2019-03-13 23:49:43 +01:00
}
}
2022-10-28 11:58:32 +02:00
// generateSaveInternalToken generates and saves the internal token to app.ini
func generateSaveInternalToken ( ) {
token , err := generate . NewInternalToken ( )
if err != nil {
log . Fatal ( "Error generate internal token: %v" , err )
}
InternalToken = token
CreateOrAppendToCustomConf ( "security.INTERNAL_TOKEN" , func ( cfg * ini . File ) {
cfg . Section ( "security" ) . Key ( "INTERNAL_TOKEN" ) . SetValue ( token )
} )
}
2020-12-23 20:09:54 +01:00
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
2021-12-20 05:41:31 +01:00
func MakeAbsoluteAssetURL ( appURL , staticURLPrefix string ) string {
2020-12-23 20:09:54 +01:00
parsedPrefix , err := url . Parse ( strings . TrimSuffix ( staticURLPrefix , "/" ) )
if err != nil {
log . Fatal ( "Unable to parse STATIC_URL_PREFIX: %v" , err )
}
if err == nil && parsedPrefix . Hostname ( ) == "" {
if staticURLPrefix == "" {
return strings . TrimSuffix ( appURL , "/" )
}
// StaticURLPrefix is just a path
2021-02-28 13:29:22 +01:00
return util . URLJoin ( appURL , strings . TrimSuffix ( staticURLPrefix , "/" ) )
2020-12-23 20:09:54 +01:00
}
return strings . TrimSuffix ( staticURLPrefix , "/" )
}
// MakeManifestData generates web app manifest JSON
2021-12-20 05:41:31 +01:00
func MakeManifestData ( appName , appURL , absoluteAssetURL string ) [ ] byte {
2020-12-23 20:09:54 +01:00
type manifestIcon struct {
Src string ` json:"src" `
Type string ` json:"type" `
Sizes string ` json:"sizes" `
}
type manifestJSON struct {
Name string ` json:"name" `
ShortName string ` json:"short_name" `
StartURL string ` json:"start_url" `
Icons [ ] manifestIcon ` json:"icons" `
}
bytes , err := json . Marshal ( & manifestJSON {
Name : appName ,
ShortName : appName ,
StartURL : appURL ,
Icons : [ ] manifestIcon {
{
2021-04-28 14:35:06 +02:00
Src : absoluteAssetURL + "/assets/img/logo.png" ,
2020-12-23 20:09:54 +01:00
Type : "image/png" ,
Sizes : "512x512" ,
} ,
{
2021-04-28 14:35:06 +02:00
Src : absoluteAssetURL + "/assets/img/logo.svg" ,
2021-01-01 20:04:35 +01:00
Type : "image/svg+xml" ,
Sizes : "512x512" ,
2020-12-23 20:09:54 +01:00
} ,
} ,
} )
if err != nil {
log . Error ( "unable to marshal manifest JSON. Error: %v" , err )
return make ( [ ] byte , 0 )
}
2020-12-22 12:13:50 +01:00
2020-12-23 20:09:54 +01:00
return bytes
2020-12-22 12:13:50 +01:00
}
2021-05-29 20:44:14 +02:00
// CreateOrAppendToCustomConf creates or updates the custom config.
// Use the callback to set individual values.
2022-10-01 19:26:33 +02:00
func CreateOrAppendToCustomConf ( purpose string , callback func ( cfg * ini . File ) ) {
if CustomConf == "" {
log . Error ( "Custom config path must not be empty" )
return
}
2021-05-29 20:44:14 +02:00
cfg := ini . Empty ( )
isFile , err := util . IsFile ( CustomConf )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , CustomConf , err )
}
if isFile {
if err := cfg . Append ( CustomConf ) ; err != nil {
log . Error ( "failed to load custom conf %s: %v" , CustomConf , err )
return
}
}
callback ( cfg )
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
log . Fatal ( "failed to create '%s': %v" , CustomConf , err )
return
}
if err := cfg . SaveTo ( CustomConf ) ; err != nil {
log . Fatal ( "error saving to custom config: %v" , err )
}
2022-10-01 19:26:33 +02:00
log . Info ( "Settings for %s saved to: %q" , purpose , CustomConf )
2021-06-27 12:07:36 +02:00
// Change permissions to be more restrictive
fi , err := os . Stat ( CustomConf )
if err != nil {
log . Error ( "Failed to determine current conf file permissions: %v" , err )
return
}
if fi . Mode ( ) . Perm ( ) > 0 o600 {
if err = os . Chmod ( CustomConf , 0 o600 ) ; err != nil {
log . Warn ( "Failed changing conf file permissions to -rw-------. Consider changing them manually." )
}
}
2021-05-29 20:44:14 +02:00
}
2016-11-27 11:14:25 +01:00
// NewServices initializes the services
2014-05-26 02:11:25 +02:00
func NewServices ( ) {
2019-08-24 11:24:45 +02:00
InitDBConfig ( )
2014-04-10 20:20:58 +02:00
newService ( )
2021-04-14 14:02:12 +02:00
newOAuth2Client ( )
2019-04-07 02:25:14 +02:00
NewLogServices ( false )
2014-04-10 20:20:58 +02:00
newCacheService ( )
newSessionService ( )
2019-05-13 17:38:53 +02:00
newCORSService ( )
2023-01-11 21:09:24 +01:00
parseMailerConfig ( Cfg )
2023-01-14 16:57:10 +01:00
newIncomingEmail ( )
2014-04-10 20:20:58 +02:00
newRegisterMailService ( )
newNotifyMailService ( )
2021-08-18 15:10:39 +02:00
newProxyService ( )
2014-06-08 10:45:34 +02:00
newWebhookService ( )
2019-11-16 09:30:06 +01:00
newMigrationsService ( )
2019-02-19 15:39:39 +01:00
newIndexerService ( )
2019-10-13 15:23:14 +02:00
newTaskService ( )
2020-01-07 12:23:09 +01:00
NewQueueService ( )
2020-08-17 05:07:38 +02:00
newProject ( )
2021-05-10 22:38:08 +02:00
newMimeTypeMap ( )
2021-09-28 01:38:06 +02:00
newFederationService ( )
2014-04-10 20:20:58 +02:00
}
2021-06-17 01:32:57 +02:00
// NewServicesForInstall initializes the services for install
func NewServicesForInstall ( ) {
newService ( )
2023-01-11 21:09:24 +01:00
parseMailerConfig ( Cfg )
2021-06-17 01:32:57 +02:00
}