release-stamp¶
Comments the shipped version onto every issue a release closed, on the tag pipeline.
stages:
- release
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
inputs:
stage: release
token: $RELEASE_STAMP_TOKEN
What problem this solves¶
org spec 0001 (D3) moved issue closure to merge. That is the right boundary — once a merge request lands, no engineering action remains — but it drops what the previous rule carried: which release actually shipped the work.
The cost scales with cadence. On a repo that releases daily the gap is a day; on a slower one it has run to a fortnight, during which the ticket reads done and the thing is not yet usable by anyone consuming a tag.
Neither release tool closes it. releaser-pleaser's forge interface has no
comment method at all, and release-plz's forge client has no /comments
endpoint. Both are feature requests upstream, not configuration.
The chain, though, is derivable entirely from Free-tier API endpoints:
compare(previous_tag, tag) -> commits
commits/<sha>/merge_requests -> merge requests
merge_requests/<iid>/closes_issues -> the issues the release shipped
What it writes¶
One comment per issue:
Shipped in v0.37.0.
A comment, not a label. A label would need a value per version, would need creating per release, and goes stale the moment anything moves — the reason org 0001 D10 rules it out. A comment carries the version in its own text and cannot rot.
Each comment carries a hidden <!-- release-stamp --> marker, which is how the
job recognises its own work on a re-run.
It will not double-comment¶
A tag pipeline gets retried — by a person, or by the runner after an infrastructure failure. Without a guard, every retry adds another Shipped in vX.Y.Z. to every issue in the release.
Before commenting, the job reads the issue's notes and skips any already
carrying this version's marker. Keyed on the version rather than the whole
rendered comment, so changing template does not re-stamp history.
It will not fail your release¶
allow_failure defaults to true, matching
discord-release. By the time this job runs the tag
exists and the artefacts are published; a red release pipeline because an
annotation did not post sends someone looking for a release problem that is not
there.
It compensates by being loud — the log carries the resolved tag span, every issue stamped, and every one skipped with the reason:
release span: v0.36.0..v0.37.0
18 commit(s)
13 merge request(s): [206, 207, ..., 219]
4 issue(s) closed by v0.37.0:
#6: stamped
#14: stamped
#15: already stamped for v0.37.0, skipping
#16: stamped
stamped 3, skipped 1 already carrying v0.37.0
Set allow_failure: false if you would rather know loudly.
How the span is derived¶
The span is previous_tag..$CI_COMMIT_TAG, and getting previous wrong
silently changes which issues get stamped — too few, or a re-stamp of an older
release's.
The tags API's default ordering is by commit date, which is not release
order: a patch cut from an older branch sorts after a later minor. The job
asks for order_by=version and selects the tag immediately preceding the
current one in that list.
Two cases are handled rather than crashing:
- The first release has no predecessor. The span is the whole history, and the job says so.
- A tag not in the list means the job is running on something that is not a release tag. It refuses rather than guessing a span.
Cross-project issues¶
closes_issues can return issues in other projects. Stamping those writes into
a repository whose release this is not, on the strength of a cross-reference —
so the job filters to the running project (by numeric $CI_PROJECT_ID, which is
what that endpoint returns) and logs what it skipped, rather than silently
handling it either way.
Adopting on an existing backlog¶
Set dry_run: true for one release to see exactly which issues would be
stamped, then turn it off.
Retrofitting past releases is deliberately out of scope. The engine could do it, but a bulk edit of historical issues should be a deliberate act, not a side effect of adopting a component.
Jobs¶
| Job | When |
|---|---|
release-stamp |
On tag pipelines ($CI_COMMIT_TAG). Override with if. |
Inputs¶
| Input | Type | Default | Description |
|---|---|---|---|
stage |
string | release |
Stage to assign the job to. Declare it in your stages:. |
image |
string | …/dev-tools:v0.3.4 |
Runtime image. Needs only python3 — the engine is standard-library only, so there is no install step and no lockfile to rot. |
token |
string | $RELEASE_STAMP_TOKEN |
Token used to read the span and write a comment on each closed issue. Needs api scope. See token scope. |
template |
string | Shipped in {version}. |
The comment body. {version} is replaced with the tag. |
dry_run |
boolean | false |
Log the full plan and write nothing. |
allow_failure |
boolean | true |
Whether a failure here fails the release pipeline. See it will not fail your release. |
if |
string | $CI_COMMIT_TAG |
Gating expression. Tag pipelines are the only context where a release exists to stamp. |
Token scope¶
The token default is $RELEASE_STAMP_TOKEN, not $CI_JOB_TOKEN — a
deliberate deviation from the authoring rule that token inputs default to the
job token.
A job token cannot post issue notes. Defaulting to it would fail every single run, so the input names a variable you supply instead; the job refuses to start when it resolves empty, rather than running and stamping nothing.
api scope is required — this is the one component in this repository that
writes to issues. Scope it to the project it runs in, and make sure the variable
is protected if your release tags are protected refs, or it will not be
present on the tag pipeline.
Reusing an existing api-scoped release token is reasonable if you already have
one; this repository points the input at its releaser-pleaser token rather
than minting a second secret with the same power.
See also¶
releaser-pleaser— cuts the tag this job annotates.discord-release— the other never-fail-the-release announcement job, and where theallow_failuredefault comes from.- spec 0073 — the decision record.