Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions .github/workflows/reusable-ci.yml
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 }}
Expand All @@ -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
Expand Down
20 changes: 20 additions & 0 deletions changelog.d/20261016-go-test-timeout-input.md
Original file line number Diff line number Diff line change
@@ -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=<value>`; `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.
Loading