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
4 changes: 4 additions & 0 deletions .changeset/objectui-pin-freshness-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

ci(release): add the objectui pin-freshness gate (#3340 P0). `scripts/check-objectui-pin-fresh.mjs` fails when `.objectui-sha` is not objectui `main` (or a named `--ref`), naming the commits ahead and the `.changeset/*.md` files declared after the pin — the blind spot that dropped four frontend changes, two of them `minor` features, from the v16 release page. Wired as `Console Pin Freshness` in `.github/workflows/objectui-pin-freshness.yml`: it runs on every PR so the context can be required in branch protection, but blocks only on the Version Packages / release PR. Distinct from ci.yml's `Console Pin Gate` (#4290), which proves the pin still *builds* rather than that it is still *current*. Tooling and CI only; releases nothing.
98 changes: 98 additions & 0 deletions .github/workflows/objectui-pin-freshness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Console Pin Freshness

# Is `.objectui-sha` still CURRENT? (#3340 P0)
#
# ⚠️ NOT ci.yml's "Console Pin Gate" (#4290). The names are close and the
# questions are opposite ends of the same fact:
#
# Console Pin Gate (#4290) "does the PINNED SHA still BUILD?" — clones
# objectui at the pin and builds the SPA.
# Console Pin Freshness "is the PIN still CURRENT?" — compares the pin
# (this workflow) against objectui `main`.
#
# A two-month-old pin builds perfectly (Pin Gate green) while hiding two months
# of frontend releases from the release record (this gate red). Keep both.
#
# WHERE IT BLOCKS
# ---------------
# On the changesets **Version Packages / release PR** only. Between pin bumps an
# ordinary code PR sits behind objectui almost always — that is the normal state
# of the repo, not a defect, and failing every PR over it would train everyone to
# ignore this check. So the job runs everywhere and blocks only on the release
# lane, where a lagging pin silently drops frontend changes from the release
# record (#3340: four changes, two of them `minor` features, lost from v16).
#
# WHY THE JOB IS NEVER SKIPPED
# ----------------------------
# It carries no job-level `if:` and no paths filter on purpose. A check that
# does not run reports nothing, and a *required* context that reports nothing
# leaves every PR stuck "Expected — waiting for status". Advisory mode is
# expressed in the EXIT CODE, not by skipping: the report is printed in full
# either way, so a green run on an ordinary PR still shows how far the pin has
# drifted.
#
# REQUIRED-CHECK ENFORCEMENT IS NOT SELF-DECLARED
# -----------------------------------------------
# A workflow cannot make itself required. A maintainer must add the
# `Console Pin Freshness` context to the branch-protection rule for `main`
# (Settings → Branches → main → Require status checks to pass). Until then this
# workflow REPORTS on the release PR without blocking the merge button.

on:
pull_request:
branches: [main]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
# Job name == the branch-protection context. Keep it stable: renaming it
# silently detaches the required check (the #3622 lesson ci.yml records).
pin-freshness:
name: Console Pin Freshness
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v7

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22'

# "A change to the guard runs the guard" — the rule this repo applies to
# every other scripts/ gate. No install: the script is dependency-free.
- name: Self-test the gate
run: node scripts/check-objectui-pin-fresh.mjs --self-test

- name: Check objectui pin freshness
env:
# Only ITEMIZES an already-established lag (`git ls-remote` decides the
# verdict), but the token keeps the API off the 60/hr anonymous limit
# so the report names the commits and changesets instead of degrading.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Read through env, never inlined into the shell: a PR title is
# attacker-controlled text.
HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_TITLE: ${{ github.event.pull_request.title }}
EVENT: ${{ github.event_name }}
run: |
# The changesets action opens the version PR from `changeset-release/<base>`
# with the title configured in release.yml. Either identifies the lane;
# both are checked so a future rename of one does not silently disarm
# the gate.
if [ "$EVENT" != "pull_request" ] \
|| [ "$HEAD_REF" = "changeset-release/main" ] \
|| [ "$PR_TITLE" = "chore: version packages" ]; then
echo "::notice::Release lane — the objectui pin-freshness gate BLOCKS here (#3340)."
node scripts/check-objectui-pin-fresh.mjs
else
echo "::notice::Not the Version Packages PR — pin freshness is reported but does not block (a pin lagging between bumps is normal). It blocks on the release PR."
node scripts/check-objectui-pin-fresh.mjs --advisory
fi
35 changes: 35 additions & 0 deletions docs/releases-maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,41 @@ changesets also embed companion frontend notes inline ("Companion objectui PR
ships…", renderer notes), which are enough to write an accurate Console section on
their own.

### Pin freshness — the gate on the release PR (#3340)

Everything above reads the range `OLD_PIN..NEW_PIN`. That is exact, and it is also the
whole blind spot: anything objectui merged **after** the current pin is outside every
range, so it reaches no changeset, no changelog and no release page — and a
complete-*looking* release record is indistinguishable from a complete one. Cutting v16
that way lost four frontend changes, two of them `minor` features, while objectui `main`
sat 4 commits and 21 pending changesets ahead of the pin.

`scripts/check-objectui-pin-fresh.mjs` (`pnpm check:objectui-pin-fresh`) closes it. It is
red when the pin is not objectui `main` (or the `--ref` you name), and it lists the
commits ahead plus the `.changeset/*.md` files that exist at `main` and not at the pin.

```bash
pnpm check:objectui-pin-fresh # enforcing
node scripts/check-objectui-pin-fresh.mjs --advisory # report only
node scripts/check-objectui-pin-fresh.mjs --ref v17.0.0 --json
```

- **Where it blocks:** the changesets **Version Packages / release PR**, via
`.github/workflows/objectui-pin-freshness.yml`. The job runs on every PR — so the
context always reports and can be a branch-protection *required* check — but passes
`--advisory` outside the release lane, because a pin lagging between bumps is the
normal state of an ordinary code PR.
- **It is not the Console Pin Gate.** `ci.yml`'s **Console Pin Gate** (#4290) proves the
pinned SHA still **builds**; this one proves the pin is still **current**. Either can
be green while the other is red; neither replaces the other.
- **Network failure is never green.** `git ls-remote` alone decides the verdict, so the
GitHub API (which only itemizes an already-established lag) can be rate-limited or
down without turning red into green — the degradation is printed, not swallowed. An
unreachable remote is reported as `unreadable` and exits non-zero.
- **Fix when it fires:** `scripts/bump-objectui.sh` to move the pin (which writes the
`@objectstack/console` changeset for the crossed range), then re-source the Console
section with `scripts/objectui-range.mjs`.

## Drift guard

`scripts/check-release-notes.mjs` (run in CI as `pnpm check:release-notes`) fails the
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"check:startup-registry-verdict": "node scripts/check-startup-registry-verdict.mjs --self-test && node scripts/check-startup-registry-verdict.mjs",
"check:console-sha": "node scripts/check-console-sha.mjs",
"check:objectui-changeset": "node scripts/objectui-changeset-digest.mjs --self-test && node scripts/objectui-range.mjs --self-test",
"check:objectui-pin-fresh": "node scripts/check-objectui-pin-fresh.mjs --self-test && node scripts/check-objectui-pin-fresh.mjs",
"check:release-notes": "node scripts/check-release-notes.mjs",
"check:node-version": "node scripts/check-node-version.mjs",
"check:published-files": "node scripts/check-published-files.mjs --self-test && node scripts/check-published-files.mjs",
Expand Down
Loading
Loading