forked from heavy-duty/rig
4 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| ab29ee78fb |
fix: the --version read cannot die ahead of its own refusal
Self-review of the whole head after the panel converged. One real defect,
reproduced verbatim before it was touched.
runner_version_of is a pipeline, and every forgejo-runner-* command runs under
set -euo pipefail. When the binary exits non-zero the pipeline fails, and
`PRESENT_VER="$(runner_version_of "$BIN")"` is an ASSIGNMENT — so install died
at that line, exit 1, printing nothing at all. The lib documented the opposite
("empty when it cannot answer"), and the refusal written for exactly this case
twenty lines later — "the download landed but cannot run" — was unreachable
code that could never have fired.
The shape is not hypothetical. `[ -x ]` yes, runnable no is what a truncated or
wrong-arch download leaves behind, and a ci-box's template preinstalls the
binary at mint — so the silent death sat on the one path this command family
exists to serve.
json_field, two files away, already carries `|| true` with this reasoning
written above it: "callers run under set -e with pipefail, where a grep that
matches nothing would otherwise kill the script with no message". The same
guard, for the same reason, at both sites: the lib reader and install's own
read of --version.
The test could not have caught it. vparse drove the lib through `bash -c` with
NO shell options, and its "bad" stub exits 0 — it proved garbage-in-empty-out
in a permissive shell, which is not where the function is used. vparse now runs
under set -euo pipefail, and a stub that exits non-zero is a third row.
Mutation-checked: that row exits 1 against the unpatched lib and 0 against the
fixed one. Install's site is grep-pinned, since reaching it for real needs root
and a downloaded binary.
748 passed (was 746), release 31, drill 43, shellcheck clean.
|
|||
| 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> |