diff --git a/modules/git/command.go b/modules/git/command.go index d71497f1d7..a1bacbb707 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -105,23 +105,36 @@ type RunOpts struct { PipelineFunc func(context.Context, context.CancelFunc) error } +func commonBaseEnvs() []string { + // at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it + envs := []string{ + "HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config + "GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace) + } + + // some environment variables should be passed to git command + passThroughEnvKeys := []string{ + "GNUPGHOME", // git may call gnupg to do commit signing + } + for _, key := range passThroughEnvKeys { + if val, ok := os.LookupEnv(key); ok { + envs = append(envs, key+"="+val) + } + } + return envs +} + // CommonGitCmdEnvs returns the common environment variables for a "git" command. func CommonGitCmdEnvs() []string { - // at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it - return []string{ - fmt.Sprintf("LC_ALL=%s", DefaultLocale), - "GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3 - "GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace) - "HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config - } + return append(commonBaseEnvs(), []string{ + "LC_ALL=" + DefaultLocale, + "GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3 + }...) } // CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command func CommonCmdServEnvs() []string { - return []string{ - "GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace) - "HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config - } + return commonBaseEnvs() } // Run runs the command with the RunOpts diff --git a/modules/git/git.go b/modules/git/git.go index 8ee31e815e..96e4efb486 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/hashicorp/go-version" ) @@ -167,6 +168,47 @@ func InitSimple(ctx context.Context) error { var initOnce sync.Once +func initFixGitHome117rc() error { + // Gitea 1.17-rc uses "setting.RepoRootPath" for Git HOME, which is incorrect. + // Do this check to make sure there is no legacy file in the RepoRootPath. This check might be able to be removed with 1.19 release. + + // remove the auto generated git config file (it will be moved to new home) + gitConfigNewPath := filepath.Join(HomeDir(), ".gitconfig") + gitConfigLegacyPath := filepath.Join(setting.RepoRootPath, ".gitconfig") + if ok, err := util.IsExist(gitConfigLegacyPath); ok && err == nil { + if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { + return err + } + if ok, err = util.IsExist(gitConfigNewPath); !ok && err == nil { + err = util.CopyFile(gitConfigLegacyPath, gitConfigNewPath) + } else { + err = util.CopyFile(gitConfigLegacyPath, gitConfigNewPath+".bak") + } + if err != nil { + return err + } + _ = os.Remove(gitConfigLegacyPath) + } + + // remove the empty directories, if some directories are non-empty, warn users and exit + var hasCheckErr bool + for _, wellDirName := range []string{".ssh", ".gnupg"} { + checkLegacyDir := filepath.Join(setting.RepoRootPath, wellDirName) + _ = os.Remove(checkLegacyDir) // try to remove the empty dummy directory first + _, checkErr := os.Stat(checkLegacyDir) // if the directory is not empty, then it won't be removed, it should be handled manually + if checkErr == nil || !errors.Is(checkErr, os.ErrNotExist) { + log.Error(`Git HOME has been moved to [git].HOME_PATH, but there are legacy file in old place. Please backup and remove the legacy files %q`, checkLegacyDir) + hasCheckErr = true + } + } + + if hasCheckErr { + log.Fatal("Please fix errors above, remove legacy files.") + } + + return nil +} + // InitOnceWithSync initializes git module with version check and change global variables, sync gitconfig. // This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too), // otherwise there will be data-race problem at the moment. @@ -176,28 +218,12 @@ func InitOnceWithSync(ctx context.Context) (err error) { } initOnce.Do(func() { - err = InitSimple(ctx) - if err != nil { + if err = InitSimple(ctx); err != nil { return } - - // Gitea 1.17-rc uses "setting.RepoRootPath" for Git HOME, which is incorrect. - // Do this check to make sure there is no legacy file in the RepoRootPath. This check might be able to be removed with 1.19 release. - var hasCheckErr bool - _ = os.Remove(filepath.Join(setting.RepoRootPath, ".gitconfig")) // remove the auto generated git config file - _ = os.Remove(filepath.Join(setting.RepoRootPath, ".ssh")) // remove the empty dummy ".ssh" directory - for _, wellKnownName := range []string{".ssh", ".gnupg"} { - checkLegacyFile := filepath.Join(setting.RepoRootPath, wellKnownName) - _, checkErr := os.Stat(checkLegacyFile) - if checkErr == nil || !errors.Is(checkErr, os.ErrNotExist) { - log.Error(`Git HOME has been moved to [git].HOME_PATH, but there are legacy file in old place. Please backup and remove the legacy files %q`, checkLegacyFile) - hasCheckErr = true - } + if err = initFixGitHome117rc(); err != nil { + return } - if hasCheckErr { - log.Fatal("Please fix errors above, remove legacy files") - } - // end of legacy Gitea 1.17-rc check // Since git wire protocol has been released from git v2.18 if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {