ci: add changeset coverage check#168
Merged
Merged
Conversation
New composite action `actions/changeset/check-coverage` that fails when a PR's changesets don't bump every workspace package the PR modifies. Catches the catalog-software gap (pnpm-workspace.yaml catalog version bumped without a matching changeset for the consuming workflow package). Wired into the existing `check-changesets` job in node-simple-pnpm.yaml as a `continue-on-error: true` step on PR / merge_group events. Emits `::error file=…,line=…::` annotations inline on the PR diff; never blocks merge.
TODO: REVERT before merging this PR — flip @feat/changeset-coverage-check back to @v4-beta. The action only lives on this branch right now; consumers pinning the workflow at this branch otherwise resolve the nested uses: against v4-beta and fail to find the action.
The over-broad sed in merge-beta.sh/fix-beta.sh flips @v4 → @v4-beta on every ref, including third-party actions. actions/checkout and aws-actions/configure-aws-credentials have no @v4-beta tag upstream, so the workflow fails to load when a consumer pins this branch. Only this file is fixed — the minimum needed to canary-test the new coverage check. The same bug affects 27 other files on v4-beta and needs a separate sed-scope fix in merge-beta.sh / fix-beta.sh.
Same pre-existing sed-scope bug as the previous commit, now hit in the composite actions check-changesets transitively calls. Restores actions/setup-node, pnpm/action-setup, and actions/cache to @v4.
…s suite Refactor check-coverage.sh (267 → 213 lines): - step 4a now uses `pnpm -r --filter '[origin/<base>]' list` instead of hand-rolled longest-prefix matching over the changed-files list and the manual ignore patterns. Root-level paths (.github/, docs/, README) aren't in any package directory, so pnpm's filter naturally excludes them. - step 4b parses pnpm-workspace.yaml structurally with yq (flat key=value pairs from both .catalog and .catalogs.*) instead of a line-level regex over the diff. Avoids the regex's false-positive surface — e.g. an unrelated `overrides.<name>` bump no longer spuriously requires catalog-consumer bumps. - workspace map is now built lazily, only when pnpm-workspace.yaml changed. - drops the `::error file=,line=::` per-file annotation plumbing. Devs land on .changeset/ when fixing this anyway; the summary block still names every missing package. Add bats suite under test/ with a minimal pnpm-workspace fixture and a CI workflow (0-test-changeset-coverage.yaml) that runs the suite on PRs that touch the action. 14 cases cover direct edits, catalog miss/hit/partial, private packages, structural-vs-regex catalog parsing, and the no- changesets corner.
Drop the workflow-self path from the trigger filter — the suite should only run when the action it tests actually changes. Add workflow_dispatch as a manual escape hatch for re-running after a workflow edit.
declare -A is bash 4+. macOS ships 3.2 at /bin/bash; without homebrew's bash earlier on PATH the script aborts at the first declare with a cryptic 'invalid option'. Detect at start and emit a useful error instead.
Note that ubuntu-latest runners ship bash 5+ — so the guard is a no-op in CI; it exists to help local runs on macOS, whose /bin/bash is still 3.2 and would otherwise produce a cryptic 'invalid option' error.
…ack to @v4-beta" This reverts commit bff4168.
Collaborator
Author
- check-coverage.sh: tighten catalog detection to `startswith("catalog:")`
(was `startswith("catalog")`) — pnpm's protocol prefix is the literal
`catalog:`.
- node-simple-pnpm.yaml: add `!cancelled()` to the coverage-check
conditional so the step skips on cancelled jobs (manual cancel,
concurrency-group eviction) instead of burning runner minutes.
- test/helpers.bash: harden `_build_base_workspace` — `set -e` inside
the subshell, capture output to a log file, dump only on failure.
Without this, a silently failing `pnpm install` corrupts the base
workspace and every test then fails with a confusing error pointing
at the script under test, not the install.
- test/coverage.bats: three new tests.
- BASE_BRANCH input: production callers pass `v4` (or `master`),
not `main`. Five `origin/${BASE_BRANCH}` references in the script
could regress to hardcoded `main`; this guards against it.
- exit 2 on missing changeset binary: locks down the tooling-failure
contract distinct from exit 1 (coverage gap).
- named-catalog over-flagging: skip-marked, documents the deferred
bug from the PR body with a runnable fixture for the follow-up.
…e primary case The prior description led with the catalog-version gap, but the more common case the action catches is the simple one: edit files inside a workspace package, forget to add that package to the changeset. List both, with the direct-edit case first.
This was referenced May 28, 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.
New composite action
actions/changeset/check-coverage— fails when a PR's changesets don't bump every workspace package the PR modifies. Catches the catalog-software gap (apnpm-workspace.yamlcatalog version bumped without a matching changeset for the consuming workflow package).Wired into the
check-changesetsjob innode-simple-pnpm.yamlas acontinue-on-error: truestep. Reports missing packages in the job log. Never blocks merge today.How "modified" is detected
pnpm -r --filter '[origin/<base>]' listselects packages whose own files changed. pnpm runs the per-package git diff itself, so no manual longest-prefix matching or ignore-list maintenance.pnpm-workspace.yaml—yqflattens.catalogand.catalogs.*tokey=value;uniq -uover old vs head gives the changed keys;jqthen finds every workspace consumer that depends on the key via"catalog:...". Structural parse avoids the false-positive surface of a regex on the raw diff (e.g. an unrelatedoverrides.<name>bump no longer leaks into the catalog path).Skips private (unpublished) packages — they never appear in a changeset's release set.
Tests
actions/changeset/check-coverage/test/ships a bats suite (14 cases) against a minimal pnpm-workspace fixture..github/workflows/0-test-changeset-coverage.yamlruns it on every PR that touches the action source.Local:
bats actions/changeset/check-coverage/test/coverage.bats. Seetest/README.mdfor fixture and runner deps.Known follow-ups (out of scope here)
jqpredicate usesstartswith("catalog"), so it doesn't distinguish"catalog:"from"catalog:<name>". Not exercised today (no named catalogs in target repo). Tighten when.catalogs.*are introduced and add a fixture variant for them.continue-on-error: trueramp. Stays warn-only through the canary. Tighten to blocking after the gate proves itself — same conditional as the surroundingpreflight-*jobs fits: enforce onchangeset-default-branchandmerge_group, warn-only on PRs.merge-beta.sh/fix-beta.sh. Flagged separately. The sed flips third-party refs too, leavingactions/checkout@v4-betaetc. unresolvable while the workflow lives onv4-beta. Not addressed in this PR; ticket open.Greptile Summary
This PR introduces a new
actions/changeset/check-coveragecomposite action that detects when a PR modifies workspace packages without a corresponding changeset bump, wired into thecheck-changesetsjob as a non-blocking (continue-on-error: true) step. It is accompanied by a bats test suite and a dedicated CI workflow.check-coverage.sh: detects two categories of missed bumps — direct file edits inside a workspace package (viapnpm --filter '[origin/base]' list) and catalog version changes inpnpm-workspace.yaml(via structuralyqdiffing of.catalog/.catalogs); exits 1 on a gap, 2 on tooling failure, 0 on clean coverage.coverage.bats+helpers.bash: thirteen bats tests covering the main paths (direct edits, catalog bumps, private packages, partial coverage, empty changesets) using a sharedpnpm installbase with per-test tar-copied workspaces for speed.node-simple-pnpm.yaml: the coverage check is added after the existing "Check for Changesets" step withif: always()so it runs even when there are no changesets yet, surfacing every un-bumped package at once.Confidence Score: 5/5
Safe to merge; the coverage check is non-blocking and the core detection logic is solid for the common single-catalog case.
The change is entirely additive and non-blocking. The detection logic is well-tested with a thorough bats suite. The two edge cases flagged have no impact on the critical path and won't cause data loss or incorrect merge decisions.
The cat_pairs section of check-coverage.sh (lines 163–178) is worth revisiting if the repo ever adopts multiple named catalogs with overlapping package keys.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[check-changesets job] --> B[pnpm install] B --> C[Check for Changesets\npnpm changeset status] C -->|always| D[check-coverage action] D --> E{run check-coverage.sh} E --> F[1. Build bumped_set\nchangeset status --output JSON] E --> G[2a. Build required_set\npnpm filter list direct edits] E --> H{pnpm-workspace.yaml\nchanged?} H -->|yes| I[2b. yq diff old vs new\ncatalog entries] I --> J[Find consumers of\ntouched catalog keys\nvia jq in package.json] J --> K[Add to required_set] G --> K F --> L{required_set minus\nbumped_set} K --> L L -->|empty| M[exit 0] L -->|non-empty| N[emit error annotations\nexit 1] N --> O[continue-on-error: true\njob continues]Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "Revert "Fix incomplete revert: flip rema..." | Re-trigger Greptile