The release-bump GitHub Action
When OpenRegs publishes a release, every repository that pins one has the same question to answer: does moving to it change a duty one of our controls discharges? This action answers it in the only place the answer is useful — the pull request that does the moving.
It does four things, in this order:
- bumps the pin in your
controls.yaml, on a branch, rewriting one line and no other byte; - runs
openregs impactfor exactly that release move against exactly that mapping; - posts the impact table as the pull request body — impacted control, its owner, the atom, the classification and the citation, one row each;
- fails the check when an atom mapped to a control changed substantively, so that with the check required by branch protection the bump cannot merge unreviewed.
Using it
Section titled “Using it”name: openregs release bump
on: repository_dispatch: types: [openregs-release]
permissions: contents: write pull-requests: write
jobs: bump: name: bump runs-on: ubuntu-24.04 steps: - uses: actions/checkout@<sha> # v7.0.1 - name: bump the pinned OpenRegs release uses: openregs/openregs/.github/actions/release-bump@<sha> # v1 with: release: ${{ github.event.client_payload.release }} registry: ${{ vars.OPENREGS_REGISTRY }} env: GH_TOKEN: ${{ github.token }}Pin the uses: to a commit sha, the way this repository pins every action it
uses. A tag is a pointer its owner can move.
fixtures/consumers/fixture-bank/ and fixtures/consumers/quiet-mutual/ are two
complete consuming repositories in exactly this shape, and the test suite runs
this action against both.
Inputs
Section titled “Inputs”| Input | Default | What it is |
|---|---|---|
release | (required) | the release to pin to, as <regime>@<tag> |
registry | "" | the registry to pull it from. It is verified — signature, trust anchor, digests, provenance, transparency-log inclusion — over a staged copy before a byte of it is read |
map | controls.yaml | the mapping to bump, relative to the repository root |
base | main | the branch the bump is cut from and opened against |
out | .openregs | where the five output files are written |
What it writes
Section titled “What it writes”| File | For |
|---|---|
impact.json | the whole report — the document openregs impact writes to stdout |
pull-request.md | the request body, rendered from that document |
pull-request-title.txt | one line |
verdict.txt | pass or fail — the check status, in one word |
branch.txt | the branch the bump was pushed to |
They land in the workspace, not on the branch: the bump commit contains the
mapping and nothing else. Add .openregs/ to your .gitignore so a later job
cannot commit them by accident.
The pin is controls.yaml
Section titled “The pin is controls.yaml”There is deliberately no separate pin file. A controls.yaml already has to state
which release its atom ids were written against — that is what the release:
field is for — and a second copy of that fact in a second file would only ever be
useful for disagreeing with the first. So the bump rewrites release: in place,
keeps every comment around it, re-parses the file, and refuses the whole bump if
anything else moved.
What “opens a pull request” means
Section titled “What “opens a pull request” means”A pull request is a branch on a remote plus a request body. The action always
produces both: openregs bump pushes openregs/bump-<regime>-<tag> to the
repository’s own origin and writes the body beside it. Registering that branch
with the forge is the one step that needs a forge, and the action is explicit
about which of the two paths it took:
pull request mode: forge—GH_TOKENis set andghis on the runner, so the action opens the pull request (or updates the one already open on that branch) with--body-file;pull request mode: branch-only— neither is available, so the action says so and leaves the branch and the body, which is a pull request in every sense except the forge’s record of it.
Grant GH_TOKEN (and pull-requests: write) unless you have a reason not to. The
check is what enforces review either way: it is red for the same input in both
modes.
When the check is red
Section titled “When the check is red”The body names the control and its owner. Someone who owns that control reads what moved — the body quotes the before and after text — and either:
- updates the control and pushes the update onto the bump branch, so that the release move and the control change merge together; or
- decides it does not apply, and replaces the mapping entry with a waiver
(
status: not_applicableplus a written rationale) in the same pull request. A waiver never fails a later check, which is exactly why it has to be written down and reviewed once.
What nobody should do is merge past a red check by disabling it. The check is red on precisely the days it is worth something.
What it will not fail for
Section titled “What it will not fail for”- an editorial change to a mapped atom: the words moved, the duty did not, and the release said so in the manifest it shipped;
- a change to an atom you have waived: the decision was made and recorded already, and a check that re-raised it every release would train its readers to ignore it. It is still in the body;
- a change to an atom nobody mapped: there is no control to review and no
owner to tell. It is a warning in the body, and
openregs coverageis the report that exists to close that gap.
This is openregs impact’s exit-code contract unchanged; see
docs/ARCHITECTURE.md and the module docstring of tooling/openregs/impact.py.
Offline, and what that means for what is proven
Section titled “Offline, and what that means for what is proven”No test in this repository may reach the network, so the action is exercised by
tooling/tests/workflow_harness.py, which executes every run: step for real
from a checkout and satisfies uses: steps locally. The consuming repositories
are real git repositories with bare remotes under .test-remotes/; the release
is pulled and verified out of fixtures/registry/; the branch is really pushed
and really carries the bumped pin.
What that does not prove is that GitHub runs it: the gh pr create path and
the runner image have never executed here. See docs/runbooks/ci.md for the same
statement about this repository’s own pipelines.
GitLab
Section titled “GitLab”docs/integrations/gitlab-ci.md is the same integration as a .gitlab-ci.yml
job, running the same command with the same exit code, and it opens the merge
request with git push options instead of a forge CLI.
Rendered from openregs/openregs@cbe9615:docs/integrations/github-actions.md