Skip to content

ci: require a single aggregate gate job per workflow#12453

Merged
zkochan merged 1 commit into
mainfrom
fix/rust-ci-required-test-checks-skip
Jun 16, 2026
Merged

ci: require a single aggregate gate job per workflow#12453
zkochan merged 1 commit into
mainfrom
fix/rust-ci-required-test-checks-skip

Conversation

@zkochan

@zkochan zkochan commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

PRs that touch no Rust files (e.g. #12287) get stuck as not mergeable, blocked on required checks that are never reported.

Branch protection requires the individual Rust CI Test jobs:

  • Rust CI / Test / ubuntu
  • Rust CI / Test / windows
  • Rust CI / Test / macos

These come from a single matrix job gated to skip on non-Rust PRs. When a matrix job is skipped at the job level, GitHub does not expand the matrix — it reports a single check under the literal, unexpanded name:

Rust CI / Test / ${{ matrix.os_label }}   (skipping)

So the three required per-OS contexts never appear, and the PR waits on them forever.

Fix

Add one static-named aggregate gate per workflow — the canonical pattern for required matrix checks:

  • Rust CI / Successneeds every Rust job (changes, test, clippy, doc, typos, deny, format, dylint).
  • TS CI / Successneeds compile-and-lint, test-smoke, test.

Each runs with if: ${{ always() }} so it reports under a static name in every state, and fails only when a dependency actually failed or was cancelled (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')). Skipped dependencies count as a pass — a skipped job means "not applicable to this PR". No third-party action is used, keeping the merge gate's supply-chain surface minimal.

The TS CI same-repo bypass, and how the gate avoids it

TS CI runs on both push and pull_request. On a same-repo PR the pull_request run skips compile-and-lint (the dedup if: guard), cascading skips to the whole matrix. A naive gate would report a green TS CI / Success on that skipped run and satisfy branch protection before the real push run finished — letting a PR merge without CI actually running.

To prevent this, the gate's name is TS CI / Success only on runs that do real work, and a different name (TS CI / Success (skipped duplicate)) on the same-repo pull_request run that gets deduplicated:

name: ${{ (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') && 'TS CI / Success' || 'TS CI / Success (skipped duplicate)' }}

The condition is compile-and-lint's if: verbatim, so the required TS CI / Success context is emitted only by real runs (same-repo push, fork pull_request, chore/update-lockfile PRs, main push). Forks keep working (their real run is the pull_request event). The two spots carry a comment noting the name and the if: must stay in sync.

Rust CI needs no such guard: its push trigger is restricted to main, so a PR produces a single run with no concurrent run to shadow it.

Required follow-up (branch protection)

One-time change on main, landed in lockstep with the merge. Replace the per-job contexts:

  • Rust CI / Clippy, Rust CI / Dylint, Rust CI / Doc, Rust CI / Spell Check, Rust CI / Test / ubuntu, Rust CI / Test / windows, Rust CI / Test / macos
  • TS CI / Compile & Lint, TS CI / Test / windows / Node 22, TS CI / Test / ubuntu / Node 22, TS CI / Test / ubuntu / Node 24, TS CI / Test / ubuntu / Node 26

with just:

  • Rust CI / Success
  • TS CI / Success

Note: the Success gates also fold in jobs that were not previously required individually (Cargo Deny, Format), so a failure in those will now block merge too.


Written by an agent (Claude Code, claude-opus-4-8).

Summary by CodeRabbit

  • Chores
    • Enhanced Continuous Integration pipeline reliability with unified, always-running success gates that aggregate results across Rust and TypeScript checks.
    • Updated workflow trigger/comment behavior and required-gate alignment to prevent duplicate runs from incorrectly blocking branch protection.
    • Success gating now treats skipped dependency checks as passing, while still failing when any required check fails or is cancelled.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0)

Grey Divider


Action required

1. TS CI success bypass 🐞 Bug ☼ Reliability
Description
TS CI / Success always runs and treats skipped dependencies as a pass, so for same-repo
pull_request events where compile-and-lint is skipped, it can report success even though no TS
compile/lint/tests ran. If branch protection is updated to require only this context (per the new
comment), same-repo PRs can be merged without waiting for the push-based CI run that actually
executes the test matrix.
Code

.github/workflows/ci.yml[R94-105]

+  success:
+    name: TS CI / Success
+    if: ${{ always() }}
+    needs:
+      - compile-and-lint
+      - test-smoke
+      - test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Fail if any dependency failed or was cancelled
+        if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+        run: exit 1
Evidence
On same-repo pull_request events, compile-and-lint is skipped by an if: guard, and
test-smoke/test depend on it, so they will not run; the newly-added success job still runs due
to always() and explicitly treats skipped dependencies as a pass by only failing on
failure/cancelled results.

.github/workflows/ci.yml[9-14]
.github/workflows/ci.yml[42-46]
.github/workflows/ci.yml[88-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new aggregate job `TS CI / Success` runs unconditionally (`if: always()`) and only fails when dependencies are `failure`/`cancelled`. On same-repo `pull_request` events, `compile-and-lint` is intentionally skipped, which causes `test-smoke` and `test` to be skipped as well; the aggregate job still reports success, creating a potential merge-gate bypass if this context is used as the only required check.
## Issue Context
`compile-and-lint` already has an `if:` guard to avoid duplicate same-repo PR runs; the aggregate job should follow the same guard (or otherwise ensure it does not produce a passing required check when the actual CI did not run).
## Fix Focus Areas
- .github/workflows/ci.yml[9-14]
- .github/workflows/ci.yml[88-105]
### Concrete fix
Update the `success` job to use the same `if:` condition as `compile-and-lint` (while still using `always()` semantics for dependency failures within that scope), e.g.:
- `if: ${{ always() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') }}`
This ensures same-repo PR events don’t get a green `TS CI / Success` without running the underlying TS jobs, while push events still produce the required context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing rust-test path filter 🐞 Bug ☼ Reliability
Description
The changes job’s paths-filter omits .github/actions/rust-test/**, so PRs that only modify the
new rust-test composite action will leave needs.changes.outputs.rust false and skip all Rust CI
jobs. This allows changes to the shared test runner logic to merge without actually executing the
tests/artifact staging behavior it defines.
Code

.github/workflows/pacquet-ci.yml[R84-86]

+      - uses: ./.github/actions/rust-test
     with:
-          save-cache: ${{ github.ref_name == 'main' }}
+          os: ubuntu-latest
Evidence
The workflow’s paths-filter list does not include the new composite action directory, while the
required per-OS test jobs invoke ./.github/actions/rust-test; therefore changes limited to the
action won’t flip the rust output and will skip those jobs.

.github/workflows/pacquet-ci.yml[47-64]
.github/workflows/pacquet-ci.yml[75-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Rust change-detection filter in `.github/workflows/pacquet-ci.yml` does not include the newly introduced composite action directory `.github/actions/rust-test/**`. As a result, PRs that only change that action will not be treated as “Rust changes”, and all Rust CI jobs (including the required Test jobs) will be skipped.
### Issue Context
The workflow now runs tests via `uses: ./.github/actions/rust-test`, but the detector only watches `rustup` and `binstall` action directories.
### Fix Focus Areas
- .github/workflows/pacquet-ci.yml[47-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 22aa26a

Results up to commit 76e6b68


🐞 Bugs (2) 📘 Rule violations (0)


Action required
1. TS CI success bypass 🐞 Bug ☼ Reliability
Description
TS CI / Success always runs and treats skipped dependencies as a pass, so for same-repo
pull_request events where compile-and-lint is skipped, it can report success even though no TS
compile/lint/tests ran. If branch protection is updated to require only this context (per the new
comment), same-repo PRs can be merged without waiting for the push-based CI run that actually
executes the test matrix.
Code

.github/workflows/ci.yml[R94-105]

+  success:
+    name: TS CI / Success
+    if: ${{ always() }}
+    needs:
+      - compile-and-lint
+      - test-smoke
+      - test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Fail if any dependency failed or was cancelled
+        if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+        run: exit 1
Evidence
On same-repo pull_request events, compile-and-lint is skipped by an if: guard, and
test-smoke/test depend on it, so they will not run; the newly-added success job still runs due
to always() and explicitly treats skipped dependencies as a pass by only failing on
failure/cancelled results.

.github/workflows/ci.yml[9-14]
.github/workflows/ci.yml[42-46]
.github/workflows/ci.yml[88-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new aggregate job `TS CI / Success` runs unconditionally (`if: always()`) and only fails when dependencies are `failure`/`cancelled`. On same-repo `pull_request` events, `compile-and-lint` is intentionally skipped, which causes `test-smoke` and `test` to be skipped as well; the aggregate job still reports success, creating a potential merge-gate bypass if this context is used as the only required check.
## Issue Context
`compile-and-lint` already has an `if:` guard to avoid duplicate same-repo PR runs; the aggregate job should follow the same guard (or otherwise ensure it does not produce a passing required check when the actual CI did not run).
## Fix Focus Areas
- .github/workflows/ci.yml[9-14]
- .github/workflows/ci.yml[88-105]
### Concrete fix
Update the `success` job to use the same `if:` condition as `compile-and-lint` (while still using `always()` semantics for dependency failures within that scope), e.g.:
- `if: ${{ always() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') }}`
This ensures same-repo PR events don’t get a green `TS CI / Success` without running the underlying TS jobs, while push events still produce the required context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing rust-test path filter 🐞 Bug ☼ Reliability
Description
The changes job’s paths-filter omits .github/actions/rust-test/**, so PRs that only modify the
new rust-test composite action will leave needs.changes.outputs.rust false and skip all Rust CI
jobs. This allows changes to the shared test runner logic to merge without actually executing the
tests/artifact staging behavior it defines.
Code

.github/workflows/pacquet-ci.yml[R84-86]

+      - uses: ./.github/actions/rust-test
      with:
-          save-cache: ${{ github.ref_name == 'main' }}
+          os: ubuntu-latest
Evidence
The workflow’s paths-filter list does not include the new composite action directory, while the
required per-OS test jobs invoke ./.github/actions/rust-test; therefore changes limited to the
action won’t flip the rust output and will skip those jobs.

.github/workflows/pacquet-ci.yml[47-64]
.github/workflows/pacquet-ci.yml[75-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Rust change-detection filter in `.github/workflows/pacquet-ci.yml` does not include the newly introduced composite action directory `.github/actions/rust-test/**`. As a result, PRs that only change that action will not be treated as “Rust changes”, and all Rust CI jobs (including the required Test jobs) will be skipped.
### Issue Context
The workflow now runs tests via `uses: ./.github/actions/rust-test`, but the detector only watches `rustup` and `binstall` action directories.
### Fix Focus Areas
- .github/workflows/pacquet-ci.yml[47-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit ae4bfe5


🐞 Bugs (2) 📘 Rule violations (0)


Action required
1. TS CI success bypass 🐞 Bug ☼ Reliability ⭐ New
Description
TS CI / Success always runs and treats skipped dependencies as a pass, so for same-repo
pull_request events where compile-and-lint is skipped, it can report success even though no TS
compile/lint/tests ran. If branch protection is updated to require only this context (per the new
comment), same-repo PRs can be merged without waiting for the push-based CI run that actually
executes the test matrix.
Code

.github/workflows/ci.yml[R94-105]

+  success:
+    name: TS CI / Success
+    if: ${{ always() }}
+    needs:
+      - compile-and-lint
+      - test-smoke
+      - test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Fail if any dependency failed or was cancelled
+        if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+        run: exit 1
Evidence
On same-repo pull_request events, compile-and-lint is skipped by an if: guard, and
test-smoke/test depend on it, so they will not run; the newly-added success job still runs due
to always() and explicitly treats skipped dependencies as a pass by only failing on
failure/cancelled results.

.github/workflows/ci.yml[9-14]
.github/workflows/ci.yml[42-46]
.github/workflows/ci.yml[88-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new aggregate job `TS CI / Success` runs unconditionally (`if: always()`) and only fails when dependencies are `failure`/`cancelled`. On same-repo `pull_request` events, `compile-and-lint` is intentionally skipped, which causes `test-smoke` and `test` to be skipped as well; the aggregate job still reports success, creating a potential merge-gate bypass if this context is used as the only required check.

## Issue Context
`compile-and-lint` already has an `if:` guard to avoid duplicate same-repo PR runs; the aggregate job should follow the same guard (or otherwise ensure it does not produce a passing required check when the actual CI did not run).

## Fix Focus Areas
- .github/workflows/ci.yml[9-14]
- .github/workflows/ci.yml[88-105]

### Concrete fix
Update the `success` job to use the same `if:` condition as `compile-and-lint` (while still using `always()` semantics for dependency failures within that scope), e.g.:
- `if: ${{ always() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') }}`
This ensures same-repo PR events don’t get a green `TS CI / Success` without running the underlying TS jobs, while push events still produce the required context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing rust-test path filter 🐞 Bug ☼ Reliability
Description
The changes job’s paths-filter omits .github/actions/rust-test/**, so PRs that only modify the
new rust-test composite action will leave needs.changes.outputs.rust false and skip all Rust CI
jobs. This allows changes to the shared test runner logic to merge without actually executing the
tests/artifact staging behavior it defines.
Code

.github/workflows/pacquet-ci.yml[R84-86]

+      - uses: ./.github/actions/rust-test
       with:
-          save-cache: ${{ github.ref_name == 'main' }}
+          os: ubuntu-latest
Evidence
The workflow’s paths-filter list does not include the new composite action directory, while the
required per-OS test jobs invoke ./.github/actions/rust-test; therefore changes limited to the
action won’t flip the rust output and will skip those jobs.

.github/workflows/pacquet-ci.yml[47-64]
.github/workflows/pacquet-ci.yml[75-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Rust change-detection filter in `.github/workflows/pacquet-ci.yml` does not include the newly introduced composite action directory `.github/actions/rust-test/**`. As a result, PRs that only change that action will not be treated as “Rust changes”, and all Rust CI jobs (including the required Test jobs) will be skipped.
### Issue Context
The workflow now runs tests via `uses: ./.github/actions/rust-test`, but the detector only watches `rustup` and `binstall` action directories.
### Fix Focus Areas
- .github/workflows/pacquet-ci.yml[47-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 3a6e0c9


🐞 Bugs (1) 📘 Rule violations (0)


Action required
1. Missing rust-test path filter 🐞 Bug ☼ Reliability
Description
The changes job’s paths-filter omits .github/actions/rust-test/**, so PRs that only modify the
new rust-test composite action will leave needs.changes.outputs.rust false and skip all Rust CI
jobs. This allows changes to the shared test runner logic to merge without actually executing the
tests/artifact staging behavior it defines.
Code

.github/workflows/pacquet-ci.yml[R84-86]

+      - uses: ./.github/actions/rust-test
        with:
-          save-cache: ${{ github.ref_name == 'main' }}
+          os: ubuntu-latest
Evidence
The workflow’s paths-filter list does not include the new composite action directory, while the
required per-OS test jobs invoke ./.github/actions/rust-test; therefore changes limited to the
action won’t flip the rust output and will skip those jobs.

.github/workflows/pacquet-ci.yml[47-64]
.github/workflows/pacquet-ci.yml[75-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Rust change-detection filter in `.github/workflows/pacquet-ci.yml` does not include the newly introduced composite action directory `.github/actions/rust-test/**`. As a result, PRs that only change that action will not be treated as “Rust changes”, and all Rust CI jobs (including the required Test jobs) will be skipped.

### Issue Context
The workflow now runs tests via `uses: ./.github/actions/rust-test`, but the detector only watches `rustup` and `binstall` action directories.

### Fix Focus Areas
- .github/workflows/pacquet-ci.yml[47-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Both .github/workflows/ci.yml and .github/workflows/pacquet-ci.yml gain a new success job that serves as an aggregate gate for their respective CI suites. Each job runs unconditionally, depends on all required checks, fails only when a dependency reports failure or cancellation, and uses conditional naming to distinguish real runs from skipped duplicates. Header comments document the unified design.

Changes

Unified Aggregate Success Gates for CI Workflows

Layer / File(s) Summary
TypeScript CI success gate
.github/workflows/ci.yml
Header comments updated to document the deduplication condition keeping compile-and-lint skip logic in sync with the success job's display name. New success job added with if: always(), needs on compile-and-lint, test-smoke, and test, conditionally named to distinguish real CI from skipped duplicates, and fails only when any dependency result is failure or cancelled.
Rust CI success gate
.github/workflows/pacquet-ci.yml
Header comments updated to explain path filtering is excluded so the success gate always runs. New success job added with if: always(), needs on changes, test, clippy, doc, typos, deny, format, and dylint, and exits with failure if any dependency reports failure or cancelled.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

  • pnpm/pnpm#12415: Modifies the same pacquet-ci.yml workflow's gating logic for Rust CI jobs, which directly interacts with the new success gate's dependency chain.
  • pnpm/pnpm#12389: Refactored the clippy job in pacquet-ci.yml, which is now a direct dependency of the new Rust CI success gate.
  • pnpm/pnpm#12417: Modifies workflow job naming in both ci.yml and pacquet-ci.yml, aligning with the display names used by the new aggregate success gates.

Suggested reviewers

  • KSXGitHub

Poem

🐇 Two gates now guard the CI,
One for types, one for Rust so bright,
Each waits for all to pass or die,
If skipped? They beam with gentle light! ✨
One check, one truth, unified might.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing an aggregate gate job per workflow to fix branch protection issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rust-ci-required-test-checks-skip

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix required Rust CI Test checks by splitting matrix into per-OS jobs
🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

Walkthroughs

Description
• Replace the Rust Test matrix with three static per-OS required-check jobs
• Extract shared Rust test steps into a reusable composite action
• Ensure non-Rust PRs report required contexts as skipped (not missing)
Diagram
graph TD
  A[".github/workflows/pacquet-ci.yml"] --> B["changes job (rust flag)"] --> C["test-ubuntu job"] --> F["rust-test composite action"] --> G[("ci-performance-rust-<os> artifact")]
  B --> D["test-windows job"] --> F
  B --> E["test-macos job"] --> F
  H{{"Branch protection: required checks"}} --> C
  H --> D
  H --> E
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep matrix; move gating from job `if:` to step-level `if:`
  • ➕ Retains a single job definition and avoids job duplication
  • ➕ Matrix contexts would always expand, so per-OS check names exist
  • ➖ Still schedules runners for non-Rust PRs unless carefully engineered to exit early
  • ➖ Harder to guarantee the job reports as 'skipped' vs 'success' and can still waste CI minutes
2. Add three lightweight wrapper jobs for required contexts
  • ➕ Preserves required-check names with minimal duplication
  • ➕ Wrappers could decide to skip or pass quickly based on changes output
  • ➖ Adds indirection (wrappers + inner implementation) and makes logs/navigation more complex
  • ➖ May still hit naming inconsistencies if wrappers call reusable workflows and get skipped

Recommendation: The chosen approach (three static per-OS jobs + a shared composite action) is the most robust way to satisfy branch-protection required check naming in GitHub Actions. It guarantees the exact required contexts exist whether the jobs run or are skipped, while keeping the implementation DRY via the composite action.

Grey Divider

File Changes

Other (2)
action.yml Add composite action for Rust test + Bencher artifact staging +122/-0

Add composite action for Rust test + Bencher artifact staging

• Introduces a new 'rust-test' composite action that encapsulates the Rust test pipeline: frees disk on Linux runners, installs toolchains and dependencies, runs pacquet/pnpr tests with timing capture, and uploads the 'ci-performance-rust-<os>' artifact.

.github/actions/rust-test/action.yml


pacquet-ci.yml Split Rust Test matrix into static per-OS required-check jobs +34/-114

Split Rust Test matrix into static per-OS required-check jobs

• Replaces the single matrix 'test' job with three jobs ('test-ubuntu', 'test-windows', 'test-macos') that have static names matching branch protection required contexts. Each job keeps the same 'needs: changes' and 'if:' gating and delegates its implementation to the new './.github/actions/rust-test' composite action.

.github/workflows/pacquet-ci.yml


Grey Divider

Qodo Logo

Comment thread .github/workflows/pacquet-ci.yml Outdated
Comment on lines +84 to +86
- uses: ./.github/actions/rust-test
with:
save-cache: ${{ github.ref_name == 'main' }}
os: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Missing rust-test path filter 🐞 Bug ☼ Reliability

The changes job’s paths-filter omits .github/actions/rust-test/**, so PRs that only modify the
new rust-test composite action will leave needs.changes.outputs.rust false and skip all Rust CI
jobs. This allows changes to the shared test runner logic to merge without actually executing the
tests/artifact staging behavior it defines.
Agent Prompt
### Issue description
The Rust change-detection filter in `.github/workflows/pacquet-ci.yml` does not include the newly introduced composite action directory `.github/actions/rust-test/**`. As a result, PRs that only change that action will not be treated as “Rust changes”, and all Rust CI jobs (including the required Test jobs) will be skipped.

### Issue Context
The workflow now runs tests via `uses: ./.github/actions/rust-test`, but the detector only watches `rustup` and `binstall` action directories.

### Fix Focus Areas
- .github/workflows/pacquet-ci.yml[47-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer applicable — this PR was reworked to use a single Rust CI / Success aggregate gate instead of per-OS jobs, so the .github/actions/rust-test composite action was removed. The change-detection paths-filter is unchanged from main.


Written by an agent (Claude Code, claude-opus-4-8).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/actions/rust-test/action.yml (1)

100-114: 💤 Low value

Heredoc produces leading whitespace in metadata.json.

The indented heredoc content will include leading spaces in the generated JSON file. While JSON parsers tolerate this, using <<-EOF with tab indentation (or unindenting the content) would produce cleaner output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/rust-test/action.yml around lines 100 - 114, The heredoc
that generates the metadata.json file includes leading whitespace due to
indentation, which produces non-standard JSON formatting. Replace the current
`<<EOF` heredoc syntax with `<<-EOF` to enable tab-stripping mode, then adjust
the indentation of the JSON content lines to use tabs instead of spaces, which
will be stripped automatically. Alternatively, unindent all the JSON content
lines to remove the leading whitespace while keeping the heredoc delimiter
unindented.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/actions/rust-test/action.yml:
- Around line 100-114: The heredoc that generates the metadata.json file
includes leading whitespace due to indentation, which produces non-standard JSON
formatting. Replace the current `<<EOF` heredoc syntax with `<<-EOF` to enable
tab-stripping mode, then adjust the indentation of the JSON content lines to use
tabs instead of spaces, which will be stripped automatically. Alternatively,
unindent all the JSON content lines to remove the leading whitespace while
keeping the heredoc delimiter unindented.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 88132de0-f75f-4ab6-a369-58e6db65900a

📥 Commits

Reviewing files that changed from the base of the PR and between 302a2f7 and 3a6e0c9.

📒 Files selected for processing (2)
  • .github/actions/rust-test/action.yml
  • .github/workflows/pacquet-ci.yml

@zkochan zkochan force-pushed the fix/rust-ci-required-test-checks-skip branch from 3a6e0c9 to ae4bfe5 Compare June 16, 2026 14:04
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit ae4bfe5

@zkochan zkochan changed the title ci(rust): split required Test matrix into static per-OS jobs ci: require a single aggregate gate job per workflow Jun 16, 2026
Comment thread .github/workflows/ci.yml
Comment on lines +94 to +105
success:
name: TS CI / Success
if: ${{ always() }}
needs:
- compile-and-lint
- test-smoke
- test
runs-on: ubuntu-latest
steps:
- name: Fail if any dependency failed or was cancelled
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Ts ci success bypass 🐞 Bug ☼ Reliability

TS CI / Success always runs and treats skipped dependencies as a pass, so for same-repo
pull_request events where compile-and-lint is skipped, it can report success even though no TS
compile/lint/tests ran. If branch protection is updated to require only this context (per the new
comment), same-repo PRs can be merged without waiting for the push-based CI run that actually
executes the test matrix.
Agent Prompt
## Issue description
The new aggregate job `TS CI / Success` runs unconditionally (`if: always()`) and only fails when dependencies are `failure`/`cancelled`. On same-repo `pull_request` events, `compile-and-lint` is intentionally skipped, which causes `test-smoke` and `test` to be skipped as well; the aggregate job still reports success, creating a potential merge-gate bypass if this context is used as the only required check.

## Issue Context
`compile-and-lint` already has an `if:` guard to avoid duplicate same-repo PR runs; the aggregate job should follow the same guard (or otherwise ensure it does not produce a passing required check when the actual CI did not run).

## Fix Focus Areas
- .github/workflows/ci.yml[9-14]
- .github/workflows/ci.yml[88-105]

### Concrete fix
Update the `success` job to use the same `if:` condition as `compile-and-lint` (while still using `always()` semantics for dependency failures within that scope), e.g.:
- `if: ${{ always() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository || github.head_ref == 'chore/update-lockfile') }}`
This ensures same-repo PR events don’t get a green `TS CI / Success` without running the underlying TS jobs, while push events still produce the required context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this is a real bypass and I've removed the TS CI / Success gate entirely (force-pushed). TS CI runs on both push and pull_request, and on same-repo PRs the pull_request run skips compile-and-lint (cascading to the whole matrix), so a single gate would report success-via-skip. A gate is a late check, so it also can't provide the early in_progress blocking signal that TS CI's granular required contexts give during the real push run.

TS CI never had the original problem this PR fixes, so it keeps its granular required contexts unchanged. The aggregate gate is now applied only to Rust CI, which is safe there because push is restricted to main — a PR branch produces a single run with no concurrent run to shadow it.


Written by an agent (Claude Code, claude-opus-4-8).

@zkochan zkochan force-pushed the fix/rust-ci-required-test-checks-skip branch from ae4bfe5 to 76e6b68 Compare June 16, 2026 14:15
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 76e6b68

@zkochan zkochan changed the title ci: require a single aggregate gate job per workflow ci(rust): require a single aggregate gate job for Rust CI Jun 16, 2026
Branch protection required the individual Rust CI and TS CI jobs as
status checks. That is brittle: most Rust jobs skip on PRs that touch no
Rust files, and a matrix job skipped at the job level never expands its
'${{ matrix.* }}' name — GitHub reports one check under the literal
unexpanded name, so the required per-OS contexts never appear and any
non-Rust PR blocks forever waiting for status that is never reported.

Add one static-named aggregate gate per workflow ('Rust CI / Success'
and 'TS CI / Success') that runs with 'if: always()' and fails only when
a dependency actually failed or was cancelled; skipped dependencies
count as a pass. Branch protection can then require just these two
contexts, decoupling them from the matrix shapes.

TS CI runs on both push and pull_request, and on same-repo PRs the
pull_request run skips every job for deduplication. A naive gate would
report a green 'TS CI / Success' on that skipped run and let a PR merge
before the real push run finished. The gate's name is therefore
'TS CI / Success' only on runs that do real work, and a different name
on the skipped duplicate run — using 'compile-and-lint''s 'if:' verbatim,
which the name MUST stay in sync with. Rust CI needs no such guard: its
push trigger is restricted to main, so a PR produces a single run.

Requires a branch-protection update: replace the per-job Rust CI / TS CI
contexts with 'Rust CI / Success' and 'TS CI / Success'.
@zkochan zkochan force-pushed the fix/rust-ci-required-test-checks-skip branch from 76e6b68 to 22aa26a Compare June 16, 2026 16:51
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 22aa26a

@zkochan zkochan changed the title ci(rust): require a single aggregate gate job for Rust CI ci: require a single aggregate gate job per workflow Jun 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

17-17: ⚖️ Poor tradeoff

Condition duplication creates a synchronization risk.

The condition on line 106 must exactly match line 17 to preserve the race-condition safety: if they diverge, a skipped duplicate run could report as TS CI / Success before the real run finishes, satisfying branch protection prematurely.

The "KEEP IN SYNC" comments mitigate this, but manual synchronization is fragile. GitHub Actions doesn't offer good primitives (env, variables, anchors) for sharing job-level expressions, so eliminating the duplication is difficult.

Consider adding a CI linting step or a custom action that verifies these conditions remain identical, or document this as a known limitation in a broader CONTRIBUTING guide.

Also applies to: 106-106

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 17, The workflow has a duplicated condition
expression on line 17 and line 106 that must remain perfectly synchronized to
prevent race-condition safety issues where a skipped duplicate run could report
success prematurely. To fix this, implement one of the following approaches:
either add a CI linting step or custom action that verifies both conditions on
line 17 and line 106 remain identical (failing the build if they diverge), or
document this synchronization requirement as a known limitation in a
CONTRIBUTING guide with clear instructions for maintainers on how to update both
locations together whenever changes are needed. The goal is to move from relying
on informal "KEEP IN SYNC" comments to an automated or well-documented approach
that prevents accidental divergence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 17: The workflow has a duplicated condition expression on line 17 and
line 106 that must remain perfectly synchronized to prevent race-condition
safety issues where a skipped duplicate run could report success prematurely. To
fix this, implement one of the following approaches: either add a CI linting
step or custom action that verifies both conditions on line 17 and line 106
remain identical (failing the build if they diverge), or document this
synchronization requirement as a known limitation in a CONTRIBUTING guide with
clear instructions for maintainers on how to update both locations together
whenever changes are needed. The goal is to move from relying on informal "KEEP
IN SYNC" comments to an automated or well-documented approach that prevents
accidental divergence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e6cae91-5264-4fdb-bf0f-817281155016

📥 Commits

Reviewing files that changed from the base of the PR and between 76e6b68 and 22aa26a.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • .github/workflows/pacquet-ci.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/pacquet-ci.yml

@zkochan zkochan merged commit 7c97d9e into main Jun 16, 2026
24 checks passed
@zkochan zkochan deleted the fix/rust-ci-required-test-checks-skip branch June 16, 2026 17:10
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