mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-17 02:13:25 +09:00
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:
@@ -124,20 +124,3 @@ func (err *ErrPushRejected) GenerateMessage() {
|
||||
}
|
||||
err.Message = strings.TrimSpace(messageBuilder.String())
|
||||
}
|
||||
|
||||
// ErrMoreThanOne represents an error if pull request fails when there are more than one sources (branch, tag) with the same name
|
||||
type ErrMoreThanOne struct {
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrMoreThanOne checks if an error is a ErrMoreThanOne
|
||||
func IsErrMoreThanOne(err error) bool {
|
||||
_, ok := err.(*ErrMoreThanOne)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err *ErrMoreThanOne) Error() string {
|
||||
return fmt.Sprintf("ErrMoreThanOne Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
|
||||
// IsRemoteNotExistError checks the prefix of the error message to see whether a remote does not exist.
|
||||
func IsRemoteNotExistError(err error) bool {
|
||||
return gitcmd.IsStderr(err, gitcmd.StderrNoSuchRemote1) || gitcmd.IsStderr(err, gitcmd.StderrNoSuchRemote2)
|
||||
return gitcmd.IsStderr(err, gitcmd.StderrNoSuchRemote1, gitcmd.StderrNoSuchRemote2)
|
||||
}
|
||||
|
||||
// ParseRemoteAddr checks if given remote address is valid,
|
||||
|
||||
@@ -47,11 +47,11 @@ func ManagedRemoteRemove(ctx context.Context, repo RepositoryFacade, remoteName
|
||||
|
||||
func ParseRemoteAddressURL(ctx context.Context, repo RepositoryFacade, remoteName string) (*giturl.GitURL, error) {
|
||||
addr, err := GetRemoteAddress(ctx, repo, remoteName)
|
||||
if (addr == "" && err == nil) || IsRemoteNotExistError(err) {
|
||||
return nil, util.NewNotExistErrorf("remote '%s' does not exist", remoteName)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addr == "" {
|
||||
return nil, util.NewNotExistErrorf("remote '%s' does not exist", remoteName)
|
||||
}
|
||||
return giturl.ParseGitURL(addr)
|
||||
}
|
||||
|
||||
@@ -279,8 +279,6 @@ func Push(ctx context.Context, localRepoPath string, opts PushOptions) error {
|
||||
err := &ErrPushRejected{StdOut: stdout, StdErr: stderr, Err: err}
|
||||
err.GenerateMessage()
|
||||
return err
|
||||
} else if strings.Contains(stderr, "matches more than one") {
|
||||
return &ErrMoreThanOne{StdOut: stdout, StdErr: stderr, Err: err}
|
||||
}
|
||||
return fmt.Errorf("push failed: %w - %s\n%s", err, stderr, stdout)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user