Skip to content

fix(spec): 锚点漂移提示按实测方向措辞,不再把「领先」说成 trails … by 0 key(s) (#5847) #1243

fix(spec): 锚点漂移提示按实测方向措辞,不再把「领先」说成 trails … by 0 key(s) (#5847)

fix(spec): 锚点漂移提示按实测方向措辞,不再把「领先」说成 trails … by 0 key(s) (#5847) #1243

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 — AND IT HAS A PRECONDITION
# ----------------------------------------------------------------------------
# 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.
#
# ⚠️ THE PRECONDITION IS THE `merge_group:` TRIGGER BELOW (#6121). Adding this
# context to a required set is safe ONLY BECAUSE that trigger now exists. The
# paragraph above states the PR half of the rule — "a required context that
# reports nothing leaves every PR stuck" — and until #6121 this file invited
# maintainers into the required set while missing the QUEUE half: branch
# protection is evaluated AGAIN on the `gh-readonly-queue/**` generation, so a
# required context that produces no run THERE parks every queue generation
# forever (#3622 is the prototype for that deadlock).
#
# FUTURE EDITORS: before this context is added to — or left in — any required
# set, confirm `merge_group:` is still in `on:` below. Deleting that trigger
# does not fail loudly; it deadlocks the merge queue for every PR in the repo.
on:
pull_request:
branches: [main]
# Merge queue (see ci.yml for the full note): a required context must report
# on queue generations too, or queue builds wait forever on a check that never
# arrives. This is the precondition documented above (#6121). Which LANE the
# event lands in — blocking vs reporting — is decided in the pin-freshness
# step below, not here.
merge_group:
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.
#
# LANE BY EVENT — classify every new trigger HERE, explicitly (#6121).
# This test used to read `[ "$EVENT" != "pull_request" ]`, a negation
# that meant "workflow_dispatch" back when `on:` had exactly two
# entries. Adding `merge_group:` is the moment that proxy breaks: the
# negation would sweep every queue generation into the BLOCKING lane,
# where this gate goes red whenever the pin lags — the repo's normal
# state between bumps (see WHERE IT BLOCKS at the top) — and a required
# context that is systematically red EJECTS every PR from the queue.
# That would trade one deadlock for another rather than fix it.
#
# workflow_dispatch → BLOCKS. A human asked for the full check.
# merge_group → reports. The queue generation of an ordinary PR
# is an ordinary PR; the Version Packages PR is
# enforced on its own `pull_request` run, and
# release.yml runs this same script on the publish
# path — the backstop with no override input.
# pull_request → BLOCKS on the release lane only.
#
# HEAD_REF and PR_TITLE are empty on merge_group (no `pull_request` in
# the payload), so the two release-lane tests are false there anyway.
if [ "$EVENT" = "workflow_dispatch" ] \
|| [ "$HEAD_REF" = "changeset-release/main" ] \
|| [ "$PR_TITLE" = "chore: version packages" ]; then
echo "::notice::Release lane or manual run — the objectui pin-freshness gate BLOCKS here (#3340)."
node scripts/check-objectui-pin-fresh.mjs
else
echo "::notice::Not the release lane — pin freshness is reported but does not block (a pin lagging between bumps is normal). It blocks on the Version Packages PR and on the publish path."
node scripts/check-objectui-pin-fresh.mjs --advisory
fi