-
Notifications
You must be signed in to change notification settings - Fork 172
ci: split Go unit test job into parallel shards #19702
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,12 @@ SILENT ?= @ | |||||||||||||
| # TODO: [ROX-19070] Update postgres store test generation to work for foreign keys | ||||||||||||||
| UNIT_TEST_IGNORE := "stackrox/rox/sensor/tests|stackrox/rox/operator/tests|stackrox/rox/central/reports/config/store/postgres|stackrox/rox/central/complianceoperator/v2/scanconfigurations/store/postgres|stackrox/rox/central/auth/store/postgres|stackrox/rox/scanner/e2etests" | ||||||||||||||
|
|
||||||||||||||
| # UNIT_TEST_PKG_INCLUDE/EXCLUDE allow filtering the package list for parallel CI sharding. | ||||||||||||||
| # UNIT_TEST_PKG_INCLUDE: grep -E pattern to include only matching packages (default: match all) | ||||||||||||||
| # UNIT_TEST_PKG_EXCLUDE: grep -Ev pattern to exclude matching packages (default: match none) | ||||||||||||||
| UNIT_TEST_PKG_INCLUDE ?= . | ||||||||||||||
| UNIT_TEST_PKG_EXCLUDE ?= ^$$ | ||||||||||||||
|
|
||||||||||||||
| GOBUILD := $(CURDIR)/scripts/go-build.sh | ||||||||||||||
| DOCKERBUILD := $(CURDIR)/scripts/docker-build.sh | ||||||||||||||
| GO_TEST_OUTPUT_PATH=$(CURDIR)/test-output/test.log | ||||||||||||||
|
|
@@ -533,9 +539,12 @@ test-prep: | |||||||||||||
| go-unit-tests: build-prep test-prep | ||||||||||||||
| set -o pipefail ; \ | ||||||||||||||
| CGO_ENABLED=1 GOEXPERIMENT=cgocheck2 MUTEX_WATCHDOG_TIMEOUT_SECS=30 GOTAGS=$(GOTAGS),test scripts/go-test.sh -timeout 25m -race -cover -coverprofile test-output/coverage.out -v \ | ||||||||||||||
| $(shell git ls-files -- '*_test.go' | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list| grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE)) \ | ||||||||||||||
| $(shell git ls-files -- '*_test.go' | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list| grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE) | grep -E "$(UNIT_TEST_PKG_INCLUDE)" | grep -Ev "$(UNIT_TEST_PKG_EXCLUDE)") \ | ||||||||||||||
| | tee $(GO_TEST_OUTPUT_PATH) | ||||||||||||||
| # Exercise the logging package for all supported logging levels to make sure that initialization works properly | ||||||||||||||
|
|
||||||||||||||
| # Exercise the logging package for all supported logging levels to make sure that initialization works properly. | ||||||||||||||
| .PHONY: go-log-level-tests | ||||||||||||||
| go-log-level-tests: | ||||||||||||||
|
Comment on lines
+545
to
+547
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: The new When this logic lived under
Suggested change
|
||||||||||||||
| @echo "Run log tests" | ||||||||||||||
| for encoding in console json; do \ | ||||||||||||||
| for level in debug info warn error fatal panic; do \ | ||||||||||||||
|
|
@@ -555,12 +564,12 @@ sensor-pipeline-benchmark: build-prep test-prep | |||||||||||||
| go-postgres-unit-tests: build-prep test-prep | ||||||||||||||
| set -o pipefail ; \ | ||||||||||||||
| CGO_ENABLED=1 GOEXPERIMENT=cgocheck2 MUTEX_WATCHDOG_TIMEOUT_SECS=30 GOTAGS=$(GOTAGS),test,sql_integration scripts/go-test.sh -timeout 15m -race -cover -coverprofile test-output/coverage.out -v \ | ||||||||||||||
| $(shell git grep -rl "//go:build sql_integration" central pkg tools | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list -tags sql_integration | grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE)) \ | ||||||||||||||
| $(shell git grep -rl "//go:build sql_integration" central pkg tools | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list -tags sql_integration | grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE) | grep -E "$(UNIT_TEST_PKG_INCLUDE)" | grep -Ev "$(UNIT_TEST_PKG_EXCLUDE)") \ | ||||||||||||||
| | tee $(GO_TEST_OUTPUT_PATH) | ||||||||||||||
| @# The -p 1 passed to go test is required to ensure that tests of different packages are not run in parallel, so as to avoid conflicts when interacting with the DB. | ||||||||||||||
| set -o pipefail ; \ | ||||||||||||||
| CGO_ENABLED=1 GOEXPERIMENT=cgocheck2 MUTEX_WATCHDOG_TIMEOUT_SECS=30 GOTAGS=$(GOTAGS),test,sql_integration scripts/go-test.sh -p 1 -race -cover -coverprofile test-output/migrator-coverage.out -v \ | ||||||||||||||
| $(shell git grep -rl "//go:build sql_integration" migrator | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list -tags sql_integration | grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE)) \ | ||||||||||||||
| $(shell git grep -rl "//go:build sql_integration" migrator | sed -e 's@^@./@g' | xargs -n 1 dirname | sort | uniq | xargs go list -tags sql_integration | grep -v '^github.com/stackrox/rox/tests$$' | grep -Ev $(UNIT_TEST_IGNORE) | grep -E "$(UNIT_TEST_PKG_INCLUDE)" | grep -Ev "$(UNIT_TEST_PKG_EXCLUDE)") \ | ||||||||||||||
| | tee -a $(GO_TEST_OUTPUT_PATH) | ||||||||||||||
|
|
||||||||||||||
| .PHONY: go-postgres-bench-tests | ||||||||||||||
|
|
@@ -595,7 +604,7 @@ ui-component-tests: | |||||||||||||
| make -C ui test-component | ||||||||||||||
|
|
||||||||||||||
| .PHONY: test | ||||||||||||||
| test: go-unit-tests ui-test shell-unit-tests | ||||||||||||||
| test: go-unit-tests go-log-level-tests ui-test shell-unit-tests | ||||||||||||||
|
|
||||||||||||||
| .PHONY: integration-unit-tests | ||||||||||||||
| integration-unit-tests: build-prep test-prep | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: stackrox/stackrox
Length of output: 3776
🏁 Script executed:
cat -n .github/workflows/unit-tests.yaml | head -150Repository: stackrox/stackrox
Length of output: 5288
🏁 Script executed:
Repository: stackrox/stackrox
Length of output: 7078
🏁 Script executed:
cat -n .github/workflows/unit-tests.yaml | tail -n +350Repository: stackrox/stackrox
Length of output: 8503
🏁 Script executed:
Repository: stackrox/stackrox
Length of output: 14667
🌐 Web query:
GitHub Actions matrix job outputs limitation last completed shard only💡 Result:
Yes, GitHub Actions matrix jobs have a limitation where job-level outputs (defined in jobs.<job_id>.outputs) are overwritten by each matrix shard, and dependent jobs only receive the output from the last completed shard. This is a known behavior since matrix jobs share the same job ID, and outputs are not preserved per shard. Official documentation on job outputs and matrix strategies does not mention per-shard output preservation, confirming the limitation. A 2023 runner PR (
#2477) aimed to support matrix context in output keys (e.g., outputs: ${{ matrix.os }}_result: ...), but server-side changes were pending, and no changelog entry confirms full rollout by 2026-03-30. Workarounds include: - Upload per-shard data as artifacts from matrix jobs, download and aggregate in a dependent job. - Use third-party actions like cloudposse/github-action-matrix-outputs-{write,read}. - Predefine fixed output names matching matrix values (not dynamic). Example artifact workaround: jobs: test: strategy: matrix: runner: [DEV, TEST] steps: - run: jq -cn --arg runner ${{ matrix.runner }} --arg outcome success '$ARGS.named' > outcome.json - uses: actions/upload-artifact@v4 with: name: outcome-${{ matrix.runner }} path: outcome.json aggregate: needs: test steps: - uses: actions/download-artifact@v4 - id: collect run: | jq -s . outcomes/*.json > all-outcomes.json echo "outcomes=$(cat all-outcomes.json)" >> $GITHUB_OUTPUTCitations:
Matrix job outputs capture only the last completed shard, not all shards.
For matrix jobs, GitHub Actions only captures the output from the last completed matrix job, not an aggregation of all. Since
go-unit-testsruns 8 parallel jobs (4 components × 2 gotags), thenew-jirasoutput will only reflect one shard's result, potentially missing Jira tickets created by other shards. The same applies togo-extra-testsandgo-postgres.Consider implementing an aggregation job that collects outputs from all matrix shards using artifact uploads and aggregation, rather than relying on the single final shard output.
🤖 Prompt for AI Agents