Files
audita/docs/release.md

6.1 KiB

Releasing Audita

The release policy defines versioning, supported platforms, immutability, and success. This runbook implements it with scripts/release.sh. Publication ends after a successful source/tag push; binary delivery runs asynchronously.

Prerequisites

Use Bash, Git, and Go 1.25 or newer (CI uses Go 1.25), with dependencies available locally or downloadable. Tests themselves are offline. Work from a checkout of main with permission to push that branch and tags to origin. For this repository, origin is https://gitea.maximumdirect.net/eric/audita.git. The script uses the checkout's configured origin; set it correctly before use. The remote must support atomic pushes. No CI credentials or Mac are needed locally.

CI uses the existing GITEA_RELEASE_TOKEN secret for asynchronous asset delivery. macOS arm64 is a best-effort development environment; no macOS binary is published.

Prepare the Candidate

Choose an unused version following the policy. Write docs/releases/<version>.md, beginning with # Audita <version>, followed by a short summary and any compatibility or upgrade instructions relevant to the release. There are no mandatory boilerplate sections. Update affected canonical documentation with behavior changes.

Commit the intended changes, including the note, on main. The release script requires a clean index and worktree and does not create commits. There is no separate approval round. Published notes describe their tagged release; existing release history does not need to be reconstructed.

Validate and Publish

From the repository root, substitute the chosen version:

bash scripts/release.sh --publish v0.2.0

This single invocation:

  1. Checks the version, branch, clean candidate, matching note title, and tag absence.
  2. Runs the default offline test suite once, then builds both required Linux targets in a temporary directory using the same build script as CI.
  3. Checks that the candidate and worktree are unchanged and that it includes the current origin/main history.
  4. Creates the version tag at that commit and atomically pushes the exact source commit to main and the exact tag to origin.
  5. Reports release success and exits without polling CI or checking asset availability.

To validate a prepared candidate without remote access, creating a tag, or pushing:

bash scripts/release.sh --check v0.2.0

This is an optional diagnostic command, not a required step before --publish. Publication validates within its own invocation; it does not use persistent cached results. Both modes require the candidate note and a clean main checkout.

Artifact Delivery

Woodpecker runs on tag events. It invokes scripts/build-release.sh, then publishes:

  • audita-<version>-linux-amd64
  • audita-<version>-linux-arm64
  • SHA256SUMS

Builds disable CGO and use the shared script's release flags. The version is in the asset name and source tag; Audita has no CLI version command. Cross-compilation checks compilation, not native execution on each architecture.

The CI job status and logs show delivery progress or failure. Assets appear on the matching Gitea release page when delivery succeeds. A successful tag push must not be reported as confirmation that these files are available.

Consumers can download the binary for their Linux architecture and SHA256SUMS from that page, check its listed SHA-256 digest with sha256sum, and make it executable. Optional consumer checks are not maintainer release gates. Digests detect file mismatches; they are not an independent signature of authenticity.

Failure and Retry

Local validation fails: correct the issue, commit any changes, and rerun the publication command. A failure before tag creation publishes nothing. Temporary local build files are removed on exit.

Push fails or the connection is interrupted: the script retains the local tag and reports failure rather than guessing whether publication succeeded. Inspect remote state with the actual version:

git rev-parse 'v0.2.0^{commit}'
git ls-remote origin refs/heads/main refs/tags/v0.2.0

If the remote tag identifies the intended candidate and its source is on remote main, the release succeeded; no CI waiting is needed. An atomic push normally updates both refs or neither. If main has advanced since publication, fetch it and check candidate ancestry rather than requiring tip equality.

If the tag is absent remotely and was never published or used by a release channel, remove only that unpublished local tag (git tag -d v0.2.0), resolve any rejected branch update, and rerun the command. If the remote tag has a different target, do not overwrite it; choose a fresh version. Never force a branch or tag update to bypass the guards.

CI delivery fails: the release remains successful. Inspect the failed tag job's logs and retry that same tagged job for a transient failure. Publication is configured to skip existing filenames and not overwrite assets. Keep the same source and build environment when retrying; inspect any existing assets/checksum manifest before filling gaps. Do not regenerate and replace a published manifest or binary. If an existing manifest cannot describe the missing rebuilt assets, use a new version instead. A pipeline or source defect also requires a corrected commit and new version. An outage does not require moving the tag.

A released source defect is discovered: fix it on main and publish a new appropriate version with a note explaining the correction. Leave the old tag and published files intact.

Maintaining the Release Workflow

The shared build script keeps local and CI build settings aligned. Tests in scripts/release_test.go exercise publication against temporary local Git remotes, including guard failures and rejected pushes, with Go execution stubbed to avoid recursive builds/tests. They run in go test ./.... Changes to CI delivery should preserve explicit asset names, checksums, and non-overwriting publication. CI delivery is separate from the local success path.