| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package git
- /*
- errors.go
- Error vocabulary plus the classifier that decides whether a failed transport
- operation should make the front-end pop the credential dialog.
- go-git surfaces authentication problems through several unrelated error
- values depending on the transport and the server, so matching on the message
- is unavoidable. IsAuthError is kept as a pure, table-tested function.
- */
- import (
- "errors"
- "strings"
- "github.com/go-git/go-git/v5/plumbing/transport"
- )
- var (
- //ErrNotARepo is returned when a path is not inside a git working tree.
- ErrNotARepo = errors.New("not a git repository")
- //ErrNoRemote is returned when an operation needs a remote but none is set.
- ErrNoRemote = errors.New("repository has no remote configured")
- //ErrUnbornBranch is returned when HEAD does not resolve yet (no commits).
- ErrUnbornBranch = errors.New("branch has no commits yet")
- //ErrAuthRequired is returned when the remote rejected or demanded
- //credentials. The AGI layer turns this into authRequired = true.
- ErrAuthRequired = errors.New("authentication required")
- //ErrUnmergedBranch is returned when deleting a local branch that still holds
- //commits unreachable from HEAD. The AGI layer turns this into
- //unmerged = true so the caller can confirm and retry with force.
- ErrUnmergedBranch = errors.New("branch has commits that are not merged into the current branch")
- )
- // authErrorFragments are lower-cased substrings that identify an authentication
- // or authorisation failure across the git hosting services ArozOS users are
- // likely to hit (GitHub, GitLab, Gitea, Bitbucket, plain http backends).
- var authErrorFragments = []string{
- "authentication required",
- "authorization failed",
- "authentication failed",
- "invalid auth method",
- "unauthorized",
- "403 forbidden",
- "401 unauthorized",
- "bad credentials",
- "permission denied",
- "could not read username",
- "terminal prompts disabled",
- "support for password authentication was removed",
- }
- // IsAuthError reports whether err represents a credential problem rather than a
- // genuine transport or repository failure.
- func IsAuthError(err error) bool {
- if err == nil {
- return false
- }
- if errors.Is(err, ErrAuthRequired) ||
- errors.Is(err, transport.ErrAuthenticationRequired) ||
- errors.Is(err, transport.ErrAuthorizationFailed) {
- return true
- }
- message := strings.ToLower(err.Error())
- for _, fragment := range authErrorFragments {
- if strings.Contains(message, fragment) {
- return true
- }
- }
- return false
- }
- // classifyError normalises a go-git transport error so callers upstream can
- // simply check errors.Is(err, ErrAuthRequired). The original message is kept as
- // the wrapped text because it usually names the failing host.
- func classifyError(err error) error {
- if err == nil {
- return nil
- }
- if IsAuthError(err) {
- return &authError{inner: err}
- }
- return err
- }
- // authError wraps a transport failure while reporting as ErrAuthRequired to
- // errors.Is, so the AGI layer can set authRequired without losing the message.
- type authError struct {
- inner error
- }
- func (e *authError) Error() string { return e.inner.Error() }
- func (e *authError) Unwrap() error { return e.inner }
- func (e *authError) Is(target error) bool { return target == ErrAuthRequired }
|