From 2105888b3451f28689892ce9ea1f2e4372d1f5ea Mon Sep 17 00:00:00 2001 From: Rex Raphael Date: Sat, 5 Sep 2026 10:38:48 -0500 Subject: [PATCH 1/2] feat(go-ci): let callers raise the lint timeout golangci-lint-action hardcodes --timeout=5m today. A caller with a large module and a cold build cache (setup-go only warms the module cache, not the build cache) can blow through that before context loading even finishes, and had no way to ask for more time. Add golangci-lint-timeout, defaulting to 5m so every existing caller keeps behaving exactly as it does now. --- .github/workflows/go-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index 08229c8..73a940c 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -22,6 +22,10 @@ on: golangci-lint-version: type: string default: 'v2.12.2' + golangci-lint-timeout: + description: 'Timeout passed to golangci-lint. Raise it for a large module or one with a cold build cache - setup-go only caches the module cache, not the build cache, so a big repo can typecheck every package and dependency from scratch within this window.' + type: string + default: '5m' gosec-version: type: string default: 'v2.28.0' @@ -331,7 +335,7 @@ jobs: with: version: ${{ inputs.golangci-lint-version }} working-directory: ${{ inputs.working-directory }} - args: --timeout=5m + args: --timeout=${{ inputs.golangci-lint-timeout }} verify: name: Verify From 15c8939c02e8828a5ed1f09f25db5f249a021af6 Mon Sep 17 00:00:00 2001 From: Rex Raphael Date: Sat, 5 Sep 2026 10:39:03 -0500 Subject: [PATCH 2/2] feat(go-ci): warm the build cache before lint golangci-lint reads dependency export data out of GOCACHE the same way a build does, and setup-go never populates that cache, only the module cache. Running go build first fills it in for lint to reuse on the same runner, which is most of why cold lint is slow on a large module, and it turns an unbuildable module into a clear compiler error instead of a lint timeout. Kept as its own commit so it can be dropped independently of the timeout input if it turns out not to be worth the cost on small callers. --- .github/workflows/go-ci.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index 73a940c..21549e9 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -330,6 +330,22 @@ jobs: check-latest: true cache-dependency-path: ${{ inputs.working-directory }}/go.mod + # golangci-lint's package loader gets export data for every dependency + # by reading GOCACHE, same as a plain build. setup-go's cache only + # covers the module download cache, so on a fresh runner GOCACHE starts + # empty and lint ends up compiling everything itself, uncached, inside + # the timeout above. Building here fills GOCACHE for lint to reuse a + # moment later on the same runner, and fails fast with a plain compiler + # error if the module does not build at all, instead of a confusing + # lint timeout. Costs a few seconds on a small module; saves much more + # on a large one. + - name: Warm build cache + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + set -euo pipefail + go build ./... + - name: Run golangci-lint uses: golangci/golangci-lint-action@v9 with: