diff --git a/go/cmd/gitter/gitter.go b/go/cmd/gitter/gitter.go index 16700e151eb..04bac9a75f0 100644 --- a/go/cmd/gitter/gitter.go +++ b/go/cmd/gitter/gitter.go @@ -346,8 +346,16 @@ func isAuthError(err error) bool { errString := err.Error() return strings.Contains(errString, "could not read Username") || - strings.Contains(errString, "Authentication failed") || - strings.Contains(errString, "The requested URL returned error: 403") + strings.Contains(errString, "Authentication failed") +} + +func isForbiddenError(err error) bool { + if err == nil { + return false + } + errString := err.Error() + + return strings.Contains(errString, "The requested URL returned error: 403") } func isNotFoundError(err error) bool { @@ -540,6 +548,11 @@ func FetchRepo(ctx context.Context, repoURL string, forceUpdate bool) error { // If still failing or recovery wasn't attempted, reclone the repo as final fallback if err != nil { + if isForbiddenError(err) { + logger.WarnContext(ctx, "Fetch failed with 403 Forbidden. Using local repo.", slog.Duration("sinceLastFetch", time.Since(accessTime)), slog.Any("err", err)) + return nil + } + logger.WarnContext(ctx, "Fetch and reset failed after recovery attempt, deleting repo and recloning", slog.Any("err", err)) if err := os.RemoveAll(repoPath); err != nil { return fmt.Errorf("failed to remove repo directory for reclone: %w", err) @@ -732,7 +745,7 @@ func gitHandler(w http.ResponseWriter, r *http.Request) { // Fetch repo first if err := doFetch(ctx, repoURL, forceUpdate); err != nil { - if isAuthError(err) { + if isAuthError(err) || isForbiddenError(err) { statusCode = http.StatusForbidden } else if isNotFoundError(err) { statusCode = http.StatusNotFound @@ -799,7 +812,7 @@ func cacheHandler(w http.ResponseWriter, r *http.Request) { logger.DebugContext(ctx, "Received request: /cache") if _, err := getFreshRepo(ctx, repoURL, body.GetForceUpdate()); err != nil { - if isAuthError(err) { + if isAuthError(err) || isForbiddenError(err) { statusCode = http.StatusForbidden } else if isNotFoundError(err) { statusCode = http.StatusNotFound @@ -870,7 +883,7 @@ func affectedCommitsHandler(w http.ResponseWriter, r *http.Request) { repo, err := getFreshRepo(ctx, repoURL, body.GetForceUpdate()) if err != nil { - if isAuthError(err) { + if isAuthError(err) || isForbiddenError(err) { statusCode = http.StatusForbidden } else if isNotFoundError(err) { statusCode = http.StatusNotFound @@ -1010,7 +1023,7 @@ func tagsHandler(w http.ResponseWriter, r *http.Request) { return nil, FetchRepo(ctx, repoURL, false) }); errFetch != nil { logger.ErrorContext(ctx, "Error fetching repo", slog.Any("error", errFetch)) - if isAuthError(errFetch) { + if isAuthError(errFetch) || isForbiddenError(errFetch) { invalidRepoCache.SetWithTTL(repoURL, http.StatusForbidden, 1, invalidRepoTTL) statusCode = http.StatusForbidden http.Error(w, fmt.Sprintf("Error fetching repository: %v", errFetch), statusCode) @@ -1048,7 +1061,7 @@ func tagsHandler(w http.ResponseWriter, r *http.Request) { return repo.GetRemoteTags(ctx) }) if errLsRemote != nil { - if isAuthError(errLsRemote) { + if isAuthError(errLsRemote) || isForbiddenError(errLsRemote) { invalidRepoCache.SetWithTTL(repoURL, http.StatusForbidden, 1, invalidRepoTTL) statusCode = http.StatusForbidden http.Error(w, fmt.Sprintf("Repository authentication failed: %v", errLsRemote), statusCode) diff --git a/go/cmd/gitter/gitter_test.go b/go/cmd/gitter/gitter_test.go index 745d422b53e..c4b1a8f8ca5 100644 --- a/go/cmd/gitter/gitter_test.go +++ b/go/cmd/gitter/gitter_test.go @@ -146,9 +146,9 @@ func TestIsAuthError(t *testing.T) { {errors.New("fatal: Authentication failed for 'https://github.com/google/this-repo-does-not-exist-12345.git/'"), true}, {errors.New("remote: Repository not found"), false}, {errors.New("fatal: could not read Username for 'https://github.com': terminal prompts disabled"), true}, - {errors.New("The requested URL returned error: 403"), true}, {errors.New("some other error"), false}, {errors.New("git clone failed: exit status 128"), false}, + {nil, false}, } for _, tt := range tests { @@ -158,6 +158,23 @@ func TestIsAuthError(t *testing.T) { } } +func TestIsForbiddenError(t *testing.T) { + tests := []struct { + err error + expected bool + }{ + {errors.New("fatal: unable to access 'https://github.com/composer/composer/': The requested URL returned error: 403"), true}, + {errors.New("some other error"), false}, + {nil, false}, + } + + for _, tt := range tests { + if result := isForbiddenError(tt.err); result != tt.expected { + t.Errorf("isForbiddenError(%v) = %v, expected %v", tt.err, result, tt.expected) + } + } +} + func TestIsNotFoundError(t *testing.T) { tests := []struct { err error