Skip to content

Commit 14ff8df

Browse files
wxiaoguangzeripathlunny
authored andcommitted
Refactor git module, make Gitea use internal git config (go-gitea#19732)
* Refactor git module, make Gitea use internal git config, add safe.directory config * introduce git.InitSimple and git.InitWithConfigSync, make serv cmd use gitconfig * use HOME instead of GIT_CONFIG_GLOBAL, because git always needs a correct HOME * fix cmd env in cmd/serv.go * fine tune error message * Fix a incorrect test case * fix configAddNonExist * fix configAddNonExist logic, add `--fixed-value` flag, add tests * add configSetNonExist function in case it's needed. * use configSetNonExist for `user.name` and `user.email` * add some comments * Update cmd/serv.go Co-authored-by: zeripath <[email protected]> * Update cmd/serv.go Co-authored-by: zeripath <[email protected]> * Update modules/git/git.go Co-authored-by: zeripath <[email protected]> * Update modules/setting/setting.go Co-authored-by: zeripath <[email protected]> * Update modules/git/repo_attribute.go Co-authored-by: zeripath <[email protected]> * fix spaces in messages * use `configSet("core.protectNTFS", ...)` instead of `globalCommandArgs` * remove GIT_CONFIG_NOSYSTEM, continue to use system's git config * Update cmd/serv.go Co-authored-by: zeripath <[email protected]> * fix merge * remove code for safe.directory * separate git.CommonEnvs to CommonGitCmdEnvs and CommonCmdServEnvs * avoid Golang's data race error Co-authored-by: zeripath <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent 989733b commit 14ff8df

File tree

21 files changed

+283
-190
lines changed

21 files changed

+283
-190
lines changed

cmd/serv.go

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package cmd
77

88
import (
9+
"context"
910
"fmt"
1011
"net/http"
1112
"net/url"
@@ -65,6 +66,21 @@ func setup(logPath string, debug bool) {
6566
if debug {
6667
setting.RunMode = "dev"
6768
}
69+
70+
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
71+
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
72+
if _, err := os.Stat(setting.RepoRootPath); err != nil {
73+
if os.IsNotExist(err) {
74+
_ = fail("Incorrect configuration, no repository directory.", "Directory `[repository].ROOT` %q was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository].ROOT` an absolute value.", setting.RepoRootPath)
75+
} else {
76+
_ = fail("Incorrect configuration, repository directory is inaccessible", "Directory `[repository].ROOT` %q is inaccessible. err: %v", setting.RepoRootPath, err)
77+
}
78+
return
79+
}
80+
81+
if err := git.InitSimple(context.Background()); err != nil {
82+
_ = fail("Failed to init git", "Failed to init git, err: %v", err)
83+
}
6884
}
6985

7086
var (
@@ -80,12 +96,12 @@ var (
8096
func fail(userMessage, logMessage string, args ...interface{}) error {
8197
// There appears to be a chance to cause a zombie process and failure to read the Exit status
8298
// if nothing is outputted on stdout.
83-
fmt.Fprintln(os.Stdout, "")
84-
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
99+
_, _ = fmt.Fprintln(os.Stdout, "")
100+
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
85101

86102
if len(logMessage) > 0 {
87103
if !setting.IsProd {
88-
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
104+
_, _ = fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
89105
}
90106
}
91107
ctx, cancel := installSignals()
@@ -237,17 +253,6 @@ func runServ(c *cli.Context) error {
237253
}
238254
return fail("Internal Server Error", "%s", err.Error())
239255
}
240-
os.Setenv(repo_module.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
241-
os.Setenv(repo_module.EnvRepoName, results.RepoName)
242-
os.Setenv(repo_module.EnvRepoUsername, results.OwnerName)
243-
os.Setenv(repo_module.EnvPusherName, results.UserName)
244-
os.Setenv(repo_module.EnvPusherEmail, results.UserEmail)
245-
os.Setenv(repo_module.EnvPusherID, strconv.FormatInt(results.UserID, 10))
246-
os.Setenv(repo_module.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
247-
os.Setenv(repo_module.EnvPRID, fmt.Sprintf("%d", 0))
248-
os.Setenv(repo_module.EnvDeployKeyID, fmt.Sprintf("%d", results.DeployKeyID))
249-
os.Setenv(repo_module.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
250-
os.Setenv(repo_module.EnvAppURL, setting.AppURL)
251256

252257
// LFS token authentication
253258
if verb == lfsAuthenticateVerb {
@@ -298,20 +303,29 @@ func runServ(c *cli.Context) error {
298303
gitcmd = exec.CommandContext(ctx, verb, repoPath)
299304
}
300305

301-
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
302-
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
303-
if _, err := os.Stat(setting.RepoRootPath); err != nil {
304-
if os.IsNotExist(err) {
305-
return fail("Incorrect configuration.",
306-
"Directory `[repository]` `ROOT` %s was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository]` `ROOT` an absolute value.", setting.RepoRootPath)
307-
}
308-
}
309-
310306
process.SetSysProcAttribute(gitcmd)
311307
gitcmd.Dir = setting.RepoRootPath
312308
gitcmd.Stdout = os.Stdout
313309
gitcmd.Stdin = os.Stdin
314310
gitcmd.Stderr = os.Stderr
311+
gitcmd.Env = append(gitcmd.Env, os.Environ()...)
312+
gitcmd.Env = append(gitcmd.Env,
313+
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
314+
repo_module.EnvRepoName+"="+results.RepoName,
315+
repo_module.EnvRepoUsername+"="+results.OwnerName,
316+
repo_module.EnvPusherName+"="+results.UserName,
317+
repo_module.EnvPusherEmail+"="+results.UserEmail,
318+
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
319+
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
320+
repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
321+
repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
322+
repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
323+
repo_module.EnvAppURL+"="+setting.AppURL,
324+
)
325+
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
326+
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
327+
gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
328+
315329
if err = gitcmd.Run(); err != nil {
316330
return fail("Internal error", "Failed to execute git command: %v", err)
317331
}

integrations/integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,8 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
274274
deferFn := PrintCurrentTest(t, ourSkip)
275275
assert.NoError(t, unittest.LoadFixtures())
276276
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
277-
278277
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
278+
assert.NoError(t, git.InitWithConfigSync(context.Background()))
279279
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
280280
if err != nil {
281281
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@@ -576,6 +576,7 @@ func resetFixtures(t *testing.T) {
576576
assert.NoError(t, unittest.LoadFixtures())
577577
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
578578
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
579+
assert.NoError(t, git.InitWithConfigSync(context.Background()))
579580
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
580581
if err != nil {
581582
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

integrations/migration-test/migration_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func initMigrationTest(t *testing.T) func() {
6262
assert.True(t, len(setting.RepoRootPath) != 0)
6363
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
6464
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
65+
assert.NoError(t, git.InitWithConfigSync(context.Background()))
6566
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
6667
if err != nil {
6768
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

models/migrations/migrations_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,8 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
202202
ourSkip += skip
203203
deferFn := PrintCurrentTest(t, ourSkip)
204204
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
205-
206-
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
207-
setting.RepoRootPath))
205+
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
206+
assert.NoError(t, git.InitWithConfigSync(context.Background()))
208207
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
209208
if err != nil {
210209
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)

models/unittest/testdb.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"code.gitea.io/gitea/models/db"
1616
"code.gitea.io/gitea/modules/base"
17+
"code.gitea.io/gitea/modules/git"
1718
"code.gitea.io/gitea/modules/setting"
1819
"code.gitea.io/gitea/modules/storage"
1920
"code.gitea.io/gitea/modules/util"
@@ -116,6 +117,9 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
116117
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
117118
fatalTestError("util.CopyDir: %v\n", err)
118119
}
120+
if err = git.InitWithConfigSync(context.Background()); err != nil {
121+
fatalTestError("git.Init: %v\n", err)
122+
}
119123

120124
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
121125
if err != nil {
@@ -198,6 +202,7 @@ func PrepareTestEnv(t testing.TB) {
198202
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
199203
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
200204
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
205+
assert.NoError(t, git.InitWithConfigSync(context.Background()))
201206

202207
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
203208
assert.NoError(t, err)

modules/git/command.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package git
88
import (
99
"bytes"
1010
"context"
11+
"errors"
1112
"fmt"
1213
"io"
1314
"os"
@@ -104,6 +105,25 @@ type RunOpts struct {
104105
PipelineFunc func(context.Context, context.CancelFunc) error
105106
}
106107

108+
// CommonGitCmdEnvs returns the common environment variables for a "git" command.
109+
func CommonGitCmdEnvs() []string {
110+
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
111+
return []string{
112+
fmt.Sprintf("LC_ALL=%s", DefaultLocale),
113+
"GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3
114+
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
115+
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
116+
}
117+
}
118+
119+
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
120+
func CommonCmdServEnvs() []string {
121+
return []string{
122+
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
123+
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
124+
}
125+
}
126+
107127
// Run runs the command with the RunOpts
108128
func (c *Command) Run(opts *RunOpts) error {
109129
if opts == nil {
@@ -148,16 +168,8 @@ func (c *Command) Run(opts *RunOpts) error {
148168
cmd.Env = opts.Env
149169
}
150170

151-
cmd.Env = append(
152-
cmd.Env,
153-
fmt.Sprintf("LC_ALL=%s", DefaultLocale),
154-
// avoid prompting for credentials interactively, supported since git v2.3
155-
"GIT_TERMINAL_PROMPT=0",
156-
// ignore replace references (https://git-scm.com/docs/git-replace)
157-
"GIT_NO_REPLACE_OBJECTS=1",
158-
)
159-
160171
process.SetSysProcAttribute(cmd)
172+
cmd.Env = append(cmd.Env, CommonGitCmdEnvs()...)
161173
cmd.Dir = opts.Dir
162174
cmd.Stdout = opts.Stdout
163175
cmd.Stderr = opts.Stderr
@@ -184,7 +196,9 @@ func (c *Command) Run(opts *RunOpts) error {
184196

185197
type RunStdError interface {
186198
error
199+
Unwrap() error
187200
Stderr() string
201+
IsExitCode(code int) bool
188202
}
189203

190204
type runStdError struct {
@@ -209,6 +223,14 @@ func (r *runStdError) Stderr() string {
209223
return r.stderr
210224
}
211225

226+
func (r *runStdError) IsExitCode(code int) bool {
227+
var exitError *exec.ExitError
228+
if errors.As(r.err, &exitError) {
229+
return exitError.ExitCode() == code
230+
}
231+
return false
232+
}
233+
212234
func bytesToString(b []byte) string {
213235
return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go)
214236
}

modules/git/commit.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,11 +398,6 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
398398

399399
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
400400
func (c *Commit) GetBranchName() (string, error) {
401-
err := LoadGitVersion()
402-
if err != nil {
403-
return "", fmt.Errorf("Git version missing: %v", err)
404-
}
405-
406401
args := []string{
407402
"name-rev",
408403
}

0 commit comments

Comments
 (0)