forgejo-runner install reports 'installed and running' for a crash-looping unit — Type=simple is active the instant it forks #141
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:bootstrap
scope:coolify
scope:db
scope:docs
scope:drill
scope:installer
scope:labels
scope:platform
scope:runner
scope:users
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/rig#141
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Context
rig forgejo-runner installreported success on a box where the runner could never start. From @andres's ci-box, 2026-08-01:Meanwhile:
The runner showed Offline / Never in Forgejo for ~13 minutes while rig had already said "installed and running".
The defect
commands/forgejo-runner-install.sh:498-503:The unit is
Type=simple. systemd marks such a unit active the instant it forks, before the process has done anything at all — andsystemctl enable --nowdoes not return until that has happened. So rig's first poll always seesactive, breaks immediately, and declares success. The daemon then exited 1 after ~25 ms and entered a 10-second restart loop.This is not a flaky race that occasionally misfires. It is near-deterministic: the check cannot fail for any crash-looping
Type=simpleunit, because there is always anactivemoment between fork and death.Why it is rig's own named bug class
commands/bootstrap.sh, on the box install, puts it exactly:is-activeimmediately afterenable --nowasserts what was requested. The claim "installed and running" is the strongest claim this command makes, and it is the one least supported.Spec
Assert the service is still up after it has had a chance to fail, and distinguish
activatingfromactive.activeand remain so across the window, rather than breaking on the first sighting.activatingas not-yet-healthy.systemctl is-activeexits non-zero for it, but the loop's earlybreakmeans the distinction is never reached.NRestartsclimbing is the single clearest signal of this shape.Tasks
activatingat the end of the window, naming the restart counttest/cli.sh: stubsystemctlso a unit that reportsactiveonce thenactivatingfails the assertchangelog.d/<n>.mdAcceptance criteria
installexit non-zero — driven with a stubbedsystemctl, not reasonedbash test/cli.shpasses; CI'sshellcheck -xstep is cleanTest plan
systemctlto reportactiveon the first call andactivatingthereafter — today's code passes, the fix must fail.breakand the new test goes red.activethroughout must still succeed.Dependencies
Surfaced by #141 (no Docker preflight), which is what made the daemon exit — but independent of it: this assert would have lied for any startup failure, not only a missing Docker.
@andres — filed from your ci-box report, not to be fixed yet, per your instruction.