Skip to content

ci: share the module check between CI and Security via a composite action - #262

Closed
Syndic wants to merge 1 commit into
mainfrom
ci/extract-check-modules-action
Closed

ci: share the module check between CI and Security via a composite action#262
Syndic wants to merge 1 commit into
mainfrom
ci/extract-check-modules-action

Conversation

@Syndic

@Syndic Syndic commented Aug 22, 2026

Copy link
Copy Markdown
Owner

What

ci.yml and security.yml each carried their own copy of the modules-check job's steps. This
extracts them to .github/actions/check-modules and points both jobs at it.

Why

Both workflows need check_modules.py — CI gates golangci-lint's module matrix on it,
security.yml gates govulncheck's and pip-audit's — and jobs can't be depended on across
workflows, so both have to run it.

The problem wasn't the duplicated run, it was the duplicated definition: each copy restated the
Python pin and two action SHAs, and nothing failed if they diverged. The check could silently have
run under a different interpreter in each workflow.

This does not make it run once. The check still runs twice per PR, in parallel, ~10s each
(measured on run 32598382377: both jobs started 21:01:34Z, finished 8s and 10s later). A
reusable workflow wouldn't have changed that either — GitHub instantiates a called workflow's jobs
into each caller's run and dedupes nothing. Merging the two workflow files is the only thing that
would, and it would cost the independent concurrency groups plus force every CI-side job behind an
if: gate — and an if:-gated required check reports as skipped, which branch protection counts
as passed. That's the failure codeql-all's if: always() and //meta/scripts:test_codeql_toolchain
already exist to prevent, so it isn't a trade worth making for 10 seconds.

Reviewer notes

The Renovate change is the load-bearing part. The marker-driven customManager is scoped by file
path to ^\.github/workflows/.*\.ya?ml$. Moving the pinned python-version: "3.14" into
.github/actions/ would have taken it out of Renovate's view entirely — and per README's
"Marker-driven", that failure is silent and has already happened twice in this repo. renovate.json
therefore widens the pattern to also claim ^\.github/actions/.*/action\.ya?ml$.

Per the README's "confirm it claims the sites you expect and nothing else", I simulated the
manager over the tree. It picks up the new site as python 3.14 and adds nothing else: the two
existing action.yml files (commit-file-via-app, setup-bazel-remote) carry no # renovate:
markers.

test_check_modules_action guards three couplings, all of which fail silently — a caller that
re-inlines the steps, a pattern that stops reaching the pin, and a pin that diverges from the four
ci.yml sites still carrying one. Same shape as test_precommit_docs / test_codeql_toolchain: no
script half, rides bazel test //..., costs no CI job.

Also relocated ci.yml's Python-pin comment block above go-work-check, since the job it sat on no
longer has a pin.

Validation

  • bazel test //... — 25/25 pass; gazelle produced no diff.
  • Each of the three couplings mutated and confirmed red, then restored: narrowing the Renovate
    pattern, re-inlining the step in ci.yml, and setting the action's pin to 3.13.
  • check_modules.py passes against the edited workflows; ruff format --check, ruff check, and
    ty check clean repo-wide.
  • Both workflows and the action parsed with PyYAML — composite structure valid, and modules-check
    resolves to exactly [checkout, ./.github/actions/check-modules] in both.

🤖 Generated with Claude Code

…tion

ci.yml and security.yml both need `check_modules.py` — CI gates golangci-lint's
module matrix on it, security.yml gates govulncheck's and pip-audit's — and jobs
cannot be depended on across workflows, so both must run it. Each carried its own
copy of the steps, restating the Python pin and two action SHAs with nothing
holding the two together: a diverged copy would have run the same check under a
different interpreter in each workflow, silently.

Extract the steps to `.github/actions/check-modules`. This shares the definition,
not the execution — the check still runs twice per PR, in parallel, ~10s each. A
reusable workflow would not have changed that either; GitHub instantiates a called
workflow's jobs into each caller's run and dedupes nothing.

The pin moves out of `.github/workflows/` with the steps, so renovate.json's
marker-driven customManager widens to `.github/actions/*/action.yml`. Verified the
widened pattern claims the new site and adds nothing else: the two existing
action.yml files carry no `# renovate:` markers.

`test_check_modules_action` guards both couplings, each of which fails silently —
a caller that re-inlines the steps, and a pattern that stops reaching the pin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Syndic
Syndic force-pushed the ci/extract-check-modules-action branch from 64b9444 to 17c41e8 Compare August 22, 2026 21:50
@Syndic

Syndic commented Aug 23, 2026

Copy link
Copy Markdown
Owner Author

Superseded. Rather than share the module check between the two workflows, we're dropping it from security.yml entirely: check_modules.py is a pure function of the tree, so it belongs in the change-driven workflow only, and ci.yml's copy already validates security.yml's own matrices (the check globs every workflow file). With one caller the composite action would be pure indirection. Replacement PR to follow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant