Skip to content

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:

  1. bumps the pin in your controls.yaml, on a branch, rewriting one line and no other byte;
  2. runs openregs impact for exactly that release move against exactly that mapping;
  3. posts the impact table as the pull request body — impacted control, its owner, the atom, the classification and the citation, one row each;
  4. 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.
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.

InputDefaultWhat 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
mapcontrols.yamlthe mapping to bump, relative to the repository root
basemainthe branch the bump is cut from and opened against
out.openregswhere the five output files are written
FileFor
impact.jsonthe whole report — the document openregs impact writes to stdout
pull-request.mdthe request body, rendered from that document
pull-request-title.txtone line
verdict.txtpass or fail — the check status, in one word
branch.txtthe 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.

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.

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: forgeGH_TOKEN is set and gh is 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.

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_applicable plus 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.

  • 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 coverage is 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.

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