diff --git a/internal/gh/fetch.go b/internal/gh/fetch.go index e0bc6e0..c3e1b93 100644 --- a/internal/gh/fetch.go +++ b/internal/gh/fetch.go @@ -360,6 +360,9 @@ func (c *Client) workflowRuns(ctx context.Context, full string, id int64, branch path := fmt.Sprintf("/repos/%s/actions/workflows/%d/runs", full, id) get := c.getJSON if fresh { + // GitHub can keep answering one exact query from an old cached result, for weeks, whatever + // the connection. A different query string skips that entry and reads current runs. + q.Set("page", "1") get = c.getJSONFresh } if err := get(ctx, full, path, q, &out); err != nil { diff --git a/internal/gh/gh_test.go b/internal/gh/gh_test.go index b7ef89c..4a44037 100644 --- a/internal/gh/gh_test.go +++ b/internal/gh/gh_test.go @@ -460,13 +460,13 @@ func TestAPIErrorMessageParsing(t *testing.T) { } } -// staleThenFresh serves an old failing run on the first request and the current runs afterwards, -// like a lagging GitHub replica followed by a healthy one. +// staleThenFresh models GitHub answering one exact query from an old cached result: the plain +// query returns an old failing run every time, and the same query with page=1 returns current runs. func staleThenFresh(f *fakeGitHub, path string) *atomic.Int64 { var calls atomic.Int64 f.routes[path] = func(w http.ResponseWriter, r *http.Request) { - n := calls.Add(1) - if n == 1 { + calls.Add(1) + if r.URL.Query().Get("page") == "" { _, _ = w.Write([]byte(`{"workflow_runs":[ {"id":1,"html_url":"old","event":"schedule","head_branch":"main","status":"completed","conclusion":"failure","created_at":"2026-09-11T03:02:00Z"}]}`)) return @@ -482,7 +482,7 @@ func staleThenFresh(f *fakeGitHub, path string) *atomic.Int64 { return &calls } -func TestStaleFailureIsConfirmedOnANewConnection(t *testing.T) { +func TestStaleFailureIsConfirmedWithADifferentQuery(t *testing.T) { f := newFake(t) c := f.client(t, 10) f.json("/repos/o/r/actions/workflows", `{"workflows":[{"id":7,"name":"Autoupdate","html_url":"w","state":"active"}]}`)