fix: classify git failures on stderr, restrict migration failure detail (#39010)

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-09-15 10:59:34 +02:00
committed by GitHub
co-authored by wxiaoguang
parent 7b036e96c2
commit 812191c0f9
30 changed files with 136 additions and 137 deletions
+1 -1
View File
@@ -539,7 +539,7 @@ func (c *Command) WaitWithStderr() RunStdError {
// if no exec error but only stderr output, the stderr output is still saved in "c.cmdManagedStderr" and can be read later
return nil
}
return &runStdError{err: errWait, stderr: util.UnsafeBytesToString(c.cmdManagedStderr.Bytes())}
return NewRunStdError(errWait, util.UnsafeBytesToString(c.cmdManagedStderr.Bytes()))
}
func (c *Command) RunWithStderr(ctx context.Context) RunStdError {
+12 -3
View File
@@ -44,6 +44,10 @@ func (r *runStdError) Stderr() string {
return r.stderr
}
func NewRunStdError(err error, stderr string) RunStdError {
return &runStdError{err: err, stderr: util.NormalizeStringEOL(stderr)}
}
func ErrorAsStderr(err error) (string, bool) {
if runErr, ok := errors.AsType[RunStdError](err); ok {
return runErr.Stderr(), true
@@ -93,6 +97,9 @@ const (
StderrNoSuchRemote1 StderrPrefix = "fatal: no such remote" // git < 2.30, exit status 128
StderrNoSuchRemote2 StderrPrefix = "error: no such remote" // git >= 2.30. exit status 2
StderrAuthenticationFailed StderrPrefix = "fatal: Authentication failed for"
StderrCouldNotReadUsername StderrPrefix = "fatal: could not read Username"
StderrUnknownRevisionOrPath StderrRegexp = "^fatal: .*: unknown revision or path not in the working tree"
StderrNoMergeBase StderrRegexp = "^fatal: .*: no merge base"
StderrFileNoEnoughLines StderrRegexp = `^fatal: file .* has only \d+ lines?`
@@ -126,9 +133,11 @@ func IsStderr(err error, checks ...StderrCheck) bool {
return false
}
for _, checkIntf := range checks {
if matchStderrCheck(stderr, checkIntf) {
return true
for line := range strings.SplitSeq(stderr, "\n") { // git can emit multiple-line message in stderr
for _, checkIntf := range checks {
if matchStderrCheck(line, checkIntf) {
return true
}
}
}
return false
+2
View File
@@ -17,8 +17,10 @@ func TestIsStderr(t *testing.T) {
{StderrUnknownRevisionOrPath, "fatal: ambiguous argument 'origin': unknown revision or path not in the working tree...."},
{StderrNoMergeBase, "fatal: origin/main..HEAD: no merge base...."},
{StderrFileNoEnoughLines, "fatal: file foo/bar has only 1 line"},
{StderrAuthenticationFailed, "Cloning into 'repo'...\r\nremote: Invalid username or token.\r\nfatal: Authentication failed for 'https://host/repo.git/'\r\n"},
}
for _, tc := range cases {
assert.True(t, IsStderr(&runStdError{stderr: tc.stderr}, tc.check), "stderr: %s", tc.stderr)
}
assert.False(t, IsStderr(&runStdError{stderr: "remote: fatal: Authentication failed for 'https://host/repo.git/'\n"}, StderrAuthenticationFailed))
}