Skip to content

Commit 12a339a

Browse files
committed
Reformat git push errors from libgit2, go-git
libgit2 and go-git both have flaws in the way they treat errors from the remote. go-git takes only the first line, meaning that it gets a blank error message from GitLab which like to respond with a banner. libgit2 returns the whole response, including blank lines and fences ("=========..."). This commit corrects for both these flaws, by supplying a message if go-git has taken a blank line, and stripping out blank lines and fences from libgit2's error. This is unavoidably a brittle approach, so I have limited it to just the situation that was reported as a problem: pushing to the upstream git repo. Signed-off-by: Michael Bridgen <[email protected]>
1 parent 3f82d6c commit 12a339a

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

controllers/imageupdateautomation_controller.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,22 +409,74 @@ func push(ctx context.Context, path string, repo *gogit.Repository, branch strin
409409
}
410410

411411
func pushGoGit(ctx context.Context, repo *gogit.Repository, access repoAccess) error {
412-
return repo.PushContext(ctx, &gogit.PushOptions{
412+
err := repo.PushContext(ctx, &gogit.PushOptions{
413413
Auth: access.auth.AuthMethod,
414414
})
415+
return gogitPushError(err)
416+
}
417+
418+
func gogitPushError(err error) error {
419+
if err == nil {
420+
return nil
421+
}
422+
switch strings.TrimSpace(err.Error()) {
423+
case "unknown error: remote:":
424+
// this unhelpful error arises because go-git takes the first
425+
// line of the output on stderr, and for some git providers
426+
// (GitLab, at least) the output has a blank line at the
427+
// start. The rest of stderr is thrown away, so we can't get
428+
// the actual error; but at least we know what was being
429+
// attempted, and the likely cause.
430+
return fmt.Errorf("push rejected; check git secret has write access")
431+
default:
432+
return err
433+
}
415434
}
416435

417436
func pushLibgit2(repo *libgit2.Repository, access repoAccess, branch string) error {
418437
origin, err := repo.Remotes.Lookup(originRemote)
419438
if err != nil {
420439
return err
421440
}
422-
return origin.Push([]string{fmt.Sprintf("refs/heads/%s:refs/heads/%s", branch, branch)}, &libgit2.PushOptions{
441+
err = origin.Push([]string{fmt.Sprintf("refs/heads/%s:refs/heads/%s", branch, branch)}, &libgit2.PushOptions{
423442
RemoteCallbacks: libgit2.RemoteCallbacks{
424443
CertificateCheckCallback: access.auth.CertCallback,
425444
CredentialsCallback: access.auth.CredCallback,
426445
},
427446
})
447+
return libgit2PushError(err)
448+
}
449+
450+
func libgit2PushError(err error) error {
451+
if err == nil {
452+
return err
453+
}
454+
// libgit2 returns the whole output from stderr, and we only need
455+
// the message. GitLab likes to return a banner, so as an
456+
// heuristic, strip any lines that are just "remote:" and spaces
457+
// or fencing.
458+
msg := err.Error()
459+
lines := strings.Split(msg, "\n")
460+
if len(lines) == 1 {
461+
return err
462+
}
463+
var b strings.Builder
464+
// the following removes the prefix "remote:" from each line; to
465+
// retain a bit of fidelity to the original error, start with it.
466+
b.WriteString("remote: ")
467+
468+
var appending bool
469+
for _, line := range lines {
470+
m := strings.TrimPrefix(line, "remote:")
471+
if m = strings.Trim(m, " \t="); m != "" {
472+
if appending {
473+
b.WriteString(" ")
474+
}
475+
b.WriteString(m)
476+
appending = true
477+
}
478+
}
479+
return errors.New(b.String())
428480
}
429481

430482
// --- events, metrics

0 commit comments

Comments
 (0)