Repository navigation
ci: skip the product jobs in CI for infra-only pull requests - #822
Merged
Merged
Conversation
Add a scope job to ci.yaml that calls the reusable change-scope workflow, and gate test, bench and conformance on its output. The jobs that depend on test keep the implicit success() and skip together with it. The gate is `!cancelled() && needs.scope.outputs.product != 'false'`, so a scope job that fails or returns nothing runs every product job; only a scope job that succeeds with false skips them. Pushes and manual runs always report true. The two aggregator jobs, e2e-compat and race-stress, run under always(). They pass when every group succeeded, or when the groups were skipped and the scope output is false. A skipped group on a product pull request (for example when test failed) or an empty scope output now fails them, so a skip never reads as a pass by accident. lint and helm have no condition and run on every pull request. lint now also carries the infra checks that used to sit in test: the observability asset sync check, the scripts/ci unit tests (plus the classifier and scope wiring tests) and the configuration reference check. Two new steps join them: the README support-policy test and scripts/ci/validate_compose.sh, which runs docker compose config -q on every tracked compose file (standalone files and each overlay on its base) and fails when a compose file is missing from its list. test_scope_wiring.py reads the workflows and asserts that lint and helm are never skipped, that every job reading the scope output lists scope in needs and uses the canonical gate, that jobs downstream of test cannot bypass success(), and that both aggregators test the scope output.
Contributor
PR Quality ReportCompared against base branch Coverage and tests
Compatibility
Performance smokePerformance smoke was skipped for this PR because no perf-sensitive paths changed. State
|
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pull requests that change only tooling (workflows, scripts, Dependabot, security tooling, test-stack compose files, docs) run about 160 job-minutes of product jobs today, although they cannot change the proxy. This PR makes
ci.yamlskip the product jobs when the change-scope classifier says no product path changed, and moves the checks that verify the infra itself into the always-onlintjob, so an infra pull request is still checked.Pushes to
main, scheduled runs, manual dispatches and pull requests labelledci-fullkeep running everything.Builds on #820 (merged), which added the classifier and the reusable
change-scope.yaml.Changes
.github/workflows/ci.yamlscopejob: calls./.github/workflows/change-scope.yamlwithcontents: readandpull-requests: read. The workflow's top-level permissions are unchanged. Theusesline carries azizmor: ignore[self-repository]comment: zizmor suggests the$/form for a same-repository call, and the pinned actionlint rejects it.test,benchandconformance(the product jobs with noneeds) gainneeds: [scope]andif: ${{ !cancelled() && needs.scope.outputs.product != 'false' }}. A scope job that fails leaves the output empty, so the gate runs the job. Only a scope job that succeeds withfalseskips anything.test(tuple-contract, fuzz-smoke, race-stress shards, tuple-smoke, docker, e2e-compat groups, e2e-fleet, e2e-ui, stress-tests, memory-leak-tests) get noif:. They keep the implicitsuccess(): they skip whentestskips, and they do not run whentestfails.if: always()aggregators,e2e-compat(required) andrace-stress, now also needscope. They pass when every group succeeded, or when the groups were skipped and the scope output isfalse. They fail otherwise, including whentestfailed on a product pull request and when the scope output is empty.lintandhelmhave noif:and run on every pull request.lintgains the infra checks, moved fromtestwith identical commands:sync_observability_assets.sh --check, thescripts.ci.testsunittest list (plustest_change_scopeandtest_scope_wiring) andgo run ./cmd/configdoc -check. It also gains two new steps:go test ./internal/logsql -run '^TestVictoriaLogsSupportPolicy$' -count=1(so README edits stay checked) andscripts/ci/validate_compose.sh. They run after gofmt and golangci-lint.scripts/ci/validate_compose.sh(new): runsdocker compose config -qon every tracked compose file, 9 standalone files and 6 overlays stacked on the files they are applied after (15 stacks), and fails if a trackeddocker-compose*.ymlorcompose*.ymlis not in its list or a listed file is missing. It starts no container and needs no daemon.scripts/ci/tests/test_scope_wiring.py(new): reads the workflows with a small stdlib parser (no PyYAML dependency) and asserts the wiring described below. It also mutates a copy ofci.yamlsix ways (ungatedtest, missingscopeinneeds, weaker gate, a dependent withif: always(), a condition onlint, an aggregator that ignores the scope output) and checks each is caught.Per-job behaviour
scope(new)linthelmtestbench,conformancetestis skippede2e-fleettestis skippedtestblocks as todaye2e-compat(aggregator)false, passesrace-stress(aggregator)Case walk-through
falsefalse)truetestfailstruetrue)!= 'false')scopefailed upstream and the implicitsuccess()covers the whole chainmain(also schedule, dispatch,ci-full)trueCase (d) fails closed:
test,benchandconformancerun, ande2e-compatgoes red, so the merge is blocked; the dependents oftestare not given anif:on purpose. The scope job rarely fails outright, because every step in it iscontinue-on-errorand its result step defaults totrue; a failure there means a runner-level problem.Case (c) is the one that matters:
e2e-compatandrace-stressusealways(), so without the explicit scope test a skipped group would read as a pass.Proofs
actionlint(v1.7.7 as in the Security workflow, and the local 1.7.12): no findings across all workflows.zizmor1.30.1--offline, same flags as the Security workflow: no findings; the one newself-repositoryhint on thescopecall is suppressed inline as above.python3 -m unittest discover -s scripts/ci/tests -p 'test_*.py': 127 tests pass, includingtest_workflow_pins,test_tool_pins,test_change_scopeand the 7 intest_scope_wiring.scripts/ci/validate_compose.shwith Compose v5.4.0: 15 of 15 stacks pass. It reports a tracked compose file that is missing from its list.sync_observability_assets.sh --check,go run ./cmd/configdoc -checkandTestVictoriaLogsSupportPolicypass.Verification on GitHub
scopereportingproduct=false, every product job skipped,e2e-compatandrace-stressgreen, andlintandhelmrunning. The step summary ofscopelists every path as infra.internal/shows every product job running with this workflow. Pushing one deliberately failing unit test to it should turntestred ande2e-compatred (its groups skip), and block the merge. The throwaway pull request is closed without merging.mainafter merge runs every job (scopereportstruefor a push).lintruns the compose validation with the runner's Compose version: confirm it passes there on this run.Not applicable
Infra-only pull request; product checks (e2e vs Loki, Grafana range matrix, visual, A/B, benchstat, load) do not apply.