From 4d6e68ed85ce0cf8f63bba9e7e01835f95a5e021 Mon Sep 17 00:00:00 2001 From: bircni Date: Sun, 18 Oct 2026 17:15:38 +0200 Subject: [PATCH] fix(doctor): check the account when the instance requires sign-in to view Signed-off-by: bircni --- internal/app/doctor.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/app/doctor.go b/internal/app/doctor.go index cc6beb8e..9c80d0e7 100644 --- a/internal/app/doctor.go +++ b/internal/app/doctor.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "time" @@ -15,6 +16,7 @@ import ( "gitea.dev/tea/internal/services" "gitea.dev/tea/internal/settings" "gitea.dev/tea/internal/terminal" + "gitea.dev/tea/internal/transport" "github.com/spf13/cobra" ) @@ -102,9 +104,13 @@ func (a *application) diagnose(ctx context.Context) doctorReport { add(a.gitCheck(ctx, "")) return report } - instance := a.instanceCheck(ctx, t) - add(instance) authenticated, accountErr := a.resolveTarget(ctx, t.Repo != "", true) + client := a.deps.Client(t.Host, "", t.host.Insecure) + if accountErr == nil { + client = authenticated.client + } + instance := a.instanceCheck(ctx, t, client) + add(instance) switch { case instance.Status == checkFailed: accountErr = errors.New("instance does not answer") @@ -138,17 +144,23 @@ func (a *application) diagnose(ctx context.Context) doctorReport { return report } -// instanceCheck asks the instance for its version. -func (a *application) instanceCheck(ctx context.Context, t target) doctorCheck { +// instanceCheck asks the instance for its version with client, the account's when it has one: +// instances with [service] REQUIRE_SIGNIN_VIEW refuse every anonymous API request. +func (a *application) instanceCheck(ctx context.Context, t target, client *transport.Client) doctorCheck { ctx, cancel := context.WithTimeout(ctx, doctorTimeout) defer cancel() start := time.Now() - version, err := services.ServerVersion(ctx, a.deps.Client(t.Host, "", t.host.Insecure)) + version, err := services.ServerVersion(ctx, client) where := t.Host + " (" + describeSource(t.HostSource) + ")" + latency := max(time.Since(start).Milliseconds(), 1) + if refused, ok := errors.AsType[*transport.APIError](err); ok && (refused.Status == http.StatusUnauthorized || refused.Status == http.StatusForbidden) { + // The instance answers; the account check reports why the credential is refused. + return doctorCheck{"Instance", checkPassed, fmt.Sprintf("%s · answers signed-in requests only · %dms", where, latency), ""} + } if err != nil { return doctorCheck{"Instance", checkFailed, where + ": " + Explain(ctx, a.deps, err).Error(), "check --host, the instance URL and your network"} } - return doctorCheck{"Instance", checkPassed, fmt.Sprintf("%s · Gitea %s · %dms", where, version, max(time.Since(start).Milliseconds(), 1)), ""} + return doctorCheck{"Instance", checkPassed, fmt.Sprintf("%s · Gitea %s · %dms", where, version, latency), ""} } // accountCheck reports whether the account authenticates, and the scopes of its token.