Follow-up builds (#23)
A build definition may declare afterSuccessOf, running whenever the named build of the same branch turns green — the deployment path chosen over a deployCommand or a separate deploy section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #23
This commit was merged in pull request #23.
This commit is contained in:
co-authored by
Claude Fable 5.1
mhoennig
parent
0f9f119687
commit
4db294e1bc
+48
-3
@@ -101,7 +101,9 @@ single branch may decide it:
|
||||
- the container sandbox policy: `docker.enabled`/`docker.network` and
|
||||
`werkdock.enabled`/`werkdock.rootfs`/`werkdock.binary` — host-pinned as
|
||||
long as only the host's configuration sets them, master-pinned once the committed
|
||||
configuration does.
|
||||
configuration does;
|
||||
- the trigger of a [follow-up build](#follow-up-builds): `trigger.afterSuccessOf` in every definition, and the whole `trigger` block of a definition the host defines as a follow-up.
|
||||
A branch may say what its deployment does, never that — or for which branches — it happens.
|
||||
|
||||
The distinction is documentary.
|
||||
Werkator applies one rule: every pinned key is stripped from the branch layer, and the
|
||||
@@ -110,7 +112,7 @@ The names say where a key is meant to live, not how it is enforced.
|
||||
|
||||
This keeps a branch from reaching credentials, reporting statuses to another repository,
|
||||
raising the global concurrency, disabling its own build container, changing its network
|
||||
mode, or bypassing its own pull-request gate. Everything else is the branch's to decide —
|
||||
mode, bypassing its own pull-request gate, or deploying itself. Everything else is the branch's to decide —
|
||||
it can already run any command through `buildCommand`.
|
||||
The pinned settings are stripped wherever they appear, in a build definition as well as in
|
||||
a legacy `branches` entry. The deprecated `branches` section itself is read from the repo
|
||||
@@ -252,6 +254,7 @@ builds:
|
||||
# branches: ["*", "!master"] # names or globs; a "!" pattern excludes; default: all
|
||||
# atTimes: ["01:00"] # daily UTC times HH:MM ("??:05" = every hour at :05)
|
||||
# activeWithin: 24h # only branches with commits in the last 24h
|
||||
# afterSuccessOf: test # run after every green run of that build, at its commit (pinned)
|
||||
# run before each build
|
||||
cleanCommand: rm -rf build
|
||||
# shell command for each build
|
||||
@@ -397,9 +400,50 @@ Writing any of its keys outside the block is refused with a message naming the d
|
||||
Triggers: `onPush: true` builds every new commit of the selected branches; `atTimes: ["HH:MM", …]` rebuilds their heads once per day and slot (UTC).
|
||||
A slot may also be written as `??:MM` — that minute of every hour, expanded to its 24 slots, so the build runs hourly.
|
||||
Only the latest due slot of a day triggers, so slots missed while the server was down are skipped instead of piling up, and a slot whose pool is still building is retried on the next poll cycle until it succeeds.
|
||||
A definition may have both; one with neither never triggers automatically — which is how `builds.default` is written when it is meant as a settings base only.
|
||||
A definition may combine them; one with none of the three triggers (`onPush`, `atTimes`, `afterSuccessOf`) never runs automatically — which is how `builds.default` is written when it is meant as a settings base only.
|
||||
Werkator logs a warning once when no definition has a trigger at all, because such an instance never builds anything on its own.
|
||||
|
||||
#### Follow-up builds
|
||||
|
||||
`afterSuccessOf: <name>` makes a definition a *follow-up* of another one, its *predecessor*: it runs on the predecessor's branch at the predecessor's commit whenever a run of the predecessor ends green.
|
||||
This is how a deployment is configured: a deployment is a build that follows a green build, and it gets everything a build has — its own row in History with log and duration, its own Gitea check under its own `statusContext`, restart, cancel, artifacts, and the per-branch serialization.
|
||||
|
||||
Every green run counts, whatever started it — the push watcher, an `atTimes` slot, a UI restart, `werkator retry`, or the startup recovery — and a repeated green run of the same commit triggers the follow-up again.
|
||||
The follow-up builds the commit that was tested, not the branch's current origin head.
|
||||
A failed, cancelled, or interrupted run triggers nothing.
|
||||
The follow-up runs in the branch's worktree, after its predecessor, in the branch's sandbox or container — so a deployment tool like the Docker CLI is provided the way a compiler is.
|
||||
It inherits `builds.default` like every definition, so a deployment that wants the predecessor's output has to set `cleanCommand: ""` itself; a deployment command should rather be self-contained and rebuild what it ships, because a restart of the follow-up alone runs without its predecessor.
|
||||
The pull-request gate is not consulted for a follow-up: the predecessor passed it for the same commit, and the follow-up's `branches` selector is its own gate.
|
||||
|
||||
The trigger of a follow-up is pinned: a branch's committed config can neither add `afterSuccessOf` to a definition nor change the `trigger` block of a definition the host defines as a follow-up, so a branch cannot deploy itself.
|
||||
What the deployment *does* comes with the repository, like every build command; where and when it happens, and with which credentials, is the host's.
|
||||
The host's part lives in `.git/werkator/.werkator.yml`; credentials reach the sandbox like any build setting, through `werkdock.env`/`docker.env`, and a file such as an SSH key through the werkdock sandbox's persistent toolchain home, `.git/werkator/buildenv/home/` on the host, which the sandbox mounts as `/root`.
|
||||
|
||||
```yaml
|
||||
# .git/werkator/.werkator.yml — the host's part: when, for which branches, with what
|
||||
builds:
|
||||
deploy:
|
||||
trigger:
|
||||
afterSuccessOf: frontend
|
||||
branches: ["main"]
|
||||
statusContext: werkator/deploy
|
||||
werkdock:
|
||||
env:
|
||||
DEPLOY_TARGET: user@host:~/doms/example.org/htdocs-ssl
|
||||
```
|
||||
|
||||
```yaml
|
||||
# .werkator.yml — the repository's part: what
|
||||
builds:
|
||||
deploy:
|
||||
cleanCommand: ""
|
||||
buildCommand: scripts/deploy-prod.sh -y "$DEPLOY_TARGET"
|
||||
```
|
||||
|
||||
A follow-up whose predecessor no definition has, and a cycle of follow-ups, refuse the start with a message naming the definition — a deployment that silently never runs is the failure the flat-key refusal exists to prevent.
|
||||
A branch whose committed config drops or renames the predecessor only loses its follow-up, with a warning naming the branch.
|
||||
A one-shot `werkator build` runs no follow-ups — its process ends with its build — and says which ones the server would have run.
|
||||
|
||||
Selector: `trigger.branches` lists branch names or glob patterns (`*` matches any characters, also across `/`); empty selects all origin branches.
|
||||
A pattern prefixed with `!` excludes instead, and an exclusion always wins regardless of order — `["*", "!master"]` is every branch but master.
|
||||
That is how a branch gets a build of its own without being built by the default one as well.
|
||||
@@ -409,6 +453,7 @@ Both parts combine as an intersection.
|
||||
Settings: `buildCommand`, `cleanCommand`, `artifactDirs`, `stdoutLog`/`stderrLog`, `requirePullRequest`, `statusContext`, and `docker` and `werkdock` with all their keys.
|
||||
A definition carries the complete description of its build; unset keys fall back to `builds.default` and then to Werkator's own defaults.
|
||||
`requirePullRequest`, `statusContext`, `docker.enabled`, `docker.network`, `werkdock.enabled`, `werkdock.rootfs`, and `werkdock.binary` are pinned (master-pinned, see [the branch layer](#the-branch-layer-a-branch-describes-its-own-ci)): they are read from the repo install/project config even when a branch sets them in its own committed config.
|
||||
So is the trigger of a [follow-up build](#follow-up-builds).
|
||||
Inheritance from `builds.default` covers the settings only — the `trigger` block says when and where *this* build runs and is never inherited.
|
||||
Definitions are part of the branch layer: a branch may add its own and override those from the project config, for its own builds only.
|
||||
Because the inheritance is applied after all layers are merged, a build a branch invents still inherits the host's `builds.default` — its sandbox policy included, which is what keeps the pinning effective for a build the host has never heard of.
|
||||
|
||||
Reference in New Issue
Block a user