From 1065f03454dac629a84eadd8b46876f66f0092b3 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 6 Oct 2026 16:22:36 +0800 Subject: [PATCH] refactor: clarify GOOS detection (#39620) Introduce `consts.IsWindows`, now it's clearer to see how Windows build works --- models/asymkey/ssh_key_authorized_keys.go | 3 ++- modules/consts/runtime.go | 8 ++++++++ modules/dump/dumper.go | 4 ++-- modules/git/config.go | 4 ++-- modules/git/git.go | 9 --------- modules/git/hooks.go | 4 ++-- modules/setting/path.go | 3 ++- modules/setting/setting.go | 6 ++---- modules/setting/testenv.go | 3 ++- modules/user/user.go | 9 +++++---- modules/user/user_test.go | 5 +++-- modules/util/file_retry.go | 4 +++- modules/util/path.go | 10 ++++++---- modules/util/path_test.go | 4 +++- modules/util/runtime.go | 2 -- routers/web/web.go | 3 ++- 16 files changed, 44 insertions(+), 37 deletions(-) create mode 100644 modules/consts/runtime.go diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index 1a45f5c4390..850c982b966 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -15,6 +15,7 @@ import ( "sync" "gitea.dev/models/db" + "gitea.dev/modules/consts" "gitea.dev/modules/log" "gitea.dev/modules/setting" "gitea.dev/modules/util" @@ -135,7 +136,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error { defer f.Close() // Note: chmod command does not support in Windows. - if !setting.IsWindows { + if !consts.IsWindows { fi, err := f.Stat() if err != nil { return err diff --git a/modules/consts/runtime.go b/modules/consts/runtime.go new file mode 100644 index 00000000000..1d7c8c02a00 --- /dev/null +++ b/modules/consts/runtime.go @@ -0,0 +1,8 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package consts + +import "runtime" + +const IsWindows = runtime.GOOS == "windows" diff --git a/modules/dump/dumper.go b/modules/dump/dumper.go index edc666db6df..7e0c22353bf 100644 --- a/modules/dump/dumper.go +++ b/modules/dump/dumper.go @@ -16,8 +16,8 @@ import ( "slices" "strings" + "gitea.dev/modules/consts" "gitea.dev/modules/log" - "gitea.dev/modules/setting" "gitea.dev/modules/timeutil" "github.com/mholt/archives" @@ -184,7 +184,7 @@ func (dumper *Dumper) Close() error { func (dumper *Dumper) normalizeFilePath(absPath string) string { absPath = filepath.Clean(absPath) - if setting.IsWindows { + if consts.IsWindows { absPath = strings.ToLower(absPath) } return absPath diff --git a/modules/git/config.go b/modules/git/config.go index abf0051c66b..ead5bd4497c 100644 --- a/modules/git/config.go +++ b/modules/git/config.go @@ -8,9 +8,9 @@ import ( "fmt" "os" "regexp" - "runtime" "strings" + "gitea.dev/modules/consts" "gitea.dev/modules/git/gitcmd" "gitea.dev/modules/setting" ) @@ -90,7 +90,7 @@ func syncGitConfig(ctx context.Context) (err error) { return err } - if runtime.GOOS == "windows" { + if consts.IsWindows { if err := configSet(ctx, "core.longpaths", "true"); err != nil { return err } diff --git a/modules/git/git.go b/modules/git/git.go index 532bc846419..7c75f0ce8ec 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strings" "gitea.dev/modules/cache" @@ -126,16 +125,8 @@ func checkGitVersionCompatibility(gitVer *version.Version) error { func ensureGitVersion() error { if !DefaultFeatures().CheckVersionAtLeast(RequiredVersion) { moreHint := "get git: https://git-scm.com/downloads" - if runtime.GOOS == "linux" { - // there are a lot of CentOS/RHEL users using old git, so we add a special hint for them - if _, err := os.Stat("/etc/redhat-release"); err == nil { - // ius.io is the recommended official(git-scm.com) method to install git - moreHint = "get git: https://git-scm.com/downloads/linux and https://ius.io" - } - } return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", DefaultFeatures().gitVersion.Original(), RequiredVersion, moreHint) } - if err := checkGitVersionCompatibility(DefaultFeatures().gitVersion); err != nil { return fmt.Errorf("installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git", DefaultFeatures().gitVersion.String(), err) } diff --git a/modules/git/hooks.go b/modules/git/hooks.go index 365706de06e..4237ff4d874 100644 --- a/modules/git/hooks.go +++ b/modules/git/hooks.go @@ -8,8 +8,8 @@ import ( "fmt" "os" "path/filepath" - "runtime" + "gitea.dev/modules/consts" "gitea.dev/modules/git/gitrepo" "gitea.dev/modules/setting" "gitea.dev/modules/util" @@ -156,7 +156,7 @@ func createDelegateHooks(hookDir string) (err error) { func checkExecutable(filename string) bool { // windows has no concept of a executable bit - if runtime.GOOS == "windows" { + if consts.IsWindows { return true } fileInfo, err := os.Stat(filename) diff --git a/modules/setting/path.go b/modules/setting/path.go index 3c16c61033e..545fa0efb78 100644 --- a/modules/setting/path.go +++ b/modules/setting/path.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "gitea.dev/modules/consts" "gitea.dev/modules/log" "gitea.dev/modules/tempdir" ) @@ -39,7 +40,7 @@ var ( func getAppPath() (string, error) { var appPath string var err error - if IsWindows && filepath.IsAbs(os.Args[0]) { + if consts.IsWindows && filepath.IsAbs(os.Args[0]) { appPath = filepath.Clean(os.Args[0]) } else { appPath, err = exec.LookPath(os.Args[0]) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 94d3432ad2d..77493c23872 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -8,18 +8,16 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strings" "time" + "gitea.dev/modules/consts" "gitea.dev/modules/log" "gitea.dev/modules/optional" "gitea.dev/modules/user" "gitea.dev/modules/util" ) -const IsWindows = runtime.GOOS == "windows" - var ( // AppVer is the version of the current build of Gitea. It is set in main.go from main.Version. AppVer string @@ -52,7 +50,7 @@ func init() { // 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) { - if IsWindows || SSH.StartBuiltinServer { + if consts.IsWindows || SSH.StartBuiltinServer { return "", true } diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 38bb936197e..d65954c9697 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -12,6 +12,7 @@ import ( "strings" "gitea.dev/modules/auth/password/hash" + "gitea.dev/modules/consts" "gitea.dev/modules/log" "gitea.dev/modules/util" @@ -66,7 +67,7 @@ func SetupGiteaTestEnv() { StaticRootPath = giteaRoot // during testing, the AppPath must point to the pre-built Gitea binary in the source root // it needs to be called by git hooks - AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "") + AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(consts.IsWindows, ".exe", "") } initGiteaConf := func() string { diff --git a/modules/user/user.go b/modules/user/user.go index eee401a23f4..9a5406d124d 100644 --- a/modules/user/user.go +++ b/modules/user/user.go @@ -6,19 +6,20 @@ package user import ( "os" "os/user" - "runtime" "strings" + + "gitea.dev/modules/consts" ) -// CurrentUsername return current login OS user name +// CurrentUsername return current login OS username func CurrentUsername() string { userinfo, err := user.Current() if err != nil { return fallbackCurrentUsername() } username := userinfo.Username - if runtime.GOOS == "windows" { - parts := strings.Split(username, "\\") + if consts.IsWindows { + parts := strings.Split(username, "\\") // remove domain if present username = parts[len(parts)-1] } return username diff --git a/modules/user/user_test.go b/modules/user/user_test.go index d6b3911ca6e..d1d5f4cfd3f 100644 --- a/modules/user/user_test.go +++ b/modules/user/user_test.go @@ -5,10 +5,11 @@ package user import ( "os/exec" - "runtime" "strings" "testing" + "gitea.dev/modules/consts" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +27,7 @@ func TestCurrentUsername(t *testing.T) { require.NotEmpty(t, user) // Windows whoami is weird, so just skip remaining tests - if runtime.GOOS == "windows" { + if consts.IsWindows { t.Skip("skipped test because of weird whoami on Windows") } whoami, err := getWhoamiOutput() diff --git a/modules/util/file_retry.go b/modules/util/file_retry.go index ac156feb915..5b050b50731 100644 --- a/modules/util/file_retry.go +++ b/modules/util/file_retry.go @@ -8,6 +8,8 @@ import ( "os" "syscall" "time" + + "gitea.dev/modules/consts" ) // On Windows, when a file or directory is in use (opened), the file or directory is not able to be removed or renamed. @@ -32,7 +34,7 @@ func retryWhenFileBusyInternal(count int, delay time.Duration, f func() error) ( break } isErrBusy := errors.Is(err, syscall.EBUSY) - isErrBusy = isErrBusy || (isOSWindows && errors.Is(err, errWindowsSharingViolationError)) + isErrBusy = isErrBusy || (consts.IsWindows && errors.Is(err, errWindowsSharingViolationError)) if !isErrBusy { break } diff --git a/modules/util/path.go b/modules/util/path.go index 7d67cc72cb6..43c2cce67a2 100644 --- a/modules/util/path.go +++ b/modules/util/path.go @@ -13,6 +13,8 @@ import ( "regexp" "strings" "sync" + + "gitea.dev/modules/consts" ) var PathNameValidator = sync.OnceValue(func() (ret struct { @@ -93,7 +95,7 @@ const filepathSeparator = string(os.PathSeparator) func FilePathJoinAbs(base string, sub ...string) string { // POSIX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators // to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/` - if !isOSWindows { + if !consts.IsWindows { base = strings.ReplaceAll(base, "\\", filepathSeparator) } if !filepath.IsAbs(base) { @@ -110,7 +112,7 @@ func FilePathJoinAbs(base string, sub ...string) string { if s == "" { continue } - if isOSWindows { + if consts.IsWindows { elems = append(elems, filepath.Clean(filepathSeparator+s)) } else { elems = append(elems, filepath.Clean(filepathSeparator+strings.ReplaceAll(s, "\\", filepathSeparator))) @@ -284,7 +286,7 @@ func fileURLToPathInternal(u *url.URL, isWindows bool) (string, error) { // FileURLToPath extracts the path information from a file://... url. // It returns an error only if the URL is not a file URL. func FileURLToPath(u *url.URL) (string, error) { - return fileURLToPathInternal(u, isOSWindows) + return fileURLToPathInternal(u, consts.IsWindows) } // HomeDir returns path of '~'(in Linux) on Windows, @@ -293,7 +295,7 @@ func HomeDir() (home string, err error) { // TODO: some users run Gitea with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually) // TODO: when running gitea as a sub command inside git, the HOME directory is not the user's home directory // so at the moment we can not use `user.Current().HomeDir` - if isOSWindows { + if consts.IsWindows { home = os.Getenv("USERPROFILE") if home == "" { home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") diff --git a/modules/util/path_test.go b/modules/util/path_test.go index 1132d07cb73..74adc44a89c 100644 --- a/modules/util/path_test.go +++ b/modules/util/path_test.go @@ -9,6 +9,8 @@ import ( "path/filepath" "testing" + "gitea.dev/modules/consts" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -182,7 +184,7 @@ func TestCleanPath(t *testing.T) { } // for POSIX only, but the result is similar on Windows, because the first element must be an absolute path - if isOSWindows { + if consts.IsWindows { cases = []struct { elems []string expected string diff --git a/modules/util/runtime.go b/modules/util/runtime.go index 16b9376ab4a..a29fb50b39a 100644 --- a/modules/util/runtime.go +++ b/modules/util/runtime.go @@ -8,8 +8,6 @@ import ( "strings" ) -const isOSWindows = runtime.GOOS == "windows" - func CallerFuncName(optSkipParent ...int) string { pc := make([]uintptr, 1) skipParent := 0 diff --git a/routers/web/web.go b/routers/web/web.go index 25ecfea14f6..220c75d162b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -11,6 +11,7 @@ import ( auth_model "gitea.dev/models/auth" "gitea.dev/models/perm" "gitea.dev/models/unit" + "gitea.dev/modules/consts" "gitea.dev/modules/git" "gitea.dev/modules/graceful" "gitea.dev/modules/imagecaptcha" @@ -118,7 +119,7 @@ func newWebAuthMiddleware() *AuthMiddleware { webAuth.AllowOAuth2 = middlewareSetContextValue(keyAllowOAuth2{}, true) webAuth.AllowDeployToken = middlewareSetContextValue(keyAllowDeployToken{}, true) - enableSSPI := setting.IsWindows && auth_model.IsSSPIEnabled(graceful.GetManager().ShutdownContext()) + enableSSPI := consts.IsWindows && auth_model.IsSSPIEnabled(graceful.GetManager().ShutdownContext()) webAuth.MiddlewareHandler = func(ctx *context.Context) { allowBasic := ctx.GetContextValue(keyAllowBasic{}) == true allowOAuth2 := ctx.GetContextValue(keyAllowOAuth2{}) == true