fix(pr): report checks like gh, exit 8 while they are pending

tea pr checks printed relative links, "Error: pull request checks have
not all succeeded" with exit 1 for checks that were only pending, and
nothing at all without checks. It now heads the table with a colored
verdict and "0 failing, 1 successful, and 2 pending checks", prints a
status icon and absolute links, and exits 8 while checks are pending
and 1 when one failed, without an Error line (D21). clierr.Quiet
carries such an exit code past main without a message. Without checks
it says "no checks reported for pull request alice/x#8".

--watch redraws the summary in place on a terminal, and an expired
--timeout reads "timed out after 3s waiting for 2 pending checks". The
pull request argument is optional, and --web opens the pull request.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 23:54:47 +02:00
1 parent 7930f80865
commit 2c5e005bcf
6 files changed
+239 -36

No files matched your search

+69 -1
View File
@@ -6,6 +6,7 @@ package app
import (
"context"
"net/http"
"strings"
"testing"
"gitea.dev/tea/internal/clierr"
@@ -38,7 +39,8 @@ func TestChecksUseLatestContextAcrossPagesAndReturnFailureWithCleanJSON(t *testi
out.Reset()
state = "pending"
err := Execute(context.Background(), deps, args)
require.ErrorContains(t, err, "not all succeeded")
require.Equal(t, clierr.Pending, clierr.ExitCode(err))
require.True(t, clierr.IsQuiet(err))
require.JSONEq(t, `[{"id":2,"name":"build","state":"pending"}]`, out.String())
out.Reset()
state = "skipped"
@@ -84,3 +86,69 @@ func TestChecksWatchWaitsForRegistrationAndEmitsOneJSONResult(t *testing.T) {
require.ErrorIs(t, err, context.Canceled)
require.Equal(t, clierr.Canceled, clierr.ExitCode(err))
}
func TestChecksSummaryAndExitCodes(t *testing.T) {
for _, tc := range []struct {
name, statuses, want string
code int
}{
{
name: "pending",
statuses: `[{"id":1,"context":"ci / test (pull_request)","status":"pending","description":"Waiting to run","target_url":"/alice/x/actions/runs/18/jobs/18"}]`,
want: "Some checks are still pending\n0 failing, 0 successful, and 1 pending checks\n\n" +
" NAME STATUS DESCRIPTION LINK\n" +
"* ci / test (pull_request) pending Waiting to run SERVER/alice/x/actions/runs/18/jobs/18\n",
code: clierr.Pending,
},
{
name: "failing",
statuses: `[{"id":1,"context":"lint","status":"failure"},{"id":2,"context":"test","status":"success"}]`,
want: "Some checks were not successful\n1 failing, 1 successful, and 0 pending checks\n\n NAME STATUS DESCRIPTION LINK\nX lint failure\n✓ test success\n",
code: clierr.Failure,
},
{name: "none", statuses: `[]`},
} {
t.Run(tc.name, func(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/api/v1/repos/alice/x/pulls/8":
_, _ = w.Write([]byte(`{"number":8,"head":{"sha":"abc"}}`))
case r.URL.Query().Get("page") == "1":
_, _ = w.Write([]byte(tc.statuses))
default:
_, _ = w.Write([]byte(`[]`))
}
}))
defer server.Close()
deps, out, diagnostics := serverDeps(t, server.URL)
plainTTY(&deps, server.URL)
err := Execute(context.Background(), deps, []string{"pr", "checks", "8", "-R", "alice/x"})
require.Equal(t, tc.code, clierr.ExitCode(err))
if tc.code != 0 {
require.True(t, clierr.IsQuiet(err), "the summary already reports the outcome")
}
require.Equal(t, strings.ReplaceAll(tc.want, "SERVER", server.URL), out.String())
if tc.want == "" {
require.Equal(t, "no checks reported for pull request alice/x#8\n", diagnostics.String())
}
})
}
}
func TestChecksWatchTimeoutCountsPendingChecks(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/api/v1/repos/alice/x/pulls/8":
_, _ = w.Write([]byte(`{"number":8,"head":{"sha":"abc"}}`))
case r.URL.Query().Get("page") == "1":
_, _ = w.Write([]byte(`[{"id":1,"context":"a","status":"pending"},{"id":2,"context":"b","status":"pending"}]`))
default:
_, _ = w.Write([]byte(`[]`))
}
}))
defer server.Close()
deps, _, _ := serverDeps(t, server.URL)
err := Execute(context.Background(), deps, []string{"pr", "checks", "8", "-R", "alice/x", "--watch", "--interval=1ms", "--timeout=30ms"})
require.EqualError(t, err, "timed out after 30ms waiting for 2 pending checks")
require.ErrorIs(t, err, context.DeadlineExceeded)
}