Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ the major version is 0, a minor release may change configuration (each such chan

## [Unreleased]

### Fixed
- Jobs queued in a run that GitHub reports as `pending` (for example while it waits on a concurrency group) are served; the controller listed only `queued` and `in_progress` runs and left such jobs waiting forever.

### Changed
- A queued-job webhook starts a cycle after a 3 s debounce instead of a fixed 20 s; cycles still start at least 20 s apart (measured from the previous cycle's start).

Expand Down
4 changes: 2 additions & 2 deletions internal/ghapp/conditional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func TestASecondIdenticalReadIsConditionalAndReusesTheBody(t *testing.T) {
if err != nil || len(second) != 1 || second[0].ID != first[0].ID {
t.Fatalf("second %v %v, want the same job as the first", second, err)
}
if len(conditional) != 3 {
t.Fatalf("304s for %v, want the two run lists and the jobs list", conditional)
if len(conditional) != 4 {
t.Fatalf("304s for %v, want the three run lists (queued, pending, in_progress) and the jobs list", conditional)
}
}

Expand Down
4 changes: 3 additions & 1 deletion internal/ghapp/ghapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,9 @@ func (c *Client) QueuedJobs(ctx context.Context, s reconcile.Scope) ([]Job, erro
seen := map[int64]bool{}
var out []Job
for _, repo := range repos {
for _, status := range []string{"queued", "in_progress"} {
// A run waiting to start reports "pending" (behind a concurrency group, say) while its
// jobs are already queued, so pending runs are read too.
for _, status := range []string{"queued", "pending", "in_progress"} {
var runIDs []int64
var runs struct {
Runs []struct {
Expand Down
28 changes: 28 additions & 0 deletions internal/ghapp/ghapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,34 @@ func TestQueuedJobsCountOnlyQueuedJobsWithOurLabelInTheScopesRepos(t *testing.T)
}
}

// GitHub reports a run as "pending" (not "queued") while it waits to start, for example behind a
// concurrency group, and its jobs are queued all the same: they must still be served.
func TestQueuedJobsIncludeQueuedJobsOfPendingRuns(t *testing.T) {
f := &fake{h: func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/installation/repositories":
io.WriteString(w, `{"total_count":1,"repositories":[{"name":"a","owner":{"login":"acme"}}]}`)
case strings.HasSuffix(r.URL.Path, "/actions/runs") && r.URL.Query().Get("status") == "pending":
io.WriteString(w, `{"workflow_runs":[{"id":21}]}`)
case strings.HasSuffix(r.URL.Path, "/actions/runs"):
io.WriteString(w, `{"workflow_runs":[]}`)
case strings.HasSuffix(r.URL.Path, "/runs/21/jobs"):
io.WriteString(w, `{"jobs":[{"id":7,"status":"queued","labels":["self-hosted","railway"],"created_at":"2026-10-19T02:26:25Z"}]}`)
default:
t.Errorf("unexpected %s", r.URL.RequestURI())
w.WriteHeader(404)
}
}}
c := newGH(t, f)
jobs, err := c.QueuedJobs(context.Background(), org)
if err != nil {
t.Fatal(err)
}
if len(jobs) != 1 || jobs[0].ID != 7 {
t.Errorf("jobs %+v, want job 7 of the pending run", jobs)
}
}

func TestQueuedJobsOfARepoScopeLookOnlyAtThatRepo(t *testing.T) {
f := &fake{h: func(w http.ResponseWriter, r *http.Request) {
switch {
Expand Down
Loading