From 98a9b653a9e5c2fff6b0b4d59880dbb57956467d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 15:42:53 +0000 Subject: [PATCH] feat(ci): add a go-test-timeout input to reusable-ci The go-test helper action runs go test with no -timeout, so Go's 10m per-binary default applied and callers could not raise it. The new input is passed as GOFLAGS=-timeout= on the Go test steps. The go-ci job cap moves from 40 to 60 minutes so a 30m test timeout still fires before the runner cancels the job. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015uvXsjoTL4866VhFevH7wW --- .github/workflows/reusable-ci.yml | 28 ++++++++++++++----- changelog.d/20261016-go-test-timeout-input.md | 20 +++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 changelog.d/20261016-go-test-timeout-input.md diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml index 90cb6403..a0c64e90 100644 --- a/.github/workflows/reusable-ci.yml +++ b/.github/workflows/reusable-ci.yml @@ -1,5 +1,5 @@ # file: .github/workflows/reusable-ci.yml -# version: 1.17.1 +# version: 1.18.0 # guid: reusable-ci-2025-09-24-core-workflow # last-edited: 2026-10-16 @@ -58,6 +58,15 @@ on: required: false type: string default: '' + go-test-timeout: + description: >- + Per-test-binary timeout for the Go test steps, as a Go duration (e.g. + 25m). Empty (default) keeps Go's own 10m. Passed as GOFLAGS=-timeout=... + because the go-test helper action has no timeout input. Values above + 30m need go-ci's timeout-minutes raised; see the comment there. + required: false + type: string + default: '' system-packages: description: 'Space-separated list of apt packages to install before build/test (e.g. libtag1-dev)' required: false @@ -569,12 +578,11 @@ jobs: fail-fast: false matrix: ${{ fromJSON(needs.load-config.outputs.go-matrix) }} runs-on: ${{ matrix.os }} - # 40, not 30: the go-test helper runs `go test -race` with no -timeout, so - # a hung test binary is killed by Go's default 10m. The cap must exceed - # setup (~4.5m) + the slowest measured suite (~20.5m) + that 10m, or the - # runner cancels the job before Go prints the goroutine dump naming the - # stuck test. - timeout-minutes: 40 + # The cap must exceed setup (~4.5m) + the slowest measured suite (~20.5m) + + # the per-binary test timeout (Go's default 10m, or the go-test-timeout + # input, supported up to 30m), or the runner cancels the job before Go + # prints the goroutine dump naming the stuck test. + timeout-minutes: 60 permissions: contents: read env: @@ -639,9 +647,13 @@ jobs: with: command: go-setup + # go test reads test flags such as -timeout from GOFLAGS; go build and + # go tool cover ignore flags they do not know, so this only reaches tests. - name: Test Go project if: ${{ !inputs.skip-tests }} uses: falkcorp/gha-ci-workflow-helpers@4b5f4d45d42c99d98ccf731624f2f4b91d849561 # v1.1.4 + env: + GOFLAGS: ${{ inputs.go-test-timeout != '' && format('-timeout={0}', inputs.go-test-timeout) || '' }} with: command: go-test coverage-threshold: ${{ inputs.coverage-threshold || needs.load-config.outputs.coverage-threshold }} @@ -651,6 +663,8 @@ jobs: - name: Generate Go coverage profile if: ${{ !inputs.skip-tests && hashFiles('.testcoverage.yml') != '' }} continue-on-error: true + env: + GOFLAGS: ${{ inputs.go-test-timeout != '' && format('-timeout={0}', inputs.go-test-timeout) || '' }} run: go test ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./... - name: Check Go test coverage diff --git a/changelog.d/20261016-go-test-timeout-input.md b/changelog.d/20261016-go-test-timeout-input.md new file mode 100644 index 00000000..41f0a3ee --- /dev/null +++ b/changelog.d/20261016-go-test-timeout-input.md @@ -0,0 +1,20 @@ +### Added + +#### `reusable-ci.yml` takes a `go-test-timeout` input + +The Go CI job's test step runs the `go-test` command of +`gha-ci-workflow-helpers`, whose embedded Python calls +`go test -v -race -coverprofile=... ./...` with no `-timeout`. Go's default of +10 minutes per test binary therefore applied, and a caller had no way to raise +it: a calling workflow cannot pass env into a reusable workflow's jobs, and the +action has no timeout input. `falkcorp/audiobook-organizer`'s `internal/database` +package runs 510–600s under `-race` there and was failing at the 10m mark. + +The new input is a Go duration (e.g. `25m`). When set, the Go test steps run +with `GOFLAGS=-timeout=`; `go test` takes test flags from `GOFLAGS`, and +`go build` / `go tool cover` ignore flags they do not know, so only the tests +see it. Empty (the default) leaves behavior unchanged. + +The Go CI job cap moves from 40 to 60 minutes so it stays above setup + the +slowest suite + the test timeout for values up to 30m; the comment beside it +states that invariant.