Skip to main content

v2.88.0

Binaries and the merged-pull-request list are on the GitHub releases page.

A bootstrap still spent two stretches waiting on things that did not need waiting for. Each landing waited about 25 s for a GitHub runner to plan a tree that was sitting in the build box. And the first phase waited for the whole task set-up (the epic, its milestones, its tasks) before it did anything, though nothing before the landing reads it. This release removes both waits.

A landing that checks its commit here: checks: local​

orun.pr/land@v1 takes checks:

- id: land
uses: orun.pr/land@v1
with:
task: "{{ .phase.hooks.task.outputs.key }}"
branchSlug: "{{ .phase.name }}"
checks: local
  • ci (the default) is what a landing has always done: open the pull request, wait for its CI run, merge.

  • local checks the commit in the box before it is pushed, then merges without waiting for CI. The check is three commands, run on the commit:

    • orun validate;
    • orun plan as the pull request plans it, from a pull_request event made for the commit, so the trigger binding the product's CI would match is the one planned;
    • orun plan as the merge's push to the base plans it. That is the convergence a bootstrap watches next, which the PR's CI never compiled.

    On a todo product the three take under half a second. A commit that fails them is never pushed, so it never becomes a pull request, and the error names the command and shows its output.

The PR's own CI still runs; nothing waits for it. If the base branch requires a status check, GitHub refuses the early merge. The landing then waits for the checks as ci would (up to checkTimeoutSeconds) and merges. The step reads "Checked here and merged #1 into main." The outputs gain checked (ci, local, local+ci when a required check was waited for, or none) and checkSummary, which says what was planned.

A merge asked for seconds after its pull request opened can be refused while GitHub is still working out that the PR is mergeable. Such a refusal (405 "not mergeable", 409 "base branch was modified") is now asked again over about ten seconds before it is reported. A refusal that names a rule, such as a required check or a required review, is not.

The task set-up runs beside the first phase: background: true​

A hooks.preInstantiate hook can be marked background: true, and so must every hook after it. The background is the end of the list, run in order, and it starts with the run instead of before the first phase:

hooks:
preInstantiate:
- id: epic
uses: orun.task/ensure@v1
background: true
with: { kind: epic, slug: todos-baselining, name: "Todos baselining" }
- id: t01
uses: orun.task/ensure@v1
background: true
with: { kind: task, epic: todos-baselining, milestone: 01-product, name: "Build the product" }
  • A hook whose action works on the task plane (orun.task/ensure@v1, orun.task/rollup@v1, orun.pr/land@v1) waits for the background to finish. It reads what the background made, and two finds-or-creates of one task at once would make it twice. Every other hook (a provider check, the placement, a run: hook, the repository, the secrets) runs while the background is still going.
  • A background hook that fails stops the run at the next hook, with its own error, and is reported as it fails.
  • The run never ends with the background still running.

background is only for uses: hooks in hooks.preInstantiate. A run: hook would share the product tree with the phase that is writing it.

To get the overlap, a phase looks up its task where it uses it: its task hook goes from pre to post, just before land. A task hook left in pre waits for the background at once, which is the old order.