chore(go): Move to Go 1.26 and split minimum from build toolchain - #88
Merged
Conversation
Go supports a release until two newer ones exist, and go1.27rc2 is already published -- so 1.25 stops receiving security backports the moment 1.27 ships. 1.26 is the line to be on. The version arrives as two directives rather than one because they answer different questions. `go 1.26` is the minimum this module supports, a policy that moves on Go's EOL schedule. `toolchain go1.26.5` is what builds it, and it names an exact patch because .goreleaser.yaml goes out of its way to make builds reproducible -- -trimpath and mod_timestamp exist so that two builds of one commit produce one checksum, and the toolchain version is stamped into the binary, so a compiler free to drift would undo that. The directive is a floor rather than a ceiling: a machine with a newer Go runs that instead. Releases are exact anyway, because setup-go installs the version the directive names and then sets GOTOOLCHAIN=local, which disables switching altogether -- so the guarantee arrives with the bump to setup-go v6 or newer (#67), and until then a release build tracks the newest 1.26 patch. Until now a single `go 1.25.5` served both roles, which is why a gosec release requiring go1.25.8 could redden CI: the workflows install the version go.mod names, so the project's declared minimum was also its build toolchain, and a third party's Go floor could move it. Separating the two ends that coupling. `go install` now requires Go 1.26. Released binaries are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CrknafJSP5hF8u865cbnqX
Pinning `toolchain` in go.mod keeps builds reproducible, and gives up the automatic patch upgrade that GOTOOLCHAIN=auto used to perform. Every Go patch release carries security fixes, and nothing in this repository bumps the pin, so without a signal it would sit at whatever version last looked current -- which is the failure this pin was introduced to make impossible, in a slower form. The check warns and exits zero. A new Go patch is not a defect here, and a red build every six weeks would teach everyone to skip past it; a warning arrives at the same moment and survives being ignored once. An unreachable go.dev skips the check rather than failing or claiming the pin is current. It lives in the Justfile because CI and a developer's machine should be running one implementation, which is already true of every other check here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CrknafJSP5hF8u865cbnqX
The README sent readers to `go install` without saying what it needs, which was survivable while the requirement was whatever Go they already had and is not now that the module asks for 1.26. The note also says what the release binaries need, because "requires Go" next to an install command reads as a requirement of the tool rather than of one way to obtain it. The --version sample said go1.25.5, which the toolchain pin has just made untrue of anything built from this commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CrknafJSP5hF8u865cbnqX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Released binaries were built with a Go toolchain eight months stale, missing seven patch releases of
crypto/tlsandnet/httpsecurity fixes.The published v0.2.0 artifact reports
go1.25.5, released 2025-12-02. Every release fromgo1.25.6throughgo1.25.12carried security fixes, several to the exact packages this tool is built on —crypto/tls,net/http,net/url.The cause is that
go.modpinned an exact patch and the workflows install whatever versiongo.modnames. One value served two unrelated roles: the minimum Go this module supports, and the compiler that builds and ships it. Nothing signalled when they diverged, becauseGOTOOLCHAIN=autoquietly downloaded a second toolchain to paper over the gap.That coupling also lets a third party's Go floor break the build. gosec raised its requirement to
go1.25.8, and #67 — which bumpsactions/setup-goto v7, whereGOTOOLCHAIN=localdisables the silent download — went red:Solution
Split
go.mod's single version into two directives:go 1.26for the minimum supported,toolchain go1.26.5for what actually builds.go 1.25.5go 1.26go 1.25.5(same value)toolchain go1.26.51.26 is the line to be on: Go supports a release until two newer ones exist, and
go1.27rc2is already published, so 1.25 stops receiving backports the moment 1.27 ships.Why an exact patch rather than
go-version: 'stable'..goreleaser.yamldeliberately pursues reproducible builds —-trimpathandmod_timestampexist so two builds of one commit produce one checksum. The Go version is stamped into the binary, so a compiler free to drift would undo that. Two builds at a fixed toolchain do produce identicalsha256; letting the compiler float would have quietly dismantled a property the config's own comments defend.The directive is a floor, not a ceiling. A machine with a newer Go runs that instead. Releases are exact anyway because setup-go installs the version the directive names and then sets
GOTOOLCHAIN=local, disabling switching altogether — so that guarantee arrives with #67, and until then a release build tracks the newest 1.26 patch.The pin cannot upgrade itself, which is the original failure in slower form.
just toolchain-checkreports when the pin trails its patch line, as a warning that exits zero — a new Go patch is not a defect in this repository, and a red build every six weeks teaches everyone to skip past it. It lives in theJustfileso CI and a developer's machine run one implementation, as every other check here already does.Together these unblock #67:
1.26.5clears gosec's floor, so that PR goes green on a rebase with no edits to it.Other Changes
README.mdnow states the Go requirement on the source-install section, which previously sent readers togo installwithout saying what it needed. The--versionsample'sgo1.25.5was refreshed, since the pin makes it untrue of anything built from this commit.version_test.gokeeps itsgo1.25.5fixtures deliberately: they are supplieddebug.BuildInfoinputs, not reads of the live toolchain, so changing them would be diff noise.Related:
🤖 Generated with Claude Code