The release workflow installed Go via actions/setup-go, which pins the action but not the toolchain tarball it downloads at runtime -- the compiler that produces the published binaries was the last external input in the release path verified against nothing in the repo, against REPO_POLICIES.md's hash-pin rule. New script/install-go, modelled on script/install-goreleaser, downloads the exact go.dev archive for go.mod's `go` directive and refuses it unless its sha256 matches a value committed in the script. release.yml calls it instead of setup-go and sets GOTOOLCHAIN=local so that exact compiler builds the release. The version is not duplicated: go.mod owns it and install-go fails when its committed GO_VERSION disagrees, so bumping Go edits go.mod, the checksum, and the Dockerfile golang digest together. Model: opus-4-8
54 lines
2.5 KiB
YAML
54 lines
2.5 KiB
YAML
name: release
|
|
on:
|
|
push:
|
|
tags: ["v*"]
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
# actions/checkout v4, 2024-09-16
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
|
with:
|
|
# goreleaser needs the tags and the full history: the version
|
|
# it stamps comes from the tag, and the changelog comes from
|
|
# the commits since the previous one. A shallow checkout
|
|
# silently produces a mislabelled release.
|
|
fetch-depth: 0
|
|
# goreleaser is not a compiler: it shells out to `go` for the
|
|
# `before:` hook and for every one of the four cross-compiles.
|
|
# Nothing else in this repo puts a Go toolchain on the runner --
|
|
# check.yml runs script/cibuild, which does all of its work inside
|
|
# the digest-pinned Dockerfile images -- so without this step the
|
|
# release either fails at the before-hook or, worse, ships binaries
|
|
# built by whatever Go the runner happens to carry.
|
|
#
|
|
# actions/setup-go would pin the action by commit sha, but the Go
|
|
# tarball it downloads at runtime is verified against no value in
|
|
# this repo, and the action exposes no checksum input.
|
|
# REPO_POLICIES.md requires every external reference to be pinned
|
|
# by hash with no exceptions, and this is the compiler that
|
|
# produces the published binaries -- the input where a substituted
|
|
# artifact matters most. So Go is installed the way goreleaser is:
|
|
# script/install-go downloads the exact archive for go.mod's `go`
|
|
# directive and refuses it unless its sha256 matches the value
|
|
# committed in the script, then puts .tool/go/bin on PATH for the
|
|
# steps below.
|
|
- name: Install Go
|
|
run: script/install-go
|
|
- name: Install goreleaser
|
|
run: script/install-goreleaser
|
|
- name: Release
|
|
run: script/release
|
|
env:
|
|
# RELEASE_TOKEN is a repository Actions secret: a Gitea access
|
|
# token with write access to this repository's releases (scope
|
|
# write:repository), owned by an account that can publish here.
|
|
# It is deliberately not the runner's automatic token, which is
|
|
# not guaranteed to carry that scope.
|
|
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
# Build with the toolchain install-go just verified, never a
|
|
# different one auto-downloaded from a `toolchain` directive:
|
|
# the point of the hash pin is that this exact compiler makes
|
|
# the release.
|
|
GOTOOLCHAIN: local
|