Skip to content

Commit 74bb292

Browse files
authored
Migrate temp_repo.go to use git.NewCommand (#8918)
This PR migrates temp_repo.go to use git.NewCommand instead creating processes by itself - this fixes the problem underlying PR #8905. There are other places that run git outside of the controlled locale defined in #8548 but temp_repo.go is the only cause of failure of local testing in cases where English is not the default - implying that error messages from those other commands are not interpreted. Replaces #8905
1 parent 8d9e625 commit 74bb292

File tree

2 files changed

+93
-163
lines changed

2 files changed

+93
-163
lines changed

modules/git/command.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration
6767
// RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout,
6868
// it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin.
6969
func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error {
70+
return c.RunInDirTimeoutEnvFullPipelineFunc(env, timeout, dir, stdout, stderr, stdin, nil)
71+
}
72+
73+
// RunInDirTimeoutEnvFullPipelineFunc executes the command in given directory with given timeout,
74+
// it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin. Between cmd.Start and cmd.Wait the passed in function is run.
75+
func (c *Command) RunInDirTimeoutEnvFullPipelineFunc(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader, fn func(context.Context, context.CancelFunc)) error {
76+
7077
if timeout == -1 {
7178
timeout = DefaultCommandExecutionTimeout
7279
}
@@ -98,6 +105,10 @@ func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Dura
98105
pid := process.GetManager().Add(fmt.Sprintf("%s %s %s [repo_path: %s]", GitExecutable, c.name, strings.Join(c.args, " "), dir), cmd)
99106
defer process.GetManager().Remove(pid)
100107

108+
if fn != nil {
109+
fn(ctx, cancel)
110+
}
111+
101112
if err := cmd.Wait(); err != nil {
102113
return err
103114
}

modules/repofiles/temp_repo.go

Lines changed: 82 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ import (
1010
"fmt"
1111
"io"
1212
"os"
13-
"os/exec"
1413
"regexp"
1514
"strings"
1615
"time"
1716

1817
"code.gitea.io/gitea/models"
1918
"code.gitea.io/gitea/modules/git"
2019
"code.gitea.io/gitea/modules/log"
21-
"code.gitea.io/gitea/modules/process"
2220
"code.gitea.io/gitea/modules/setting"
2321
"code.gitea.io/gitea/services/gitdiff"
2422

@@ -51,9 +49,8 @@ func (t *TemporaryUploadRepository) Close() {
5149

5250
// Clone the base repository to our path and set branch as the HEAD
5351
func (t *TemporaryUploadRepository) Clone(branch string) error {
54-
if _, stderr, err := process.GetManager().ExecTimeout(5*time.Minute,
55-
fmt.Sprintf("Clone (git clone -s --bare): %s", t.basePath),
56-
git.GitExecutable, "clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath); err != nil {
52+
if _, err := git.NewCommand("clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).Run(); err != nil {
53+
stderr := err.Error()
5754
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
5855
return git.ErrBranchNotExist{
5956
Name: branch,
@@ -79,11 +76,8 @@ func (t *TemporaryUploadRepository) Clone(branch string) error {
7976

8077
// SetDefaultIndex sets the git index to our HEAD
8178
func (t *TemporaryUploadRepository) SetDefaultIndex() error {
82-
if _, stderr, err := process.GetManager().ExecDir(5*time.Minute,
83-
t.basePath,
84-
fmt.Sprintf("SetDefaultIndex (git read-tree HEAD): %s", t.basePath),
85-
git.GitExecutable, "read-tree", "HEAD"); err != nil {
86-
return fmt.Errorf("SetDefaultIndex: %v %s", err, stderr)
79+
if _, err := git.NewCommand("read-tree", "HEAD").RunInDir(t.basePath); err != nil {
80+
return fmt.Errorf("SetDefaultIndex: %v", err)
8781
}
8882
return nil
8983
}
@@ -93,33 +87,16 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
9387
stdOut := new(bytes.Buffer)
9488
stdErr := new(bytes.Buffer)
9589

96-
timeout := 5 * time.Minute
97-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
98-
defer cancel()
99-
10090
cmdArgs := []string{"ls-files", "-z", "--"}
10191
for _, arg := range filenames {
10292
if arg != "" {
10393
cmdArgs = append(cmdArgs, arg)
10494
}
10595
}
10696

107-
cmd := exec.CommandContext(ctx, git.GitExecutable, cmdArgs...)
108-
desc := fmt.Sprintf("lsFiles: (git ls-files) %v", cmdArgs)
109-
cmd.Dir = t.basePath
110-
cmd.Stdout = stdOut
111-
cmd.Stderr = stdErr
112-
113-
if err := cmd.Start(); err != nil {
114-
return nil, fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err())
115-
}
116-
117-
pid := process.GetManager().Add(desc, cmd)
118-
err := cmd.Wait()
119-
process.GetManager().Remove(pid)
120-
121-
if err != nil {
122-
err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr)
97+
if err := git.NewCommand(cmdArgs...).RunInDirPipeline(t.basePath, stdOut, stdErr); err != nil {
98+
log.Error("Unable to run git ls-files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
99+
err = fmt.Errorf("Unable to run git ls-files for temporary repo of: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
123100
return nil, err
124101
}
125102

@@ -128,7 +105,7 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
128105
filelist = append(filelist, string(line))
129106
}
130107

131-
return filelist, err
108+
return filelist, nil
132109
}
133110

134111
// RemoveFilesFromIndex removes the given files from the index
@@ -144,90 +121,50 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) er
144121
}
145122
}
146123

147-
timeout := 5 * time.Minute
148-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
149-
defer cancel()
150-
151-
cmdArgs := []string{"update-index", "--remove", "-z", "--index-info"}
152-
cmd := exec.CommandContext(ctx, git.GitExecutable, cmdArgs...)
153-
desc := fmt.Sprintf("removeFilesFromIndex: (git update-index) %v", filenames)
154-
cmd.Dir = t.basePath
155-
cmd.Stdout = stdOut
156-
cmd.Stderr = stdErr
157-
cmd.Stdin = bytes.NewReader(stdIn.Bytes())
158-
159-
if err := cmd.Start(); err != nil {
160-
return fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err())
161-
}
162-
163-
pid := process.GetManager().Add(desc, cmd)
164-
err := cmd.Wait()
165-
process.GetManager().Remove(pid)
166-
167-
if err != nil {
168-
err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr)
124+
if err := git.NewCommand("update-index", "--remove", "-z", "--index-info").RunInDirFullPipeline(t.basePath, stdOut, stdErr, stdIn); err != nil {
125+
log.Error("Unable to update-index for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
126+
return fmt.Errorf("Unable to update-index for temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
169127
}
170-
171-
return err
128+
return nil
172129
}
173130

174131
// HashObject writes the provided content to the object db and returns its hash
175132
func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error) {
176-
timeout := 5 * time.Minute
177-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
178-
defer cancel()
179-
180-
hashCmd := exec.CommandContext(ctx, git.GitExecutable, "hash-object", "-w", "--stdin")
181-
hashCmd.Dir = t.basePath
182-
hashCmd.Stdin = content
183-
stdOutBuffer := new(bytes.Buffer)
184-
stdErrBuffer := new(bytes.Buffer)
185-
hashCmd.Stdout = stdOutBuffer
186-
hashCmd.Stderr = stdErrBuffer
187-
desc := fmt.Sprintf("hashObject: (git hash-object)")
188-
if err := hashCmd.Start(); err != nil {
189-
return "", fmt.Errorf("git hash-object: %s", err)
190-
}
191-
192-
pid := process.GetManager().Add(desc, hashCmd)
193-
err := hashCmd.Wait()
194-
process.GetManager().Remove(pid)
133+
stdOut := new(bytes.Buffer)
134+
stdErr := new(bytes.Buffer)
195135

196-
if err != nil {
197-
err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOutBuffer, stdErrBuffer)
198-
return "", err
136+
if err := git.NewCommand("hash-object", "-w", "--stdin").RunInDirFullPipeline(t.basePath, stdOut, stdErr, content); err != nil {
137+
log.Error("Unable to hash-object to temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
138+
return "", fmt.Errorf("Unable to hash-object to temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
199139
}
200140

201-
return strings.TrimSpace(stdOutBuffer.String()), nil
141+
return strings.TrimSpace(stdOut.String()), nil
202142
}
203143

204144
// AddObjectToIndex adds the provided object hash to the index with the provided mode and path
205145
func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error {
206-
if _, stderr, err := process.GetManager().ExecDir(5*time.Minute,
207-
t.basePath,
208-
fmt.Sprintf("addObjectToIndex (git update-index): %s", t.basePath),
209-
git.GitExecutable, "update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath); err != nil {
146+
if _, err := git.NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath).RunInDir(t.basePath); err != nil {
147+
stderr := err.Error()
210148
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
211149
return models.ErrFilePathInvalid{
212150
Message: objectPath,
213151
Path: objectPath,
214152
}
215153
}
216-
return fmt.Errorf("git update-index: %s", stderr)
154+
log.Error("Unable to add object to index: %s %s %s in temporary repo %s(%s) Error: %v", mode, objectHash, objectPath, t.repo.FullName(), t.basePath, err)
155+
return fmt.Errorf("Unable to add object to index at %s in temporary repo %s Error: %v", objectPath, t.repo.FullName(), err)
217156
}
218157
return nil
219158
}
220159

221160
// WriteTree writes the current index as a tree to the object db and returns its hash
222161
func (t *TemporaryUploadRepository) WriteTree() (string, error) {
223-
treeHash, stderr, err := process.GetManager().ExecDir(5*time.Minute,
224-
t.basePath,
225-
fmt.Sprintf("WriteTree (git write-tree): %s", t.basePath),
226-
git.GitExecutable, "write-tree")
162+
stdout, err := git.NewCommand("write-tree").RunInDir(t.basePath)
227163
if err != nil {
228-
return "", fmt.Errorf("git write-tree: %s", stderr)
164+
log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err)
165+
return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %v", t.repo.FullName(), err)
229166
}
230-
return strings.TrimSpace(treeHash), nil
167+
return strings.TrimSpace(stdout), nil
231168
}
232169

233170
// GetLastCommit gets the last commit ID SHA of the repo
@@ -240,14 +177,12 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro
240177
if ref == "" {
241178
ref = "HEAD"
242179
}
243-
treeHash, stderr, err := process.GetManager().ExecDir(5*time.Minute,
244-
t.basePath,
245-
fmt.Sprintf("GetLastCommit (git rev-parse %s): %s", ref, t.basePath),
246-
git.GitExecutable, "rev-parse", ref)
180+
stdout, err := git.NewCommand("rev-parse", ref).RunInDir(t.basePath)
247181
if err != nil {
248-
return "", fmt.Errorf("git rev-parse %s: %s", ref, stderr)
182+
log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err)
183+
return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %v", ref, t.repo.FullName(), err)
249184
}
250-
return strings.TrimSpace(treeHash), nil
185+
return strings.TrimSpace(stdout), nil
251186
}
252187

253188
// CommitTree creates a commit from a given tree for the user with provided message
@@ -287,64 +222,64 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
287222
}
288223
}
289224

290-
commitHash, stderr, err := process.GetManager().ExecDirEnvStdIn(5*time.Minute,
291-
t.basePath,
292-
fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath),
293-
env,
294-
messageBytes,
295-
git.GitExecutable, args...)
296-
if err != nil {
297-
return "", fmt.Errorf("git commit-tree: %s", stderr)
225+
stdout := new(bytes.Buffer)
226+
stderr := new(bytes.Buffer)
227+
if err := git.NewCommand(args...).RunInDirTimeoutEnvFullPipeline(env, -1, t.basePath, stdout, stderr, messageBytes); err != nil {
228+
log.Error("Unable to commit-tree in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s",
229+
t.repo.FullName(), t.basePath, err, stdout, stderr)
230+
return "", fmt.Errorf("Unable to commit-tree in temporary repo: %s Error: %v\nStdout: %s\nStderr: %s",
231+
t.repo.FullName(), err, stdout, stderr)
298232
}
299-
return strings.TrimSpace(commitHash), nil
233+
return strings.TrimSpace(stdout.String()), nil
300234
}
301235

302236
// Push the provided commitHash to the repository branch by the provided user
303237
func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error {
304238
// Because calls hooks we need to pass in the environment
305239
env := models.PushingEnvironment(doer, t.repo)
306240

307-
if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute,
308-
t.basePath,
309-
fmt.Sprintf("actuallyPush (git push): %s", t.basePath),
310-
env,
311-
git.GitExecutable, "push", t.repo.RepoPath(), strings.TrimSpace(commitHash)+":refs/heads/"+strings.TrimSpace(branch)); err != nil {
312-
return fmt.Errorf("git push: %s", stderr)
241+
if _, err := git.NewCommand("push", t.repo.RepoPath(), strings.TrimSpace(commitHash)+":refs/heads/"+strings.TrimSpace(branch)).RunInDirWithEnv(t.basePath, env); err != nil {
242+
log.Error("Unable to push back to repo from temporary repo: %s (%s) Error: %v",
243+
t.repo.FullName(), t.basePath, err)
244+
return fmt.Errorf("Unable to push back to repo from temporary repo: %s (%s) Error: %v",
245+
t.repo.FullName(), t.basePath, err)
313246
}
314247
return nil
315248
}
316249

317250
// DiffIndex returns a Diff of the current index to the head
318-
func (t *TemporaryUploadRepository) DiffIndex() (diff *gitdiff.Diff, err error) {
319-
timeout := 5 * time.Minute
320-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
321-
defer cancel()
322-
323-
stdErr := new(bytes.Buffer)
324-
325-
cmd := exec.CommandContext(ctx, git.GitExecutable, "diff-index", "--cached", "-p", "HEAD")
326-
cmd.Dir = t.basePath
327-
cmd.Stderr = stdErr
328-
329-
stdout, err := cmd.StdoutPipe()
251+
func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
252+
stdoutReader, stdoutWriter, err := os.Pipe()
330253
if err != nil {
331-
return nil, fmt.Errorf("StdoutPipe: %v stderr %s", err, stdErr.String())
332-
}
333-
334-
if err = cmd.Start(); err != nil {
335-
return nil, fmt.Errorf("Start: %v stderr %s", err, stdErr.String())
336-
}
337-
338-
pid := process.GetManager().Add(fmt.Sprintf("diffIndex [repo_path: %s]", t.repo.RepoPath()), cmd)
339-
defer process.GetManager().Remove(pid)
340-
341-
diff, err = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout)
342-
if err != nil {
343-
return nil, fmt.Errorf("ParsePatch: %v", err)
344-
}
345-
346-
if err = cmd.Wait(); err != nil {
347-
return nil, fmt.Errorf("Wait: %v", err)
254+
log.Error("Unable to open stdout pipe: %v", err)
255+
return nil, fmt.Errorf("Unable to open stdout pipe: %v", err)
256+
}
257+
defer func() {
258+
_ = stdoutReader.Close()
259+
_ = stdoutWriter.Close()
260+
}()
261+
stderr := new(bytes.Buffer)
262+
var diff *gitdiff.Diff
263+
var finalErr error
264+
265+
if err := git.NewCommand("diff-index", "--cached", "-p", "HEAD").
266+
RunInDirTimeoutEnvFullPipelineFunc(nil, 30*time.Second, t.basePath, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) {
267+
_ = stdoutWriter.Close()
268+
diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader)
269+
if finalErr != nil {
270+
log.Error("ParsePatch: %v", finalErr)
271+
cancel()
272+
}
273+
_ = stdoutReader.Close()
274+
}); err != nil {
275+
if finalErr != nil {
276+
log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr)
277+
return nil, finalErr
278+
}
279+
log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s",
280+
t.repo.FullName(), t.basePath, err, stderr)
281+
return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %v\nStderr: %s",
282+
t.repo.FullName(), err, stderr)
348283
}
349284

350285
return diff, nil
@@ -358,12 +293,8 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
358293
return nil, err
359294
}
360295

361-
stdOut := new(bytes.Buffer)
362-
stdErr := new(bytes.Buffer)
363-
364-
timeout := 5 * time.Minute
365-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
366-
defer cancel()
296+
stdout := new(bytes.Buffer)
297+
stderr := new(bytes.Buffer)
367298

368299
cmdArgs := []string{"check-attr", "-z", attribute}
369300

@@ -379,26 +310,14 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
379310
}
380311
}
381312

382-
cmd := exec.CommandContext(ctx, git.GitExecutable, cmdArgs...)
383-
desc := fmt.Sprintf("checkAttr: (git check-attr) %s %v", attribute, cmdArgs)
384-
cmd.Dir = t.basePath
385-
cmd.Stdout = stdOut
386-
cmd.Stderr = stdErr
387-
388-
if err := cmd.Start(); err != nil {
389-
return nil, fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err())
390-
}
391-
392-
pid := process.GetManager().Add(desc, cmd)
393-
err = cmd.Wait()
394-
process.GetManager().Remove(pid)
395-
396-
if err != nil {
397-
err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr)
398-
return nil, err
313+
if err := git.NewCommand(cmdArgs...).RunInDirPipeline(t.basePath, stdout, stderr); err != nil {
314+
log.Error("Unable to check-attr in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s",
315+
t.repo.FullName(), t.basePath, err, stdout, stderr)
316+
return nil, fmt.Errorf("Unable to check-attr in temporary repo: %s Error: %v\nStdout: %s\nStderr: %s",
317+
t.repo.FullName(), err, stdout, stderr)
399318
}
400319

401-
fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
320+
fields := bytes.Split(stdout.Bytes(), []byte{'\000'})
402321

403322
if len(fields)%3 != 1 {
404323
return nil, fmt.Errorf("Wrong number of fields in return from check-attr")

0 commit comments

Comments
 (0)