forked from heavy-duty/rig
4 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| fbdce5284e |
fix: the ci-box token guidance says what a Forgejo token actually is
Round finding from @codex-reviewer-andresmgsl, elevated to blocking by
@grok-reviewer-andresmgsl and @kimi-reviewer-andresmgsl. Unanimous, and right.
creds.md called the registration token "short-lived" and said it was "consumed
at registration". Both are GitHub's facts, copied across the forge boundary
with the rest of the sibling's shape. Forgejo's primary source, read rather
than inferred:
models/actions/runner_token.go — ActionRunnerToken has NO expiry field. Only
IsActive, Created, Updated. NewRunnerToken flips IsActive false on prior
tokens at the same scope and only there, so a token dies when somebody mints
its replacement, never on a clock.
routers/api/actions/runner/runner.go — Register reads the token, refuses it
when !IsActive ("please use the latest one"), and returns WITHOUT setting
IsActive = false. Registration does not spend it. One token registers as many
runners as it is shown to.
So it is long-lived and reusable — the precise opposite of the adjective, and
GitHub's really does expire in about an hour, which is why runner-install.sh is
correct to use it.
This is not a wording nit because of where the wording lives. creds.md is
spliced into the ci-box's own CONTEXT.md: it is the paragraph an agent INSIDE
the box reads about its own credentials. Telling that reader the token
self-expires is telling it a leaked one stops mattering on its own, while it is
still registering runners.
Pinned, not merely fixed, per codex's ask — the phrase arrived by copying from
the GitHub sibling, so the same copy can bring it back. Four rows: absence from
both files, and presence of the true claim, so the pin cannot be satisfied by
deleting the sentence instead of correcting it. The first draft of the CIBOX
pin was a phrase match and passed against the exact text it was written to
catch — the old wording wrapped across two comment lines. It is a plain absence
check now, and the file explains the ban without spelling the word.
Mutation-checked: all four go red against the old wording, green after.
|
|||
| cf5858bb60 |
fix: one checksum policy, labeler coverage, orphaned-unit removal
Net-new review findings from grok and kimi on !110. Their items 1-3 were codex's, already fixed in 1933b07; these are the ones only they raised. grok #4 — the two downloaders would drift. docs/templates/ci-box/install.sh and the download block in forgejo-runner-install.sh were near-copies, and grok named the exact consequence with the exact evidence: fail-open survived in BOTH while a grep for "checksum mismatch" passed against both, because the string it looked for sat right beside the branch it could not see. The whole policy — fetch, unreadable, mismatch — is now fetch_and_verify_sha256, byte-identical in both files and diffed by test/cli.sh. They cannot share a lib: the command sources commands/lib/, and the template is a registry definition that runs standalone inside a mint with rig's tree nowhere in reach, which is the same situation valid_version faces between bin/rig and install.sh. Mutation-checked by drifting one copy's message and confirming the diff goes red. kimi #2 — the labeler could not see this family. scope:runner matched commands/runner-*.sh only, so forgejo-runner-*.sh and the staged ci-box definition scored no scope at all. Globs extended and the label's description now says either forge rather than GitHub. kimi #4 — remove stranded a unit whose user was gone. The missing-user check exited 0 before the unit was ever looked at, so a deleted account with a leftover forgejo-runner.service reported "nothing to remove" while the absence-assert that never ran implied the opposite. The unit is now checked independently. Auditing that fix surfaced a hazard kimi did not mention: with the user gone RUNNER_DIR is "", and the later unguarded "$RUNNER_DIR/.rig-labels" would have expanded to "/.rig-labels" — an rm at the filesystem root, as root. Every RUNNER_DIR path is now gated, and a test pins that none is unguarded. kimi #1 — the README handed out a config that breaks rig's own gates. DEFAULT_ACTIONS_URL is a single fallback and rig's workflows need two origins; measured: code.forgejo.org serves actions/checkout (200) but not heavy-duty/ceremony (404), which lives on the Forgejo instance. With the value the README recommended, all eight ceremony references fail to resolve. The section now states the conflict with the counts, says which references would break, and explicitly does NOT pick a side — that is an infra decision, and rig's CI running on Forgejo is not something rig forgejo-runner depends on. Asked the maintainer for direction. 746/31/43 pass, shellcheck clean, labeler.yml parses. forgejo#109 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 1933b07fd4 |
fix: honour --version, scope .rig-labels, close the checksum gate
Three defects from review !110, all the same family — a stated contract the code did not keep. --version was swallowed on the path this command exists for. The download block skipped on mere presence, copying runner-install.sh's shape without its justification: actions/runner SELF-UPDATES, so freezing it would only make GitHub refuse its jobs. forgejo-runner does not self-update, so nothing else ever moves the version — and a ci-box's template preinstalls the binary at mint, which meant the documented deterministic-pin lever could never fire on a ci-box. It now converges toward the pin, downward included, because a pin is an instruction and not a floor; absent a pin an existing binary is left alone, since chasing latest would make a re-run an unrequested upgrade. The decision moved to runner_download_decision in the lib as a pure function: the first attempt at a test here grepped for a log string and survived the logic being disabled, which is exactly the weak test the review warned about. The binary is now renamed into place rather than written over — the converge path runs while the daemon is live, and in-place is ETXTBSY. .rig-labels outlived the registration it described. The write had escaped the registration branch, where runner-install.sh correctly keeps its copy, so a plain re-run stamped this invocation's labels over a registration made with different ones and status then reported confidently wrong labels while Forgejo still held the originals. Scoped again, and an EXPLICIT --labels on a re-run now warns that Forgejo owns labels from registration time rather than letting the request evaporate silently. The checksum gate failed open. A missing .sha256 warned and installed anyway, contradicting both the README and the template's own comment about unverified root downloads. The original reasoning — do not let an upstream layout change break installs — reasons about the wrong failure: a layout change breaks the BINARY url too, so "binary yes, checksum no" is the shape of an interfered fetch, which is precisely what the checksum exists to catch. Both paths refuse now, with no bypass flag; if upstream really moves its assets that is a rig PR editing the URL, not an operator improvising past a security gate. Tests: the checksum paths are now DRIVEN against a stub curl through the real template install.sh — matching, missing, mismatched and empty — instead of grepped, and all three fixes were mutation-checked by reverting each and confirming the suite goes red. 739/31/43 pass, shellcheck clean. forgejo#109 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 903d8371b3 |
feat: Forgejo-native CI — a ci-box tenant and a forgejo-runner family
rig's CI story was GitHub-shaped end to end. This makes it work against a self-hosted Forgejo, in three pieces. The registry fetch becomes forge-aware. templates_resolve hardcoded three github.com archive URLs; RIG_TEMPLATES_HOST now selects the grammar, because the forges genuinely differ — GitHub serves refs/tags, refs/heads and bare paths, Forgejo serves exactly one, and emitting the other two there would mean two guaranteed 404s per fetch and a failure message listing URLs that never could have worked. Measured against forgejo.heavyduty.builders, not inferred. The default stays GitHub, so every existing caller is unchanged. install.sh's snapshot reads the same variable through a byte-identical copy of the builder, diffed by the tests: a snapshot cached from a forge converge would never fetch from is worse than no snapshot, and the pin-in-the-name staleness guard cannot catch a wrong-ORIGIN snapshot, only an old one. ci-box is a tenant, not a machine role. The topology is a fleet machine hosting boxes, one of which runs CI — a '-box' guest by rig's own family rule. That also deletes the docker-in-docker layer the usual setup needs: bootstrap-tenant.sh already installs Docker and adds the tenant user to the group, and the isolation a privileged dind sidecar buys is already paid for by a box that is network-isolated, inbound-less and disposable. rig runner install refuses Docker for good reason — it converges a MACHINE, where the blast radius is the machine. Here it is a guest that gets thrown away. rig forgejo-runner is a new family beside rig runner, which is untouched. Forgejo registers against an INSTANCE and the token carries the scope, so there is no --repo to converge toward and nothing to compare; folding that into one command would make every guard bimodal to share a flag name while the contract underneath differs. assert_runner_instance asks the same trust-boundary question about the axis Forgejo actually has. There is no repoint and no --local, and both absences are explained where an operator arriving from the GitHub sibling will hit them. Forgejo's .runner holds the runner's own long-lived token, unlike GitHub's, so it is installed 0600 and the mode is re-asserted on every converge — a mode that drifted leaks the secret silently, since nothing fails and the runner keeps working. status reports it and never prints the token. Both downloads verify the published .sha256 before installing: this binary lands as root and is executed by a systemd unit. bootstrap --undo learns the guard for the same hazard on the other forge, and it matters more here — Forgejo has no deregistration endpoint, so the ghost it would strand has to be deleted by hand. Known prerequisite, documented rather than assumed: the fetch is unauthenticated by contract, and a Forgejo with REQUIRE_SIGNIN_VIEW=true answers 404 for repos it reports as public. Hosting a registry there needs FORGEJO__service__REQUIRE_SIGNIN_VIEW=false. The refusal names that case, because it is indistinguishable from a wrong ref. forgejo#109 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |