fix(ci): parse Postgres array labels so the runner-label risk check fires #551
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
gmackie/ForgeGraph!551
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/runner-label-parsing"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #549. The runner-label coverage check that PR shipped has never produced a single result in production.
The bug
It guarded with
Array.isArray(row.labels). Butci_runners.labelsis atext[]that does not come back as a JavaScript array on the deployed stack — it arrives as the raw Postgres array literal:So every row yielded an empty label list, coverage was empty, and
labelRiskscame back[].Verified against production minutes after #549 merged:
At that moment
ubuntu-latestwas carried by exactly one online runner (labnuc; vanuc offline since 01:05). That is precisely the condition the check exists to catch, and it reported clean. The same fleet also hasimageswith zero online runners and several other single-runner labels, none of which appeared.Why it slipped through
apps/web/src/app/api/fg/ci/runners/route.tsalready carried a hand-written parser for this exact column — quote handling, escapes,NULLfiltering — which is the only reason that endpoint renders labels at all. The knowledge was in the repo; the new code just did not use it.The fix
Extract that parser to
packages/api/src/lib/runner-labels.ts, delete the route's local copy, and have all three readers (buildQueue.queueStats,GET /api/fg/ci/queue,GET /api/fg/ci/runners) share one implementation so they cannot drift again.Also folds in
runnerLabelName. act_runner registers labels with an execution suffix — labnuc's on-disk config hasubuntu-latest:docker://localhost/forgegraph-act:node-24.14.0— and Forgejo matches on the part before the first colon, so the suffix has to be dropped or one pool counts as two separate ones.Tests
9 new tests covering the shapes production actually returns, not the shape the code assumed: the Postgres literal, a real JavaScript array, quoted elements, escaped quotes,
NULLmembers, empty/malformed input, and mixed-type arrays. Plus the suffix stripping.Existing
ci/runnersroute tests still pass after the refactor (2/2).packages/apiandapps/webboth typecheck clean.Note
This is the second time on this install that a parser written against an assumed shape silently no-opped against the real one. Sharing the single implementation is the point of the change, not just fixing the guard.
🤖 Generated with Claude Code