From dfdc446640beb00e8b006c096ff9f848b7839d92 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Fri, 6 Nov 2026 12:49:42 +0100 Subject: [PATCH] ci(changeset/check-coverage): require a changeset for runtime catalog bumps A package now counts as changed when the branch edits a file inside it, or when the branch changes a catalog entry that the package references via `catalog:` / `catalog:` from `dependencies` or `peerDependencies`. Catalog entries referenced only from `devDependencies` or `optionalDependencies` require nothing. Changed files come from `git diff` against the merge base with the base branch, not from `pnpm --filter "[]"`. pnpm 11.28 / 12.7 made that filter mark every catalog consumer as changed, devDependencies included, which failed samples-and-data#140 on parse-h5ad and parse-seurat: both only take block-tools from the catalog as a devDependency. Restores the runtime catalog rule that de2b0ee dropped. Missing yq, a missing merge base or an unreadable catalog now exit 2 instead of passing. --- actions/changeset/check-coverage/action.yaml | 23 +- .../check-coverage/check-coverage.sh | 257 +++++++++++++----- .../changeset/check-coverage/test/README.md | 11 +- .../check-coverage/test/coverage.bats | 100 ++++--- .../test/fixtures/minimal-workspace/README.md | 7 +- .../packages/pkg-legacy/index.js | 1 + .../packages/pkg-legacy/package.json | 8 + .../packages/pkg-peer/index.js | 1 + .../packages/pkg-peer/package.json | 8 + .../minimal-workspace/pnpm-workspace.yaml | 4 + .../check-coverage/test/helpers.bash | 14 +- 11 files changed, 310 insertions(+), 124 deletions(-) create mode 100644 actions/changeset/check-coverage/test/fixtures/minimal-workspace/packages/pkg-legacy/index.js create mode 100644 actions/changeset/check-coverage/test/fixtures/minimal-workspace/packages/pkg-legacy/package.json create mode 100644 actions/changeset/check-coverage/test/fixtures/minimal-workspace/packages/pkg-peer/index.js create mode 100644 actions/changeset/check-coverage/test/fixtures/minimal-workspace/packages/pkg-peer/package.json diff --git a/actions/changeset/check-coverage/action.yaml b/actions/changeset/check-coverage/action.yaml index 515557e0..f2514b89 100644 --- a/actions/changeset/check-coverage/action.yaml +++ b/actions/changeset/check-coverage/action.yaml @@ -1,16 +1,15 @@ name: Check changeset coverage author: 'MiLaboratories' description: | - Fail if a PR's changesets don't cover every workspace package whose own - files it edits — e.g. editing block code under packages// without - adding that package to the changeset. + Fail if a PR's changesets don't cover every published workspace package it + changes. A package is changed when the PR edits a file inside its directory, + or when the PR changes a catalog entry in pnpm-workspace.yaml that the + package references via `catalog:` from `dependencies` or `peerDependencies`. + A catalog entry referenced only from `devDependencies` or + `optionalDependencies` requires nothing. - Dependency-version bumps are intentionally NOT flagged, including catalog - bumps in pnpm-workspace.yaml. A catalog entry always pins an external - package, and `pnpm changeset` never requires a release for an external - dependency bump — it propagates versions through the internal dependency - chain automatically at `changeset version` time. Flagging consumers of a - bumped catalog dependency would over-report relative to `pnpm changeset`. + Changes are counted from the merge base with the base branch, so commits that + landed on the base branch after the PR branched off are not the PR's. An empty changeset (`pnpm changeset --empty`) added in the branch is a deliberate, in-repo "no release needed" opt-out and waives the requirement for @@ -22,9 +21,9 @@ description: | caller workflow's job-level `if` (PR labels are only available on `pull_request` events). - Runs after `pnpm install`. Requires the runner to have `pnpm`, `jq` and `git` - on PATH — all pre-installed on GitHub-hosted ubuntu-latest images. `git` needs - full history (`fetch-depth: 0`) to see which changesets the branch added. + Runs after `pnpm install`. Requires `git`, `pnpm`, `jq` and `yq` (mikefarah, + v4+) on PATH. `git` needs full history (`fetch-depth: 0`) to find the merge + base with the base branch. inputs: base-branch: diff --git a/actions/changeset/check-coverage/check-coverage.sh b/actions/changeset/check-coverage/check-coverage.sh index 25c04187..b78f045b 100755 --- a/actions/changeset/check-coverage/check-coverage.sh +++ b/actions/changeset/check-coverage/check-coverage.sh @@ -1,33 +1,36 @@ #!/usr/bin/env bash # -# Verify the PR has a changeset for every workspace package whose own files -# it edits. Exit 1 on a coverage gap; exit 2 on tooling failure; exit 0 +# Verify the PR has a changeset for every published workspace package it +# changes. Exit 1 on a coverage gap; exit 2 on tooling failure; exit 0 # otherwise. # -# An empty changeset added in this branch (`pnpm changeset --empty`) waives the -# requirement — see section 4. +# A package is changed by the branch in two ways: +# +# direct edit — the branch changes a file inside the package directory. A +# file belongs to the deepest package directory containing +# it, so root-level paths (`.github/`, `docs/`, +# `pnpm-workspace.yaml`, `README.md`) belong to the workspace +# root, which is unnamed or private and never needs a bump. # -# "Modified" means a direct edit to a workspace package's own files, detected -# via `pnpm --filter '[]' list` — pnpm runs the per-package git-diff -# check itself. Root-level paths (`.github/`, `docs/`, `pnpm-workspace.yaml`, -# `README.md`) live in no package directory, so they never trigger inclusion. +# catalog bump — the branch changes a catalog entry in pnpm-workspace.yaml +# that the package references as `catalog:` or +# `catalog:` from `dependencies` or `peerDependencies`. +# Those dependencies ship with the package, so a new version +# of one is a new version of the package. `devDependencies` +# and `optionalDependencies` do not count. # -# Dependency-version bumps never require a changeset — including catalog -# bumps in pnpm-workspace.yaml. A catalog entry always pins an *external* -# package (workspace packages are referenced via `workspace:*`, never -# `catalog:`). `pnpm changeset` ignores such a bump twice over: it never -# releases a package because an external dependency's version changed, and it -# propagates internal bumps through the dependency chain automatically at -# `changeset version` time. So requiring a hand-written changeset for a -# package that only "changed" via a dependency bump over-reports relative to -# `pnpm changeset` — see platforma-open/clonotype-space#95, where a -# `@platforma-sdk/workflow-tengo` catalog bump spuriously failed the -# untouched `.workflow` package. +# "The branch changes" means the difference between the merge base with the +# base branch and HEAD: commits that landed on the base branch after the +# branch left it are not the branch's changes. # -# Skips private (unpublished) workspace packages — they never appear in -# the changeset's release set. +# Private packages are skipped — they never appear in a changeset's release +# set. +# +# An empty changeset added in this branch (`pnpm changeset --empty`) waives the +# requirement — see section 4. # -# Runs from the repo root after `pnpm install`. +# Runs from the repo root after `pnpm install`. Needs `git` with the base +# branch's history, `pnpm`, `jq` and `yq` (mikefarah, v4+). set -o nounset set -o errexit @@ -38,43 +41,42 @@ set -o pipefail log() { printf '%s\n' "$*" >&2; } err() { printf '::error::%s\n' "$*" >&2; } -# Associative arrays (`declare -A`) require bash 4+. The GitHub-hosted -# ubuntu-latest runners ship bash 5+, so this guard is a no-op in CI today. -# It matters when running locally on macOS: /bin/bash is still 3.2, and -# without homebrew's bash earlier on PATH the script would otherwise abort -# at the first `declare -A` with a cryptic `invalid option`. Fail fast with -# a useful message instead. +# Associative arrays (`declare -A`) need bash 4+. macOS ships bash 3.2 as +# /bin/bash, where `declare -A` aborts with a cryptic `invalid option`. if [ "${BASH_VERSINFO[0]:-0}" -lt 4 ]; then err "check-coverage requires bash 4+ (found ${BASH_VERSION:-unknown})." exit 2 fi -# Use cwd-relative paths for tmp files — changeset's --output mis-handles -# absolute paths on macOS (prepends cwd, causing ENOENT). Cwd-relative is -# safe on every platform. +for tool in git pnpm jq yq; do + if ! command -v "${tool}" >/dev/null 2>&1; then + err "${tool} not found on PATH." + exit 2 + fi +done + +# changeset's --output resolves an absolute path against the cwd on macOS, so +# the status file takes a cwd-relative path. status_json=".changeset-coverage-status-$$.json" +trap 'rm -f "${status_json}"' EXIT # --------------------------------------------------------------------------- -# 1. Bumped set from `changeset status --output=...`. +# 1. Bumped set: the packages the branch's changesets release. # --------------------------------------------------------------------------- -trap 'rm -f "${status_json}"' EXIT -# Invoke the binary directly. `pnpm exec` and `pnpm