What colophon needs from a pipeline¶
The list a pipeline has to satisfy for colophon to work, on any forge. The how-to guides for GitLab CI and GitHub Actions show each item as configuration; this page is the contract they implement.
1. A full checkout, with tags¶
Every verb starts by reading the checkout (history.Read): the worktree root,
the remote, and for plan and propose the current version, which is the
highest semver tag reachable from HEAD, then every commit since it. A
shallow clone cannot answer that. It hides tags and it truncates the walk, and
colophon fails closed rather than guessing:
That is what a depth-1 clone (no tags visible) and GitLab's default depth of 20
(one tag visible) both produce, measured with colophon 0.2.1 against
go/config-afero on 2026-09-04. The message does not yet say "shallow"; when
you see it in a pipeline, the checkout depth is the first thing to check.
| forge | what makes the checkout full |
|---|---|
| GitLab CI | GIT_DEPTH: "0" on the job. The cicd component sets it on every job that runs the colophon binary (colophon-release-check runs only git and fetches its own depth); do not override it with a project-wide shallow depth. Tags are fetched with a full clone. |
| GitHub Actions | actions/checkout with fetch-depth: 0, which its README defines as "all history for all branches and tags". |
Only the reading side depends on the checkout. Everything that writes (the
release commit, the tag, the containment check, the notes at a commit) goes
through pkg/tree, which clones from the remote in memory. So a job that passes
this requirement for plan passes it for every verb.
2. A token that can push tags and talk to the forge¶
The token is read from <FORGE>_TOKEN, upper-cased from the forge the remote
points at: GITLAB_TOKEN, GITHUB_TOKEN, CODEBERG_TOKEN, BITBUCKET_TOKEN.
The forge is detected from the remote's host (pkg/remote, knownHosts), and
a host it does not know is refused with ErrUnknownForge; a self-hosted Gitea
or GitLab is therefore not usable today, and GITEA_TOKEN exists in the
credential table without a way to reach it. See the
configuration reference for the
basic-auth user each token is sent as.
It needs to: read and write merge requests or pull requests, push the release
branch, push the tag, and create the release. On GitLab that is api +
write_repository. Where tags are protected, the token's owner must be allowed
to create them (v* at Maintainers on this estate's projects).
It must not be the pipeline's own job token. A tag pushed with GitLab's
CI_JOB_TOKEN fires no tag pipeline, and GitHub documents that "events
triggered by the GITHUB_TOKEN will not create a new workflow run" apart from
workflow_dispatch and repository_dispatch. Either way the build that should
follow the tag never starts. Use a project or group token on GitLab, and a
GitHub App installation token or a personal access token on GitHub.
plan needs no token at all.
3. An identity for the release commit and tag¶
colophon writes an annotated tag and a release commit and signs them with a name
and email (--author-name, --author-email; the component inputs of the same
names). Defaults are colophon <[email protected]>. Set them explicitly
rather than letting git take one from the runner's ambient configuration, or the
same release is attributed differently depending on where it ran.
4. publish before propose, as separate jobs¶
After a release merge request merges, the release commit is on the target but
the tag does not exist yet, and in that window colophon computes the same
release again. So on the default branch publish runs first and propose
second, and they are two jobs: publish tags through its own clone and
pushes to the remote, and propose sees that tag only because it is a separate
job whose fetch happened after the push. One job running both commands re-opens
a merge request for the release just tagged. The reasoning in full:
Tagging what actually landed.
5. Runs on the target branch only¶
Both verbs act on the branch named by --target (default main). Gate the jobs
to the default branch and to nothing else: not merge-request pipelines, not
tags, not schedules. The release merge request's own pipeline should run only
what proves that merge request is what colophon wrote; the cicd component's
colophon-release-check is that job.
6. Project settings the tool assumes¶
- Merge requests merge by fast-forward, with a rebase when the branch is
behind. colophon resolves the landed commit against the target rather than
from the request, so squash and rebase both work, but fast-forward is what
keeps
CHANGELOG.md's commit and the tag on the same object. - Merging requires a passing pipeline.
CHANGELOG.mdis colophon's to write. Hand edits belong in an ordinary merge request, not the release one..colophon.yamlis optional: it holds a release, or sets where release notes go and under what heading. See the release manifest.
7. For releases with assets, a tag pipeline¶
A repository whose releases carry binaries publishes with --tag-only on the
default branch, builds and uploads on the tag pipeline, and then creates the
release carrying links to what was uploaded (colophon release --tag, or
publish --assets --asset-base on older components). The tag pipeline needs
the same full checkout and the same token. See
Publish a release with its assets.
8. The image the job runs in¶
colophon itself needs nothing from the image but a git remote it can reach. One
thing bites jobs that run the git CLI against the checkout from a non-root
image: the runner's helper creates the checkout as root, and git refuses a
directory owned by another user. git diff a b refuses silently and prints
error: Could not access '<sha>' as if the object were missing. Run
git config --global --add safe.directory "$CI_PROJECT_DIR" first, or set
safe.directory '*' in the image. colophon's own verbs are unaffected: go-git
does not consult safe.directory.