colophon apply¶
Description¶
Carry what a commit asserted to the forge object it names
Read the commits a push added, take the Updates: trailers they carry, and
apply each to the issue, merge request or wiki page it names. This is the one
verb that runs when a merge lands rather than when a release is cut, so a
record outside the repository can be told what became of the work.
Nothing is inferred. A commit says what happened to a record or the record is
left alone, for the reason a Release-As: trailer exists: a tool cannot know
which merge finished a piece of work, and a record it marks done too early is
worse than one left stale, because a wrong record is trusted.
Usage¶
Run colophon apply --help for the authoritative, always-current flag set.
Flags¶
| Flag | Description | Default |
|---|---|---|
--from |
Commit the push started from. Empty, or the all-zero SHA a forge sends for a branch's first push, means the tip alone. | empty |
--to |
Commit the push ended at | HEAD |
--remote |
Git remote to read | origin |
--forge |
Forge the remote is on when its host does not say: gitlab, github or gitea. Same as forge in .colophon.yaml; both must agree when given. |
empty |
-C, --repo |
Path to the git repository | . |
The span¶
--from and --to are the commits a push added, which the CI environment
knows: on GitLab they are CI_COMMIT_BEFORE_SHA and CI_COMMIT_SHA.
This is deliberately not the span the release verbs use. plan and propose
ask what has landed since the last release; a run that acts on what a person
wrote must see exactly what this push carried, or a repository releasing
weekly would act on the whole week again on every merge.
What it writes¶
| Target | What happens |
|---|---|
| issue | a comment |
| merge request | a comment |
| wiki page | one frontmatter field is set; nothing else on the page is touched |
The syntax of the trailer, its targets and its keywords are in Commit trailers and annotations.
A wiki page whose frontmatter does not carry the field a keyword names is refused rather than grown one, and a page already carrying the value is reported as unchanged and not rewritten.
Running it twice¶
Safe. Each comment is posted under an idempotency key naming the commit that asserted it and the object it was about, and the forge provider returns the existing comment rather than posting a second. A later commit saying the same thing about the same object is a second comment, because it is a second assertion.
The key defends against a retry, not against two runs racing: it is not atomic, and two concurrent pushes asserting the same thing can both post.
What each forge can do¶
Every capability is optional and separately absent. An instruction the forge cannot carry is reported and skipped; the rest still apply.
| Forge | Issue comment | Merge request comment | Wiki |
|---|---|---|---|
| GitLab | yes | yes | yes |
| GitHub | yes | yes | yes |
| Gitea, Codeberg | yes | yes | yes |
| Bitbucket | no | yes | no |
Checked at go/forge v0.32.0 with forge-gitlab v0.25.0, forge-github v0.26.0,
forge-gitea v0.26.0 and forge-bitbucket v0.17.2. Bitbucket Cloud has no issue
tracker and no wiki API.
Output¶
One line per instruction, then a summary when anything wants attention:
applied #13
applied /wikis/specs/0079-pipeline-churn — status set to IMPLEMENTED
unchanged phpboyscout/cicd/wikis/specs/0080-other — status is already IMPLEMENTED
skipped !45 — this forge has no comment capability for merge request
failed phpboyscout/go/forge#23 — 403 forbidden
2 of 5 needed attention. The merge stands; nothing here failed the pipeline.
Exit code¶
Non-zero when an instruction failed, and zero otherwise.
| Outcome | Exit |
|---|---|
| every instruction applied or already present | 0 |
| an instruction was skipped — this forge has no such capability | 0 |
| an instruction failed — the forge was asked and refused | 1 |
| the run could not start — unreadable manifest, unknown forge, bad span | 1 |
A write that lands but loses part of what was asked is applied, not failed. GitLab's wiki API takes no commit message, so a page edit there succeeds with GitLab's own message instead of colophon's; the line says the page changed and names what was dropped, and the exit is 0.
A failure does not stop the instructions after it. The run carries everything it can, prints one line per instruction, and then reports the failure. By the time this runs the merge has happened, so abandoning the rest would help nobody.
A skipped instruction is never a failure. It means the forge cannot do this at all — Bitbucket has no wiki, Gitea's adapter has no comment capability — so no change to your repository could ever clear it, and gating on it would report a problem forever.
Choosing whether that fails your pipeline¶
That choice is allow_failure, and it belongs in your CI configuration rather
than in colophon:
Yellow means the run completed and something wants an engineer's eye, which is
usually what you want here: the merge already happened, so a red default branch
gives nobody anything to go back and fix. Set allow_failure: false where an
unapplied instruction should stop the line.
This changed. apply used to exit zero whatever happened, so a job whose
only instruction failed went green and nothing said so. Colophon now reports
the outcome and leaves the pipeline's colour to allow_failure. A repository
running apply without allow_failure will see a red job where it saw a green
one; allow_failure: true restores the old colour and keeps the signal.