diff --git a/.github/actions/publish/action.yml b/.github/actions/publish/action.yml index 4c1597427..44366d9d8 100644 --- a/.github/actions/publish/action.yml +++ b/.github/actions/publish/action.yml @@ -63,6 +63,7 @@ runs: run: docker exec --env GITHUB_TOKEN --env HOMEBREW_DEPLOY_KEY + --env GOTOOLCHAIN=auto --workdir "$PWD" --tty "$CONTAINER_ID" diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..7eb7e9676 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,30 @@ +# golangci-lint v2 config reproducing the v1 defaults this repo linted with +# before the Go 1.26 bump (golangci-lint v1 cannot read Go 1.26 export data). +# Generated with `golangci-lint migrate` from an empty v1 config. +version: "2" +linters: + settings: + staticcheck: + # v1 ran staticcheck (SA) and gosimple (S1) by default; v2 merges + # stylecheck (ST) and quickfix (QF) into staticcheck. Keep the v1 set. + checks: + - "SA*" + - "S1*" + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/.goreleaser.yaml b/.goreleaser.yaml index cda90a022..d2a13a796 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -58,6 +58,12 @@ builds: # The real values are defined by the CC__ env vars defined at the top `env` attribute. - 'CC={{ index .Env (print "CC_" .Os "_" .Arch) }}' - 'CXX={{ index .Env (print "CXX_" .Os "_" .Arch) }}' + hooks: + post: + # Refuse to ship a binary built with an older Go than go.mod's toolchain + # directive (INC-289: Go 1.26.10 distrusts compromised eMudhra CAs). + - cmd: ./scripts/check-go-toolchain.sh "{{ .Path }}" + output: true dockers: # AMD64 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55acc6463..22ffe8eaa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ -# Cache bust: force rebuild for Go 1.26.0 compatibility +# Cache bust: golangci-lint v2.9.0 for Go 1.26.10 repos: - repo: https://github.com/golangci/golangci-lint - rev: v1.63.4 + rev: v2.9.0 hooks: - id: golangci-lint entry: golangci-lint run diff --git a/AGENTS.md b/AGENTS.md index 8e6787a2c..a7cee19ec 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -132,5 +132,5 @@ Installed via `make install-hooks`. Checks: ## Linting -- Go: `golangci-lint` (v1.63.4) via pre-commit +- Go: `golangci-lint` (v2.9.0) via pre-commit - Frontend: ESLint + Prettier diff --git a/go.mod b/go.mod index 1c4698e07..e97c4f63a 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/launchdarkly/ldcli go 1.25.0 +toolchain go1.26.10 + require ( github.com/adrg/frontmatter v0.2.0 github.com/adrg/xdg v0.5.3 diff --git a/scripts/check-go-toolchain.sh b/scripts/check-go-toolchain.sh new file mode 100755 index 000000000..ec388ab6b --- /dev/null +++ b/scripts/check-go-toolchain.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Fails the build if a binary was not compiled with the toolchain pinned in +# go.mod. Run by goreleaser as a post-build hook, so a release built with an +# older Go (e.g. one missing crypto/x509 security fixes) never gets published. +set -euo pipefail + +binary="$1" +want="$(awk '$1 == "toolchain" { print $2 }' go.mod)" +got="$(go version "$binary" | awk '{ print $NF }')" + +if [ -z "$want" ]; then + echo "check-go-toolchain: no toolchain directive in go.mod" >&2 + exit 1 +fi + +if [ "$got" != "$want" ]; then + echo "check-go-toolchain: $binary was built with $got, go.mod pins $want" >&2 + exit 1 +fi + +echo "check-go-toolchain: $binary built with $got"