Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/6321-changeset-guard-self-coverage.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
---
---

Internal CI-only change: `changeset-guard.yml`'s `paths:` filter now includes its own
YAML and `scripts/check-changeset-no-major.mjs`, so a change to the gate is exercised by
the PR that makes it instead of by the next unrelated `.changeset/**` PR (objectui#6321).
`content/docs/guide/ci-cd-pipeline.md` is updated to match. No published package changed.
39 changes: 39 additions & 0 deletions .github/workflows/changeset-guard.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,16 +25,55 @@ name: Changeset Guard
#
# It needs no install and no build — a checkout plus one `node` call, a few
# seconds — so keep it that way if you add checks to it.
#
# ── Self-coverage (objectui#6321) ────────────────────────────────────────────
#
# The `.changeset/**` entry above is the inverse trigger this file exists for;
# the two entries below it are a DIFFERENT thing — ordinary self-coverage, so a
# change to the gate itself is exercised by the PR that makes it rather than by
# the next unrelated PR that happens to touch `.changeset/**`. Before this,
# neither this YAML nor the script it runs was in the filter at all: measured
# against the other six path-filtered workflows in this repo, 5 of 7
# self-include and this was one of the two that did not (the other,
# `performance-budget.yml`, was closed in #6315).
#
# The convention those five follow is "list what the `run:` step below actually
# executes" — own YAML plus the invoked script — not the transitive import
# graph. `half-state-patrol.yml` additionally lists `scripts/invoked-as.mjs`,
# but that is a documented one-off (its own header's divergence #2, tied to
# objectui#5791's port from objectstack) and NOT the pattern the other three
# self-including, script-running gates follow: `node-esm-load-gate.yml`,
# `published-dist-gate.yml` and `spec-range-floors.yml` all import
# `scripts/invoked-as.mjs` too (same `isEntrypoint` helper) and none of them
# lists it — it is a widely shared utility (40+ importers under `scripts/`),
# and a change to it is already caught by `check-entry-guard.mjs` /
# `entry-guard-wiring.test.ts` in the ordinary vitest suite, not by re-running
# every gate that happens to import it. So this filter matches the
# three-of-four majority and does not add it either.
#
# Also deliberately NOT listed: `scripts/__tests__/check-changeset-no-major.test.ts`.
# That file matches the `~ partial` pattern `published-dist-gate.yml` and
# `spec-range-floors.yml` already set for their own gate scripts' `__tests__`
# files — the test lives under `scripts/**`, which the `Decide whether this
# change needs a full run` step in `ci.yml`/`lint.yml` does NOT exclude, so any
# PR that edits it (or the script) already gets a real run of the root vitest
# suite that executes it. Adding it here would duplicate coverage `pnpm test`
# already provides on exactly the PRs where it matters, for no PR it would add
# it on.

on:
pull_request:
branches: [main, develop]
paths:
- '.changeset/**'
- '.github/workflows/changeset-guard.yml'
- 'scripts/check-changeset-no-major.mjs'
push:
branches: [main]
paths:
- '.changeset/**'
- '.github/workflows/changeset-guard.yml'
- 'scripts/check-changeset-no-major.mjs'

concurrency:
group: changeset-guard-${{ github.event.pull_request.number || github.ref }}
Expand Down
25 changes: 20 additions & 5 deletions content/docs/guide/ci-cd-pipeline.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,7 +25,7 @@ one has its own section below.
|---|---|---|---|
| `ci.yml` | CI | Push / PR to `main`, `develop`; merge-queue builds | **Yes** — every job but the two coverage-lane jobs (`test-coverage` and `coverage-report`, push only) runs on PRs and on queue builds |
| `lint.yml` | Lint | Push / PR to `main`, `develop`; merge-queue builds; manual | **Yes** — ESLint **errors** only |
| `changeset-guard.yml` | Changeset Bump Policy | PR / push touching `.changeset/**` | **Yes** |
| `changeset-guard.yml` | Changeset Bump Policy | PR / push touching `.changeset/**` or the gate itself | **Yes** |
| `changeset-presence.yml` | Changeset Declaration | PR to `main`, `develop` — **no path filter**; merge-queue builds | **Yes** — when a released package's `src/` changed and no changeset was added |
| `control-bytes.yml` | Control Byte Scan | Push / PR to `main`, `develop` — **no path filter**; merge-queue builds; manual | **Yes** |
| `docs-links.yml` | Internal Docs Link Check | Push / PR to `main`, `develop` — **no path filter**; merge-queue builds; manual | **Yes** |
Expand DownExpand Up@@ -65,8 +65,10 @@ The path filters explain most "why did nothing run on my PR?" questions:
the whole point: a check that is never *created* cannot be a required check — it leaves the PR
pending rather than failing it — so while the filter sat on the trigger, none of these could be
required at all.
- `changeset-guard.yml` carries the inverse filter — it runs *only* when `.changeset/**` changes,
which is precisely why it is a separate workflow instead of a job inside `ci.yml`.
- `changeset-guard.yml` carries the inverse filter — it runs *only* when `.changeset/**` changes
(plus its own YAML and `scripts/check-changeset-no-major.mjs`, so a change to the gate itself is
exercised by the PR that makes it — objectui#6321), which is precisely why it is a separate
workflow instead of a job inside `ci.yml`.
- `changeset-presence.yml` is that guard's mirror image and the reason there are two: a PR which
*forgot* its changeset does not touch `.changeset/**`, so the inverse filter guarantees the one
check that could notice never runs. It therefore carries **no** filter and decides from the diff
Expand DownExpand Up@@ -134,8 +136,9 @@ Two things follow for anyone editing this directory:
reading; they are examples rather than a census, so a further workflow carrying any of these
shapes is just as unrequirable without appearing here.
- **Changeset Bump Policy** (`changeset-guard.yml`) — an **inverse** path filter: its
`pull_request` trigger declares `paths: ['.changeset/**']`, so on a PR that touches nothing
under `.changeset/**` the context is never created at all.
`pull_request` trigger declares
`paths: ['.changeset/**', '.github/workflows/changeset-guard.yml', 'scripts/check-changeset-no-major.mjs']`,
so on a PR that touches none of those three the context is never created at all.
- **Bundle Analysis** (`performance-budget.yml`) — an ordinary path filter on the same
trigger, with the same consequence for every PR that matches none of its paths.
- **Live E2E (informational)** (`live-e2e.yml`) — the job carries `continue-on-error: true`,
Expand DownExpand Up@@ -1262,6 +1265,18 @@ changeset started nothing at all. Since [#3523](https://github.com/objectstack-a
job in them short-circuits, because `.changeset/**` is still on the in-job ignore list. The check
that has to read the changeset therefore still lives here.

The same `paths:` list also carries the gate's own YAML and
`scripts/check-changeset-no-major.mjs` ([#6321](https://github.com/objectstack-ai/objectui/issues/6321))
— self-coverage, not the inverse trigger above: without it, a PR that edits the gate is not the PR
that runs it, and the first real execution lands on someone else's unrelated `.changeset/**` PR.
Deliberately not listed: `scripts/invoked-as.mjs` (a dependency the gate script imports, but a
widely shared one — 40+ importers under `scripts/` — that `published-dist-gate.yml`,
`spec-range-floors.yml` and `node-esm-load-gate.yml` also import without listing; only
`half-state-patrol.yml` lists it, as a documented one-off) and the script's own
`__tests__/check-changeset-no-major.test.ts` (it already runs in the root vitest suite on any PR
that touches `scripts/**`, the same `~ partial` reasoning `published-dist-gate.yml` and
`spec-range-floors.yml` apply to their own gate scripts' `__tests__` files).

Runs `scripts/check-changeset-no-major.mjs`, which fails if any pending changeset declares a
`major` bump. Every publishable package is in one `fixed` group (39 packages), so a single
`major` publishes all of them as the next major — and objectui's major is pinned to the
Expand Down
Loading