Skip to content

Latest commit

 

History

History
137 lines (113 loc) · 6.8 KB

RELEASING.md

File metadata and controls

137 lines (113 loc) · 6.8 KB

Releasing reflect

Reflect is pre-1.0. Releases are cut by hand: a maintainer merges a release PR that sets VERSION and the CHANGELOG.md section, then pushes the matching v<VERSION> tag. The tag triggers .github/workflows/release.yml, which builds, signs and publishes the GitHub release. There is no release-please or other bot: nothing bumps VERSION or writes CHANGELOG.md automatically.

The next release version comes from VERSION. The new Reflect series restarts below the historical hawk versions; use explicit tags for Go installation rather than assuming @latest selects this series.

Tag protection

Only repository admins can create, move or delete release tags. The repository ruleset protect-release-tags (id 24053191) is active for tags matching refs/tags/v* and restricts creation, update, deletion and non-fast-forward updates, with the repository admin role as the only bypass actor. A push of a v* tag by anyone else is rejected, so a write-access account cannot trigger a signed release. Inspect it with:

gh api repos/GrayCodeAI/reflect/rulesets/24053191

The main branch is separately protected (required status checks, no force pushes).

Release checklist

  1. Release PR (conventional title, e.g. chore(release): vX.Y.Z):

    • VERSION holds X.Y.Z (no leading v).

    • CHANGELOG.md: rename ## [Unreleased] to ## [X.Y.Z] — YYYY-MM-DD and add a new, empty ## [Unreleased] above it. This section becomes the GitHub release notes verbatim.

    • testdata/compatibility-matrix.json: set the stable matrix to reflect X.Y.Z and the relay version pinned in go.mod.

    • README.md install section: point the install.sh URL at the new tag.

    • Run locally:

      make release-check TAG=vX.Y.Z   # tag == v$(cat VERSION), CHANGELOG section present
      make release-snapshot           # goreleaser check + all archives into dist/, no publish/sign
  2. Merge the PR and wait for CI on main to pass.

  3. Tag the merge commit (admins only, see above):

    git fetch origin
    git tag -s vX.Y.Z -m "reflect vX.Y.Z" <merge-commit-sha>
    git push origin vX.Y.Z
  4. Watch the release workflow run to completion (next section).

  5. Verify the published release as a user would (see Verifying a release), including curl -fsSL https://raw.githubusercontent.com/GrayCodeAI/reflect/vX.Y.Z/install.sh | REFLECT_REQUIRE_COSIGN=1 sh and go install github.com/GrayCodeAI/reflect/cmd/reflect@vX.Y.Z.

make release deliberately refuses to publish from a workstation: releases are signed with the workflow's GitHub OIDC identity, which a local run cannot have.

What the release workflow does

On a pushed v* tag, with permissions: contents: write, id-token: write:

  1. scripts/check-release-tag.sh refuses the run unless the tag is exactly v + VERSION and CHANGELOG.md has a non-empty ## [<VERSION>] section; that section is extracted as the release notes.
  2. scripts/check-no-replace-directives.sh refuses local replace directives. The job sets GOWORK=off, so dependencies (including relay) come only from go.mod/go.sum via the public proxy.
  3. syft (anchore/sbom-action/download-syft) and cosign (sigstore/cosign-installer) are installed; both actions are pinned by commit SHA.
  4. GoReleaser v2.17.0 (.goreleaser.yml) builds and publishes:
    • reflect_<version>_<os>_<arch>.tar.gz for linux and darwin and .zip for windows, each on amd64 and arm64 (six archives, -trimpath, CGO_ENABLED=0);
    • reflect_<version>_source.tar.gz;
    • one SPDX SBOM per archive (<archive>.spdx.sbom.json);
    • checksums.txt (SHA-256 of every asset above);
    • checksums.txt.sigstore.json, a cosign keyless Sigstore bundle (certificate, signature and transparency-log proof) over checksums.txt.
  5. The workflow re-downloads checksums.txt and the bundle from the published release and verifies them with the exact certificate identity, so a broken signature fails the run visibly.

Verifying a release

tag=vX.Y.Z
gh release download "$tag" --repo GrayCodeAI/reflect \
  --pattern checksums.txt --pattern checksums.txt.sigstore.json \
  --pattern "reflect_${tag#v}_linux_amd64.tar.gz"
cosign verify-blob --bundle checksums.txt.sigstore.json \
  --certificate-identity "https://github.com/GrayCodeAI/reflect/.github/workflows/release.yml@refs/tags/$tag" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt
sha256sum --ignore-missing -c checksums.txt

install.sh performs the same checks: it always verifies the SHA-256, verifies the bundle when cosign (v2.4 or later; v3 recommended) is installed, says so plainly when it is not, and refuses to install without cosign when REFLECT_REQUIRE_COSIGN=1.

Distribution channels

Channel Status
GitHub release archives Pending a Reflect release with matching archives.
install.sh Pending a Reflect release; refuses known hawk-era tags.
go install github.com/GrayCodeAI/reflect/cmd/reflect@latest Use an explicit Reflect tag or @main; historical higher tags affect @latest.
GitHub Action (.github/actions/reflect) Requires Reflect release archives; installs through install.sh with cosign required.
systemd unit (packaging/systemd/reflect-daemon.service) Template for running reflect daemon; see the troubleshooting guide.
Homebrew Not available. Needs a GrayCodeAI/homebrew-tap repository, a fine-grained token with write access to it stored as a secret, and a homebrew_casks stanza in .goreleaser.yml.
npm Not available. The scaffolding in npm/ is marked private; see npm/README.md.
Nix Not available. The old flake could not evaluate and was removed; a new one needs a computed vendorHash, a committed flake.lock and nix flake check in CI.

Add a channel to the README only after it has worked end to end for a real release.

Historical tags and the reset version series

  • Tags v0.1.0, v0.1.1 and v0.2.0 were published under the previous module path github.com/GrayCodeAI/hawk; their assets are hawk_* archives containing a hawk binary. None of them is an ancestor of main, and v0.1.1 has no GitHub release. The Go proxy keeps serving them, so the current go.mod retracts [v0.1.0, v0.2.0].
  • Because no earlier tag is reachable from main, the release notes come only from the CHANGELOG.md section and the "Full changelog" compare link is omitted.
  • The historical higher-numbered hawk releases must not be confused with the reset Reflect series. Publishing a lower version does not automatically make it the Go proxy's @latest. Use explicit tags; do not rewrite existing tags.