diff --git a/modules/setting/cron.go b/modules/setting/cron.go new file mode 100644 index 0000000000..48298b453c --- /dev/null +++ b/modules/setting/cron.go @@ -0,0 +1,124 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package setting + +import ( + "time" + + "code.gitea.io/gitea/modules/log" +) + +var ( + + // Cron tasks + Cron = struct { + UpdateMirror struct { + Enabled bool + RunAtStart bool + Schedule string + } `ini:"cron.update_mirrors"` + RepoHealthCheck struct { + Enabled bool + RunAtStart bool + Schedule string + Timeout time.Duration + Args []string `delim:" "` + } `ini:"cron.repo_health_check"` + CheckRepoStats struct { + Enabled bool + RunAtStart bool + Schedule string + } `ini:"cron.check_repo_stats"` + ArchiveCleanup struct { + Enabled bool + RunAtStart bool + Schedule string + OlderThan time.Duration + } `ini:"cron.archive_cleanup"` + SyncExternalUsers struct { + Enabled bool + RunAtStart bool + Schedule string + UpdateExisting bool + } `ini:"cron.sync_external_users"` + DeletedBranchesCleanup struct { + Enabled bool + RunAtStart bool + Schedule string + OlderThan time.Duration + } `ini:"cron.deleted_branches_cleanup"` + }{ + UpdateMirror: struct { + Enabled bool + RunAtStart bool + Schedule string + }{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 10m", + }, + RepoHealthCheck: struct { + Enabled bool + RunAtStart bool + Schedule string + Timeout time.Duration + Args []string `delim:" "` + }{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 24h", + Timeout: 60 * time.Second, + Args: []string{}, + }, + CheckRepoStats: struct { + Enabled bool + RunAtStart bool + Schedule string + }{ + Enabled: true, + RunAtStart: true, + Schedule: "@every 24h", + }, + ArchiveCleanup: struct { + Enabled bool + RunAtStart bool + Schedule string + OlderThan time.Duration + }{ + Enabled: true, + RunAtStart: true, + Schedule: "@every 24h", + OlderThan: 24 * time.Hour, + }, + SyncExternalUsers: struct { + Enabled bool + RunAtStart bool + Schedule string + UpdateExisting bool + }{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 24h", + UpdateExisting: true, + }, + DeletedBranchesCleanup: struct { + Enabled bool + RunAtStart bool + Schedule string + OlderThan time.Duration + }{ + Enabled: true, + RunAtStart: true, + Schedule: "@every 24h", + OlderThan: 24 * time.Hour, + }, + } +) + +func newCron() { + if err := Cfg.Section("cron").MapTo(&Cron); err != nil { + log.Fatal(4, "Failed to map Cron settings: %v", err) + } +} diff --git a/modules/setting/git.go b/modules/setting/git.go new file mode 100644 index 0000000000..59951fcb94 --- /dev/null +++ b/modules/setting/git.go @@ -0,0 +1,71 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package setting + +import ( + "time" + + "code.gitea.io/git" + "code.gitea.io/gitea/modules/log" + version "github.com/mcuadros/go-version" +) + +var ( + // Git settings + Git = struct { + Version string `ini:"-"` + DisableDiffHighlight bool + MaxGitDiffLines int + MaxGitDiffLineCharacters int + MaxGitDiffFiles int + GCArgs []string `delim:" "` + Timeout struct { + Default int + Migrate int + Mirror int + Clone int + Pull int + GC int `ini:"GC"` + } `ini:"git.timeout"` + }{ + DisableDiffHighlight: false, + MaxGitDiffLines: 1000, + MaxGitDiffLineCharacters: 5000, + MaxGitDiffFiles: 100, + GCArgs: []string{}, + Timeout: struct { + Default int + Migrate int + Mirror int + Clone int + Pull int + GC int `ini:"GC"` + }{ + Default: int(git.DefaultCommandExecutionTimeout / time.Second), + Migrate: 600, + Mirror: 300, + Clone: 300, + Pull: 300, + GC: 60, + }, + } +) + +func newGit() { + if err := Cfg.Section("git").MapTo(&Git); err != nil { + log.Fatal(4, "Failed to map Git settings: %v", err) + } + git.DefaultCommandExecutionTimeout = time.Duration(Git.Timeout.Default) * time.Second + + binVersion, err := git.BinVersion() + if err != nil { + log.Fatal(4, "Error retrieving git version: %v", err) + } + + if version.Compare(binVersion, "2.9", ">=") { + // Explicitly disable credential helper, otherwise Git credentials might leak + git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "credential.helper=") + } +} diff --git a/modules/setting/markup.go b/modules/setting/markup.go new file mode 100644 index 0000000000..41f3cdd3a1 --- /dev/null +++ b/modules/setting/markup.go @@ -0,0 +1,66 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package setting + +import ( + "regexp" + "strings" + + "code.gitea.io/gitea/modules/log" +) + +// ExternalMarkupParsers represents the external markup parsers +var ( + ExternalMarkupParsers []MarkupParser +) + +// MarkupParser defines the external parser configured in ini +type MarkupParser struct { + Enabled bool + MarkupName string + Command string + FileExtensions []string + IsInputFile bool +} + +func newMarkup() { + extensionReg := regexp.MustCompile(`\.\w`) + for _, sec := range Cfg.Section("markup").ChildSections() { + name := strings.TrimPrefix(sec.Name(), "markup.") + if name == "" { + log.Warn("name is empty, markup " + sec.Name() + "ignored") + continue + } + + extensions := sec.Key("FILE_EXTENSIONS").Strings(",") + var exts = make([]string, 0, len(extensions)) + for _, extension := range extensions { + if !extensionReg.MatchString(extension) { + log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored") + } else { + exts = append(exts, extension) + } + } + + if len(exts) == 0 { + log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored") + continue + } + + command := sec.Key("RENDER_COMMAND").MustString("") + if command == "" { + log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored") + continue + } + + ExternalMarkupParsers = append(ExternalMarkupParsers, MarkupParser{ + Enabled: sec.Key("ENABLED").MustBool(false), + MarkupName: name, + FileExtensions: exts, + Command: command, + IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false), + }) + } +} diff --git a/modules/setting/repository.go b/modules/setting/repository.go new file mode 100644 index 0000000000..5880071286 --- /dev/null +++ b/modules/setting/repository.go @@ -0,0 +1,168 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package setting + +import ( + "path" + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/log" + "github.com/Unknwon/com" +) + +// enumerates all the policy repository creating +const ( + RepoCreatingLastUserVisibility = "last" + RepoCreatingPrivate = "private" + RepoCreatingPublic = "public" +) + +// Repository settings +var ( + Repository = struct { + AnsiCharset string + ForcePrivate bool + DefaultPrivate string + MaxCreationLimit int + MirrorQueueLength int + PullRequestQueueLength int + PreferredLicenses []string + DisableHTTPGit bool + AccessControlAllowOrigin string + UseCompatSSHURI bool + DefaultCloseIssuesViaCommitsInAnyBranch bool + + // Repository editor settings + Editor struct { + LineWrapExtensions []string + PreviewableFileModes []string + } `ini:"-"` + + // Repository upload settings + Upload struct { + Enabled bool + TempPath string + AllowedTypes []string `delim:"|"` + FileMaxSize int64 + MaxFiles int + } `ini:"-"` + + // Repository local settings + Local struct { + LocalCopyPath string + LocalWikiPath string + } `ini:"-"` + + // Pull request settings + PullRequest struct { + WorkInProgressPrefixes []string + } `ini:"repository.pull-request"` + + // Issue Setting + Issue struct { + LockReasons []string + } `ini:"repository.issue"` + }{ + AnsiCharset: "", + ForcePrivate: false, + DefaultPrivate: RepoCreatingLastUserVisibility, + MaxCreationLimit: -1, + MirrorQueueLength: 1000, + PullRequestQueueLength: 1000, + PreferredLicenses: []string{"Apache License 2.0,MIT License"}, + DisableHTTPGit: false, + AccessControlAllowOrigin: "", + UseCompatSSHURI: false, + DefaultCloseIssuesViaCommitsInAnyBranch: false, + + // Repository editor settings + Editor: struct { + LineWrapExtensions []string + PreviewableFileModes []string + }{ + LineWrapExtensions: strings.Split(".txt,.md,.markdown,.mdown,.mkd,", ","), + PreviewableFileModes: []string{"markdown"}, + }, + + // Repository upload settings + Upload: struct { + Enabled bool + TempPath string + AllowedTypes []string `delim:"|"` + FileMaxSize int64 + MaxFiles int + }{ + Enabled: true, + TempPath: "data/tmp/uploads", + AllowedTypes: []string{}, + FileMaxSize: 3, + MaxFiles: 5, + }, + + // Repository local settings + Local: struct { + LocalCopyPath string + LocalWikiPath string + }{ + LocalCopyPath: "tmp/local-repo", + LocalWikiPath: "tmp/local-wiki", + }, + + // Pull request settings + PullRequest: struct { + WorkInProgressPrefixes []string + }{ + WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, + }, + + // Issue settings + Issue: struct { + LockReasons []string + }{ + LockReasons: strings.Split("Too heated,Off-topic,Spam,Resolved", ","), + }, + } + RepoRootPath string + ScriptType = "bash" +) + +func newRepository() { + homeDir, err := com.HomeDir() + if err != nil { + log.Fatal(4, "Failed to get home directory: %v", err) + } + homeDir = strings.Replace(homeDir, "\\", "/", -1) + + // Determine and create root git repository path. + sec := Cfg.Section("repository") + Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() + Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() + Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) + RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gitea-repositories")) + forcePathSeparator(RepoRootPath) + if !filepath.IsAbs(RepoRootPath) { + RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath) + } else { + RepoRootPath = filepath.Clean(RepoRootPath) + } + ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") + + if err = Cfg.Section("repository").MapTo(&Repository); err != nil { + log.Fatal(4, "Failed to map Repository settings: %v", err) + } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { + log.Fatal(4, "Failed to map Repository.Editor settings: %v", err) + } else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { + log.Fatal(4, "Failed to map Repository.Upload settings: %v", err) + } else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { + log.Fatal(4, "Failed to map Repository.Local settings: %v", err) + } else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { + log.Fatal(4, "Failed to map Repository.PullRequest settings: %v", err) + } + + if !filepath.IsAbs(Repository.Upload.TempPath) { + Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) + } +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 0962c5d726..ac44f54f0a 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -15,7 +15,6 @@ import ( "os/exec" "path" "path/filepath" - "regexp" "runtime" "strconv" "strings" @@ -63,22 +62,6 @@ const ( LandingPageOrganizations LandingPage = "/explore/organizations" ) -// MarkupParser defines the external parser configured in ini -type MarkupParser struct { - Enabled bool - MarkupName string - Command string - FileExtensions []string - IsInputFile bool -} - -// enumerates all the policy repository creating -const ( - RepoCreatingLastUserVisibility = "last" - RepoCreatingPrivate = "private" - RepoCreatingPublic = "public" -) - // enumerates all the types of captchas const ( ImageCaptcha = "image" @@ -181,113 +164,6 @@ var ( DBConnectRetries int DBConnectBackoff time.Duration - // Repository settings - Repository = struct { - AnsiCharset string - ForcePrivate bool - DefaultPrivate string - MaxCreationLimit int - MirrorQueueLength int - PullRequestQueueLength int - PreferredLicenses []string - DisableHTTPGit bool - AccessControlAllowOrigin string - UseCompatSSHURI bool - DefaultCloseIssuesViaCommitsInAnyBranch bool - - // Repository editor settings - Editor struct { - LineWrapExtensions []string - PreviewableFileModes []string - } `ini:"-"` - - // Repository upload settings - Upload struct { - Enabled bool - TempPath string - AllowedTypes []string `delim:"|"` - FileMaxSize int64 - MaxFiles int - } `ini:"-"` - - // Repository local settings - Local struct { - LocalCopyPath string - LocalWikiPath string - } `ini:"-"` - - // Pull request settings - PullRequest struct { - WorkInProgressPrefixes []string - } `ini:"repository.pull-request"` - - // Issue Setting - Issue struct { - LockReasons []string - } `ini:"repository.issue"` - }{ - AnsiCharset: "", - ForcePrivate: false, - DefaultPrivate: RepoCreatingLastUserVisibility, - MaxCreationLimit: -1, - MirrorQueueLength: 1000, - PullRequestQueueLength: 1000, - PreferredLicenses: []string{"Apache License 2.0,MIT License"}, - DisableHTTPGit: false, - AccessControlAllowOrigin: "", - UseCompatSSHURI: false, - DefaultCloseIssuesViaCommitsInAnyBranch: false, - - // Repository editor settings - Editor: struct { - LineWrapExtensions []string - PreviewableFileModes []string - }{ - LineWrapExtensions: strings.Split(".txt,.md,.markdown,.mdown,.mkd,", ","), - PreviewableFileModes: []string{"markdown"}, - }, - - // Repository upload settings - Upload: struct { - Enabled bool - TempPath string - AllowedTypes []string `delim:"|"` - FileMaxSize int64 - MaxFiles int - }{ - Enabled: true, - TempPath: "data/tmp/uploads", - AllowedTypes: []string{}, - FileMaxSize: 3, - MaxFiles: 5, - }, - - // Repository local settings - Local: struct { - LocalCopyPath string - LocalWikiPath string - }{ - LocalCopyPath: "tmp/local-repo", - LocalWikiPath: "tmp/local-wiki", - }, - - // Pull request settings - PullRequest: struct { - WorkInProgressPrefixes []string - }{ - WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, - }, - - // Issue settings - Issue: struct { - LockReasons []string - }{ - LockReasons: strings.Split("Too heated,Off-topic,Spam,Resolved", ","), - }, - } - RepoRootPath string - ScriptType = "bash" - // UI settings UI = struct { ExplorePagingNum int @@ -400,149 +276,6 @@ var ( CSRFCookieName = "_csrf" - // Cron tasks - Cron = struct { - UpdateMirror struct { - Enabled bool - RunAtStart bool - Schedule string - } `ini:"cron.update_mirrors"` - RepoHealthCheck struct { - Enabled bool - RunAtStart bool - Schedule string - Timeout time.Duration - Args []string `delim:" "` - } `ini:"cron.repo_health_check"` - CheckRepoStats struct { - Enabled bool - RunAtStart bool - Schedule string - } `ini:"cron.check_repo_stats"` - ArchiveCleanup struct { - Enabled bool - RunAtStart bool - Schedule string - OlderThan time.Duration - } `ini:"cron.archive_cleanup"` - SyncExternalUsers struct { - Enabled bool - RunAtStart bool - Schedule string - UpdateExisting bool - } `ini:"cron.sync_external_users"` - DeletedBranchesCleanup struct { - Enabled bool - RunAtStart bool - Schedule string - OlderThan time.Duration - } `ini:"cron.deleted_branches_cleanup"` - }{ - UpdateMirror: struct { - Enabled bool - RunAtStart bool - Schedule string - }{ - Enabled: true, - RunAtStart: false, - Schedule: "@every 10m", - }, - RepoHealthCheck: struct { - Enabled bool - RunAtStart bool - Schedule string - Timeout time.Duration - Args []string `delim:" "` - }{ - Enabled: true, - RunAtStart: false, - Schedule: "@every 24h", - Timeout: 60 * time.Second, - Args: []string{}, - }, - CheckRepoStats: struct { - Enabled bool - RunAtStart bool - Schedule string - }{ - Enabled: true, - RunAtStart: true, - Schedule: "@every 24h", - }, - ArchiveCleanup: struct { - Enabled bool - RunAtStart bool - Schedule string - OlderThan time.Duration - }{ - Enabled: true, - RunAtStart: true, - Schedule: "@every 24h", - OlderThan: 24 * time.Hour, - }, - SyncExternalUsers: struct { - Enabled bool - RunAtStart bool - Schedule string - UpdateExisting bool - }{ - Enabled: true, - RunAtStart: false, - Schedule: "@every 24h", - UpdateExisting: true, - }, - DeletedBranchesCleanup: struct { - Enabled bool - RunAtStart bool - Schedule string - OlderThan time.Duration - }{ - Enabled: true, - RunAtStart: true, - Schedule: "@every 24h", - OlderThan: 24 * time.Hour, - }, - } - - // Git settings - Git = struct { - Version string `ini:"-"` - DisableDiffHighlight bool - MaxGitDiffLines int - MaxGitDiffLineCharacters int - MaxGitDiffFiles int - GCArgs []string `delim:" "` - Timeout struct { - Default int - Migrate int - Mirror int - Clone int - Pull int - GC int `ini:"GC"` - } `ini:"git.timeout"` - }{ - DisableDiffHighlight: false, - MaxGitDiffLines: 1000, - MaxGitDiffLineCharacters: 5000, - MaxGitDiffFiles: 100, - GCArgs: []string{}, - Timeout: struct { - Default int - Migrate int - Mirror int - Clone int - Pull int - GC int `ini:"GC"` - }{ - Default: int(git.DefaultCommandExecutionTimeout / time.Second), - Migrate: 600, - Mirror: 300, - Clone: 300, - Pull: 300, - GC: 60, - }, - } - // Mirror settings Mirror struct { DefaultInterval time.Duration @@ -612,7 +345,6 @@ var ( InternalToken string // internal access token IterateBufferSize int - ExternalMarkupParsers []MarkupParser // UILocation is the location on the UI, so that we can display the time on UI. // Currently only show the default time.Local, it could be added to app.ini after UI is ready UILocation = time.Local @@ -1064,34 +796,7 @@ func NewContext() { SSH.BuiltinServerUser = Cfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser) - // Determine and create root git repository path. - sec = Cfg.Section("repository") - Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() - Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() - Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) - RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gitea-repositories")) - forcePathSeparator(RepoRootPath) - if !filepath.IsAbs(RepoRootPath) { - RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath) - } else { - RepoRootPath = filepath.Clean(RepoRootPath) - } - ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") - if err = Cfg.Section("repository").MapTo(&Repository); err != nil { - log.Fatal(4, "Failed to map Repository settings: %v", err) - } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { - log.Fatal(4, "Failed to map Repository.Editor settings: %v", err) - } else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil { - log.Fatal(4, "Failed to map Repository.Upload settings: %v", err) - } else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil { - log.Fatal(4, "Failed to map Repository.Local settings: %v", err) - } else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil { - log.Fatal(4, "Failed to map Repository.PullRequest settings: %v", err) - } - - if !filepath.IsAbs(Repository.Upload.TempPath) { - Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) - } + newRepository() sec = Cfg.Section("picture") AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "avatars")) @@ -1145,17 +850,14 @@ func NewContext() { log.Fatal(4, "Failed to map Markdown settings: %v", err) } else if err = Cfg.Section("admin").MapTo(&Admin); err != nil { log.Fatal(4, "Fail to map Admin settings: %v", err) - } else if err = Cfg.Section("cron").MapTo(&Cron); err != nil { - log.Fatal(4, "Failed to map Cron settings: %v", err) - } else if err = Cfg.Section("git").MapTo(&Git); err != nil { - log.Fatal(4, "Failed to map Git settings: %v", err) } else if err = Cfg.Section("api").MapTo(&API); err != nil { log.Fatal(4, "Failed to map API settings: %v", err) } else if err = Cfg.Section("metrics").MapTo(&Metrics); err != nil { log.Fatal(4, "Failed to map Metrics settings: %v", err) } - git.DefaultCommandExecutionTimeout = time.Duration(Git.Timeout.Default) * time.Second + newCron() + newGit() sec = Cfg.Section("mirror") Mirror.MinInterval = sec.Key("MIN_INTERVAL").MustDuration(10 * time.Minute) @@ -1193,57 +895,11 @@ func NewContext() { HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt")) - extensionReg := regexp.MustCompile(`\.\w`) - for _, sec := range Cfg.Section("markup").ChildSections() { - name := strings.TrimPrefix(sec.Name(), "markup.") - if name == "" { - log.Warn("name is empty, markup " + sec.Name() + "ignored") - continue - } - - extensions := sec.Key("FILE_EXTENSIONS").Strings(",") - var exts = make([]string, 0, len(extensions)) - for _, extension := range extensions { - if !extensionReg.MatchString(extension) { - log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored") - } else { - exts = append(exts, extension) - } - } - - if len(exts) == 0 { - log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored") - continue - } - - command := sec.Key("RENDER_COMMAND").MustString("") - if command == "" { - log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored") - continue - } - - ExternalMarkupParsers = append(ExternalMarkupParsers, MarkupParser{ - Enabled: sec.Key("ENABLED").MustBool(false), - MarkupName: name, - FileExtensions: exts, - Command: command, - IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false), - }) - } + newMarkup() sec = Cfg.Section("U2F") U2F.TrustedFacets, _ = shellquote.Split(sec.Key("TRUSTED_FACETS").MustString(strings.TrimRight(AppURL, "/"))) U2F.AppID = sec.Key("APP_ID").MustString(strings.TrimRight(AppURL, "/")) - - binVersion, err := git.BinVersion() - if err != nil { - log.Fatal(4, "Error retrieving git version: %v", err) - } - - if version.Compare(binVersion, "2.9", ">=") { - // Explicitly disable credential helper, otherwise Git credentials might leak - git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "credential.helper=") - } } func loadInternalToken(sec *ini.Section) string {