chadtmiller.com/knotless
git that lives in your Atmosphere account
A workflow waits for the checks it runs after holding no job
MergedThis adds an after key to workflows. A run with after waits, holding no job, for the named checks at its commit, and only then asks for a turn. It is filed failure or cancelled without running if one of them did not pass. The wake-up is sound: mark_unchanged comes before the reads, so an end that lands mid-read is not missed. Docs, the change file and tests are in place. I found nothing that must change. I did not run the tests or read the check results, and I read only the diff, not the code around it.
Two small things:
- The
///f's output, orNoneifcancelledfirst.line that documenteduntil_cancellednow sits aboveenum Waited, so it reads as Waited's doc.until_cancelledlost its doc. Move the line back. - Nothing rejects a cycle, such as
aafterbandbaftera. Both would stayqueuedwith no job until a stop, and a pull waiting on them would never merge. A parse-time check on direct cycles within one file is impossible, since workflows are parsed one file at a time. A note in the docs, or a cap on how long a run waits, would be enough.
crates/knotless-run/src/lib.rsline 4111
| |
/// `f`'s output, or `None` if `cancelled` first. | |
/// How a run's wait before its steps ended: holding its job; not to run, | |
/// as [`After::Not`] says; or cancelled. |
This line is the doc of until_cancelled, which now sits below Waited. The doc comment now attaches to Waited and until_cancelled has none. Move it back.
9 passed, 1 skipped
What changes
A workflow can name others in
after, asafter: [check, test, web]. The runner files itqueuedwith the rest, then waits for those checks at its commit, by itself, before it asks for a job at all. Once they have all passed or were skipped, it takes its turn as any run does. If one fails or errs it is filedfailurewithout running, and if one is cancelled,cancelled, its log naming which. A name with no check there is not waited for, and the log says so. It reads the checks again as each of its runs ends, and every minute besides. A stop drops a run still waiting, as any run not yet started.Why
On main at 1b7d479 the deploy and publish-lexicons each waited for the commit's checks in a step, so each held one of prod's two jobs (
--jobs 2) for 25 minutes and more.testwas queued at 16:23 and started at 17:08, just after publish-lexicons gave up its wait;webstarted one second after the deploy gave up. Both then failed, "test has not passed", and nothing deployed. A wait in a step always holds a job; only the runner can wait without one.This pull is the runner alone. The runner deploying it reads workflow files with its own, older parser, which refuses keys it does not know, so
deploy.ymlandpublish-lexicons.ymlmove toafterin a pull of their own once this is deployed: their wait steps go, and the deploy builds once the checks pass rather than while they run.How it was checked
a_workflow_runs_after_others_holding_no_job(crates/knotless-run/tests/run.rs), with one job: a deploy and a notify filed beforetestwait without it, sotestruns; both run once it passes, the deploy's log saying how long it waited and thatnopehas no check; both are filedfailurewhentestfails; and at a newer commit, the deploy, kept by its turn on the ref, endscancelledas itstestwas, and the notify is cancelled for the newer, as ever.workflow::tests::aftercovers the key's parsing. The rest is left to CI.Shortcomings
It waits only for its own runner's checks: one run by another runner counts as absent. A deploy that moves to
afterno longer builds its image while the checks run, so it goes up a few minutes later, but neither does its build take a job from them.🤖 Generated with Claude Code