Release
Use this guide to publish a tested commit as a versioned container image and GitHub release. The release workflow publishes artifacts; environment configuration, migrations, rollout, and rollback belong to deployment.
Trigger and prerequisites
The release workflow runs only through workflow_dispatch, with one required string input, version. Select master
when dispatching it. A tag push or a merge does not trigger this workflow.
Before dispatching:
- Push the selected commit to
masterso Unified CI runs for that commit in this repository. You can dispatch the release while CI is queued or running; validation waits for a successful, completed run. Pull request and manually dispatched CI runs do not satisfy this check. - Choose an unused version such as
v1.2.3orv1.2.3-rc.1: avprefix and valid SemVer are required, build metadata such as+build.7is rejected, and the complete string must be at most 128 characters. Neither a Git tag nor a GitHub release may already exist for this version. - Check the release credentials. Workflows write access allows tagging commits that change workflow files. Release credentials are used in the publication job, after the image is pushed.
- Ensure the workflow's
GITHUB_TOKENcan publish thego42package to GHCR. The build job declares Packages write, Contents read, Actions read, Attestations write, and ID token write permissions; repository and organization settings must allow those operations.
The validation job checks for successful CI immediately, then every 15 seconds for up to 100 checks, about 25 minutes. It continues as soon as a matching successful run is available and fails if none appears within that budget. Failed or cancelled CI runs do not satisfy the check; rerunning the matching push run can satisfy it before polling ends. The job timeout is 30 minutes to allow time for API requests.
Validation requires the workflow definition and dispatch SHA to match the selected master commit. It checks the
master head before and after waiting for CI. If the head has changed, validation fails; dispatch again for the new head.
All release dispatches share a concurrency group and do not cancel an active run.
Release key isolation
The GitHub configuration reference lists release credentials and their scope.
The publication job binds to release so that
the environment policy controls access to its key. Runtime denial checks for other branches and tags remain pending.
The same isolation pattern applies to the
go42x release workflow.
The go42 release run and
go42x release run on 2026-09-14 both generated App tokens
inside their release environment jobs. The go42x release completed; go42 stopped at the now-removed App slug check
because its organization variable included a trailing line break. These runs confirm key access from master.
Verify release access
-
Confirm each release workflow on
masterbindspublish-releaseto thereleaseenvironment. Retain its source validation, dependencies, permissions, and repository-scoped App token:jobs:publish-release:environment: release# Existing job configuration follows. -
Verify the environment permits only the branch
masterand holds the key for the configured App. Check that neither repository can access organization or repository copies of the key. An environment secret with the same name cannot prevent other jobs from accessing a repository or organization copy. -
Use a dedicated check that does not publish artifacts. A job referencing
releaseonmastermust start and find the key. Jobs on feature branches and tags must be blocked. A job without the environment must not find the key. Check presence only, without logging the value. The release runs above verified key access onmaster; the other runtime checks remain pending. The release workflow's own branch validation is not sufficient evidence that the environment policy works.
When rotating the key, update the environment secrets and inventory other consumers before retiring the App key. GitHub cannot return an existing secret's value; use the original private-key file or generate a new key for that App. Other consumers retaining a key for the same App retain that App's authority and need their own access review.
See GitHub's environment protection rules and secret configuration for setup and access requirements.
Publish a version
In the repository's GitHub Actions view, open the release workflow, choose Run workflow, select master, enter the
version, and run it. This writes a container image, attestations, a Git tag, and a published GitHub release.
The workflow runs the following jobs in sequence; prereleases skip image promotion:
| Job | Behavior and expected result |
|---|---|
validate-release | Validates the version, source commit, and completed CI run; records the source SHA and CI link in its summary |
docker-build | Checks out that SHA, builds and pushes both Linux architectures, validates the digest, and produces provenance and SBOM attestations |
publish-release | Creates the tag at the selected SHA and a non-draft release named Release <version> |
promote-docker | For stable releases, moves latest to the published image digest and verifies that both references identify the same image |
The GitHub release includes generated release notes plus the source SHA, immutable image reference, successful CI link,
and release workflow link. Versions containing a prerelease suffix produce GitHub prereleases and are not marked latest;
stable versions use GitHub's legacy latest-selection behavior.
The App token allows the release.published event to trigger optional downstream workflows. This checkout does not
provide a deployment workflow consuming that event. Publishing a release does not install or upgrade the Helm chart.
Artifacts and identity
The container build workflow publishes
ghcr.io/<repository-owner>/go42:<version>. The service/package name is explicitly go42, even if the repository is
renamed. The exact published image is identified by ghcr.io/<repository-owner>/go42@sha256:<digest> in the release body;
use that digest when selecting an image for deployment. The container build workflow publishes only the supplied tag.
After image publication, attestations, and GitHub release creation succeed, the release workflow moves latest to the
stable image's digest and verifies it. CI builds and prereleases do not move latest. Major-version and minor-version
aliases are not configured. Release runs are serialized because stable versions share latest.
The container build workflow checks out and verifies the source SHA supplied by its caller, then builds and pushes the
image under the supplied tag. Unified CI passes its triggering SHA and a ci-<run-id>-<run-attempt> tag. The release
workflow passes the version and source SHA produced by validate-release.
The release build targets linux/amd64 and linux/arm64. The Dockerfile compiles the application with
CGO disabled, using the Go version from go.mod, and embeds the source commit and requested release version.
The application reports these as build_commit and build_tag. The image includes the application,
static assets, API definitions, and migrations; running those migrations remains a deployment step.
The build generates sbom-go42-<version>.spdx.json from the published digest as an SPDX JSON workflow artifact and attaches
build provenance and SBOM attestations to that image identity through GitHub's attestation actions. The release workflow
does not package standalone application binaries or Helm charts as release assets.
Unified CI uses separate ci-<run-id>-<run-attempt> image tags.
CI binaries embed the same tag as build_tag, alongside the exact source commit as build_commit.
A release rebuilds the tested source with the requested version; it does not promote the CI image digest. The release
workflow does not rerun the application test suites against its new image. CI also treats image security scans and load
tests as non-blocking, so a successful run does not establish that every advisory check passed.
After CI, cleanup removes the temporary image tag. GHCR rejects deletion of a package's last tagged version; cleanup reports a warning and retains that version. Other API failures fail cleanup.
Local build tasks serve development: task build produces .build/app, and task image builds
for the host platform and loads the image into the local Docker store. It uses the build cache and defaults to
ghcr.io/go42-dev/go42:dev; override IMAGE and VERSION when needed. Neither task publishes a release.
Verify publication and investigate failures
After the workflow completes:
- Confirm the first three jobs succeeded and
promote-dockersucceeded for a stable release or was skipped for a prerelease. Open the linked GitHub release and compare its source SHA with the commit shown by the linked successful Unified CI run. Confirm the tag resolves to that SHA and the prerelease status matches the requested version. - Open the image package in GHCR and check the version tag and manifest digest against the release's immutable image
reference. Confirm the image contains both
linux/amd64andlinux/arm64manifests. - Inspect the build's provenance and SBOM attestation summaries and download the named SBOM from the workflow artifacts. Confirm their subject digest matches the release image. These establish the published artifact's identity and inventory; verify the deployed application separately using the deployment guide.
The image is pushed before attestations, SBOM generation, and GitHub release creation. A later failure can therefore leave
an image or attestations without a GitHub release; the workflow has no automatic rollback. Inspect the failed step and
existing image, tag, and release before retrying. A promotion failure leaves the GitHub release published; inspect the
latest digest and rerun the failed promotion job. Validation rejects an existing Git tag or release, but does not check
whether the version's GHCR image tag already exists.
These instructions describe checked-in workflow behavior. App installation, repository permissions, published artifacts, and environment promotion policy need verification in the target repository; they are not established by source review.
Source: docs/handbook/release.md. Revision: 3841815ceb9e.