Skip to content

The release bump on GitLab CI

The GitLab equivalent of docs/integrations/github-actions.md. It is the same command with the same exit code — GitLab has no composite actions, so the job runs openregs bump directly — and it differs in exactly one place: a merge request is opened with git push options rather than with a forge CLI, so the job needs no token to open it.

Copy this into your repository’s .gitlab-ci.yml. It is the snippet the test suite executes; see “What the test harness runs” below for the two lines it cannot.

stages:
- openregs
openregs-bump:
stage: openregs
image: python:3.12-slim
variables:
# The mapping is also the pin file: its `release:` field is the pin.
OPENREGS_MAP: controls.yaml
OPENREGS_OUT: .openregs
# $OPENREGS_RELEASE (<regime>@<tag>) comes from the pipeline trigger the
# OpenRegs release feed fires; $OPENREGS_REGISTRY is a CI/CD variable naming
# the registry to pull it from.
GIT_STRATEGY: clone
rules:
- if: $OPENREGS_RELEASE
before_script:
- pip install --quiet "openregs==1.0.0"
script:
- |
set -u
status=0
# --push-option is what opens the merge request: GitLab reads these off the
# push itself, so no token and no API call is needed to get one open.
if openregs bump \
--repo "$CI_PROJECT_DIR" \
--to "$OPENREGS_RELEASE" \
--from "$OPENREGS_REGISTRY" \
--map "$OPENREGS_MAP" \
--base "$CI_DEFAULT_BRANCH" \
--out "$OPENREGS_OUT" \
--push-option merge_request.create \
--push-option "merge_request.target=$CI_DEFAULT_BRANCH"; then
status=0
else
status=$?
fi
# Exit 2 is "the question could not be asked" — an unbuilt release, a
# registry that did not verify, a mapping the validator rejects. Nothing was
# pushed, and the job must look like neither a pass nor a reviewed failure.
if [ "$status" -eq 2 ]; then
echo "openregs: the bump could not be prepared; nothing was pushed"
exit 2
fi
# The job log is where a GitLab reviewer reads the report, and the artifact is
# what the merge request description is filled from.
- cat "$OPENREGS_OUT/pull-request.md"
- |
set -eu
if [ "$(cat "$OPENREGS_OUT/verdict.txt")" = fail ]; then
echo "openregs: FAIL — an atom mapped to a control changed substantively."
echo "openregs: $(cat "$OPENREGS_OUT/branch.txt") must be reviewed by the control"
echo "openregs: owners named in the report above before it merges."
exit 1
fi
echo "openregs: OK — no atom mapped to a control changed substantively."
artifacts:
when: always
paths:
- .openregs/
expose_as: OpenRegs impact report
  • --from "$OPENREGS_REGISTRY" — the release is pulled and verified (signature, trust anchor, digests, provenance, transparency-log inclusion) over a staged copy before a byte of it is read. A release a consumer could not check is not one it may pin itself to.
  • --push-option merge_request.create — GitLab creates the merge request from the push that carries the bump branch. merge_request.target sets its target branch; merge_request.title and merge_request.description can be set the same way when you would rather not use the artifact.
  • the exit code — 1 if and only if an atom your mapping maps to a control changed substantively; 0 otherwise; 2 when the question could not be asked. An editorial change to a mapped atom, a change to an atom you have waived and a change to an atom nobody mapped are all reported and none of them fails the job.
  • artifacts: when: always — the report is most worth keeping on the run that failed.

A red job that anyone can merge past is decoration. In GitLab:

  • Settings -> Merge requests -> Pipelines must succeed, so the bump merge request cannot merge while openregs-bump is red; and
  • give the branch openregs/bump-* no push exception that would let it bypass that.

The check is red on precisely the days it is worth something; see docs/integrations/github-actions.md for what the owner of the named control is expected to do about it.

The bump pushes to the repository’s own origin, so the job needs a credential that may write to it — a project access token or a deploy key in CI_PUSH_TOKEN, wired into the remote the usual way:

Terminal window
git remote set-url origin \
"https://oauth2:${CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"

CI_JOB_TOKEN cannot push. If your runner clones over SSH with a deploy key that has write access, nothing needs changing.

tooling/tests/test_downstream_bump.py reads the YAML block above out of this file and executes the job’s script: for real, against a consuming repository whose remote is a bare repository under .test-remotes/. That is why the snippet is written as three plain shell entries: it is a pipeline, not a picture of one. The test asserts that the branch is pushed with the pin bumped, that merge_request.create arrives at the remote as a push option, that the report printed into the job log carries the impact table, and that the job exits 1 for a mapping the release move impacts.

Two things in the snippet the harness does not execute, and no test here may claim: image: (a runner image is a container this suite may not pull) and before_script: (installing from PyPI is a network call). The test puts the CLI on PATH from the checkout instead. rules: is likewise documentation here — if: $OPENREGS_RELEASE keeps the job out of every pipeline that is not a release bump, and GitLab, not this harness, is what evaluates it.

Rendered from openregs/openregs@cbe9615:docs/integrations/gitlab-ci.md