Commit graph

3 commits

Author SHA1 Message Date
9db8317543 fix(labels-scope): jq 1.6 cannot parse $label — the runner image ships 1.6
Found by running ceremony's own CI at 9357f09 on a real Forgejo runner
rather than reasoning about it.

`label` is a reserved word in jq's grammar (`label $out | … | break $out`),
so jq **1.6** rejects `$label` outright:

  jq: error: syntax error, unexpected label, expecting IDENT

jq 1.7 parses it, which is why this survived: GitHub's hosted ubuntu-latest
ships 1.7, and ghcr.io/catthehacker/ubuntu:act-22.04 — the image this
instance maps ubuntu-latest to — ships 1.6. So parse_labeler_config died on
a compile error before it read a byte of config, and EVERY scope derivation
on this forge failed. Renamed to $lbl in the jq program only; the bash
locals keep their names.

Also makes test/forge.test.sh hermetic. Its "github + gh passes" case
depended on gh being on the HOST's PATH, so it passed on a developer box and
failed in the runner image, which has no gh. The preflight cases now run
against stub binaries, and the missing-binary refusal gets its own arm on a
PATH carrying the shell and text tools but no clients — the condition under
test, rather than whatever the machine happens to have.

Verified in both environments: local (jq 1.7, gh present) and the runner
image (jq 1.6, no gh) — shellcheck 0, 22 files 0 failed in each.

Refs #188
2026-08-02 20:34:02 +00:00
adf3299192 test(forge): assert the distinguishing text, not a surviving substring
@codex-reviewer-andresmgsl (#4727) and @grok-reviewer-andresmgsl (#4734):
"...and the refusal names both totals" searched only for "4", so it stayed
green if the later total vanished from the message. A case named "names
BOTH" must fail when one goes. Now asserts "4 then 9".

Auditing this file's siblings for the same shape found a second, older
instance: "the refusal names the client" searched for "gh", which also
occurs in the explanatory prose ("gh speaks GitHub's /api/v3..."), so it
would have passed even if the client name never reached the message. Now
asserts "the 'gh' client cannot speak it".

Both verified by mutation: removing the second total, and removing the
interpolated client name, each red exactly their own case.

Refs #188
2026-08-02 19:16:48 +00:00
7d52b2cd4a feat(forge): refuse loudly when the client cannot speak the forge
The preflight half of #188, landed first so it stands alone: the forge is
decided once, before any sweep, and a client that cannot speak it exits
non-zero with a named reason.

Measured against forgejo.heavyduty.builders at 84bb1a4 — two of the three
actions reported SUCCESS having read nothing:

  labels-scope         exit 0  "no .github/labeler.yml" (the file is HTTP 200)
  labels-reconcile     exit 0  "reconciled."            (zero PRs enumerated)
  issueflow-reconcile  exit 1  "unexpected end of JSON input"

labels-reconcile's blind-sweep warning (#96) could not fire: it counts
unreadable PRs against a list `gh pr list` never produced, and a process
substitution's failure does not trip set -e, so total stayed 0. Installing
gh makes it worse, silencing the one loud failure.

Detection is measured, not inferred from docs: a real forgejo-runner v6.3.1
job (probe task 278) shows Forgejo populating the whole GITHUB_* namespace,
so GITHUB_ACTIONS proves nothing. GITHUB_API_URL's shape, GITEA_ACTIONS and
GITHUB_SERVER_URL do. The same probe shows the runner image carries neither
gh nor stoke, which is what makes the forgejo backend REST.

Tests declare CEREMONY_FORGE at the forge boundary rather than stubbing gh
and staying silent about the forge — the boundary move term 5 asks for.

Refs #188
2026-08-02 18:29:08 +00:00