The release workflow installed Go with actions/setup-go, which pins the action but not the Go archive it downloads, so the compiler that builds the published binaries was verified against nothing in this repo. New script/install-go, modelled on script/install-goreleaser, downloads the go.dev archive for the version in go.mod and refuses it unless its sha256 matches the value committed in the script. It fails if its version disagrees with go.mod, and on any OS or architecture other than the Linux release runners. GOTOOLCHAIN=local on the release step keeps the verified toolchain from switching itself. Judgement call: release path only; script/bootstrap still uses the host Go. model: claude-opus-4-8 (implementation, review); claude-fable-5-1 (merge)
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
|