Moving between releases
Every repository on Airframe runs a fixed, tested combination of the platform’s tooling, called a release, and moves to a newer one on purpose, by a small merge request. This page is the how-to for that move, for going back, for the day a pipeline says its release was revoked, and, for platform maintainers, for cutting a new release. Releases explains what a release is and why it exists.
A release has a dated id of the form YYYY.MM.DD-N, such as 2026.09.30-2. The list of releases that may run is control-plane’s releases/permitted.yaml on main, and nothing else: a release is either permitted or revoked.
1. See which release you run
Section titled “1. See which release you run”- On this site: the footer of every page names the release the site describes. It is read, when the site is built, from the website repository’s own pipeline file.
- In a repository: its
.gitlab-ci.ymlincludes the platform’s components asgitlab.com/projectairframe/control-plane/<component>@<commit>. A release’s manifest,releases/<id>.yamlin control-plane, names the control-plane commit that belongs to it. - In a pipeline: open the
gate:integrityjob. It prints the gate’s verdict, which names the release the pipeline runs: an id,unknownfor a commit no release names or a reference that moves, ornonefor a pipeline with no platform components.
2. Install the command once
Section titled “2. Install the command once”The airframe command lives in control-plane’s cli/ directory. Install it from a clone of control-plane with a link on your path:
ln -s "$PWD/cli/airframe" ~/.local/bin/airframeThe command reads the releases from that clone, so pull its main before you move a repository; a stale clone does not know the newest release.
3. Move a repository to a release
Section titled “3. Move a repository to a release”Where: the repository you are moving, on a branch.
-
Run the command with the release you want:
Terminal window airframe pin --release <id>It rewrites every reference the repository’s pipeline files carry to that release’s commits: the component includes, the template and golden-path commits, the security policies’ include, and the release id where a file names it. It prints each file it changed. It refuses a release that is revoked or not listed.
-
Read the diff. It changes pipeline files only, never application code or infrastructure.
-
Run
airframe pin --checkto list any include still at a reference that moves. -
Open a merge request with that change and nothing else.
-
Who approves: for a product, someone from the product’s team or the platform team other than the author, then the gate, because the pipeline file belongs to both; for a platform repository, the platform team, then the gate. One platform approval suffices while one maintainer runs the platform; the design asks for two. Today the approvers are the maintainer’s own accounts: the mechanism is enforced, but the separation between two people is not yet real.
-
Merge. The next pipeline runs the new release.
4. Roll back
Section titled “4. Roll back”Going back is the same command with the previous release, and the same kind of merge request. You can go back only to a release that is still permitted: the command refuses a revoked one, and so would the gate.
5. When a pipeline says the release was revoked
Section titled “5. When a pipeline says the release was revoked”A revoked release is one a review found flawed. Once its line in the permitted list changes on control-plane’s main, the gate fails any new pipeline still pinned to it, in the gate:integrity job, with a message of this form:
release <id> is revoked: <reason>; run `airframe pin --release <upgrade>` and merge it (ADR-0031)The executor refuses the same release on a merge request’s runs, before it holds any credential (enforced). Do what the message says: steps 3.1 to 3.6 with the release it names. The merge request’s own pipeline is judged at its own commit, which already names the new release, so it runs.
A commit that no release names, or a branch name in an include, passes today with a warning in the verdict. The next release is planned to refuse it (designed), so pin a permitted release before then.
6. Cut a release (platform maintainers)
Section titled “6. Cut a release (platform maintainers)”Where: the four repositories of the five parts (the modules live in golden-path), then control-plane. Who approves: the platform team in each, and the gate in each but security-policies, which the gate does not judge. As in step 3, today the platform’s approvers are the maintainer’s own accounts.
- Pin the parts to each other, one merge request per repository: golden-path’s template and the security policies’ product template include control-plane at the new commit, golden-path’s
copier.ymlnames the modules’ commit, and platform-skills tests against the new control-plane and golden-path commits.airframe pin <commit>writes control-plane’s commit; it takes only a full 40-character commit, never a branch or a short commit. - In control-plane, add
releases/<YYYY.MM.DD-N>.yamlwith the id, the date, the five merged commits (control_plane,golden_path,modules,security_policies,platform_skills) and a note, and add<id>: {state: permitted}toreleases/permitted.yaml, in one merge request. - Run
airframe release check --remotefirst: it fetches every commit and fails when a part pins another commit than the release names. - Two jobs must pass before the merge request can merge (enforced):
release:check, which also fails when an earlier manifest changed, and therelease:verifyjobs, which run each part’s own checks at the release’s commits. - Roll it out with step 3 in each repository.
7. Revoke a release (platform maintainers)
Section titled “7. Revoke a release (platform maintainers)”Where: control-plane, releases/permitted.yaml, in a merge request approved by the platform team and the gate; as in step 3, today the platform’s approvers are the maintainer’s own accounts.
- Move every repository on the release to its successor first, as the platform did for its own revocations, so no current pipeline stops.
- Change the release’s line to
{state: revoked, reason: <why>, upgrade_to: <a permitted release>}.release:checkrefuses a revocation without a reason or with an upgrade that is not permitted. - Leave the manifest where it is: a merged manifest never changes, so the record of what ran stays.
The gate reads the permitted list from control-plane’s main and keeps it for five minutes, so a revocation reaches every new pipeline within minutes of the merge.
Where it lives
In the control-plane repository:
