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
217 changes: 206 additions & 11 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .github/workflows/control-bytes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ on:
branches: [main, develop]
push:
branches: [main, develop]
# Merge queue (objectui#3523 — see `ci.yml`'s trigger block for the full note
# and the measurements behind it). This repository's queue is enforced by a
# ruleset but had zero `merge_group` subscribers, so its required-check set
# could only ever be empty. This gate is one of the two the audit found safe
# to require today — deliberately unfiltered, so it reports on every shape of
# pull request — and a required check that does not report on a queue build
# stalls the queue until the ruleset's 60-minute timeout fails it. `types:` is
# named although `checks_requested` is currently the only one GitHub defines.
merge_group:
types: [checks_requested]
workflow_dispatch:

concurrency:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/docs-links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ on:
branches: [main, develop]
push:
branches: [main, develop]
# Merge queue (objectui#3523 — see `ci.yml`'s trigger block for the full note
# and the measurements behind it). This repository's queue is enforced by a
# ruleset but had zero `merge_group` subscribers, so its required-check set
# could only ever be empty. This gate is one of the two the audit found safe
# to require today — deliberately unfiltered, so it reports on every shape of
# pull request — and a required check that does not report on a queue build
# stalls the queue until the ruleset's 60-minute timeout fails it. `types:` is
# named although `checks_requested` is currently the only one GitHub defines.
merge_group:
types: [checks_requested]
workflow_dispatch:

concurrency:
Expand Down
93 changes: 88 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,45 @@ on:
- 'content/**'
- 'docs/**'
- '.changeset/**'
# No `paths-ignore` here any more (objectui#3523, step 2) — it skipped the
# whole workflow on a docs-only / changeset-only PR, so the `Lint` context was
# absent exactly where a required check must still report. The path decision
# moved into the job below. `push` above keeps its copy: nothing judges a push
# to `main`.
pull_request:
branches: [main, develop]
paths-ignore:
- '**/*.md'
- 'content/**'
- 'docs/**'
- '.changeset/**'
# ── Merge queue (objectui#3523) ────────────────────────────────────────
# The merge queue is ENFORCED on this repository by a ruleset — a direct push
# to `main` returns 405 `Changes must be made through the merge queue`
# (measured in #3243). Until this trigger landed, not one of the repository's
# workflows subscribed `merge_group`: repo-wide `event=merge_group` runs stood
# at total_count = 0, historically. A queue with nothing subscribed to it can
# only have an EMPTY required-check set, so it rebuilt each PR on the current
# `main` and let it through without validating anything.
#
# That is not a theoretical hole; it was cashed in on 2026-08-07. #3498 landed
# a `scripts/` type gate, itself fully green, that left a TS2578 on `main`;
# #3503, #3510 and #3516 then merged between 02:11Z and 02:15Z with `Type
# Check` at conclusion=failure, and #3505 hot-fixed the result. objectstack
# went through the same frames (objectstack#6067 -> #5615).
#
# `types:` is spelled out although `checks_requested` is the ONLY activity
# type GitHub defines for `merge_group` today — the two spellings are
# equivalent right now (objectstack's `ci.yml` and `lint.yml` use the bare
# `merge_group:` form and produce queue builds normally, 3552 of them). Naming
# the type means a second activity type added later cannot silently start
# queue builds this workflow was never written for.
#
# `concurrency` below needs no merge-queue special case, and that was checked
# rather than assumed: on `merge_group` the `github.event.pull_request` half of
# the group expression is null, so the group falls back to `github.ref`, which
# on a queue build is the queue's own generation — measured on objectstack,
# `gh-readonly-queue/main/pr-6594-251e888ac9ace8226f3a8450951e5b40a0a84c2c`.
# It can collide with neither a pull-request group (a bare PR number) nor a
# push group (`refs/heads/main`), so a queue build and the PR build it came
# from never cancel each other.
merge_group:
types: [checks_requested]
workflow_dispatch:

concurrency:
Expand All @@ -60,14 +92,61 @@ jobs:
uses: actions/checkout@v7
with:
submodules: true
# `fetch-depth: 0` for the gate step below (objectui#3523): it diffs
# against the merge base, which a depth-1 clone cannot resolve.
fetch-depth: 0

# ── Always report; run only when it matters (objectui#3523) ──────────
# `on.pull_request.paths-ignore` used to skip this whole workflow on a
# docs-only or changeset-only pull request, so the `Lint` context was
# simply absent there — and a required check that never reports leaves the
# PR pending forever (in the merge queue, until the ruleset's 60-minute
# timeout fails it). The filter moved from the trigger into the job: the
# job always runs and always reports, the paths decide only whether the
# expensive steps execute. `ci.yml`'s `docs` job is the in-repo precedent
# for the shape, and its `type-check` job carries the long version of this
# note. The list below IS the `paths-ignore` it replaced; the `push`
# trigger keeps its copy, because nothing judges a push to `main`.
#
# Fails OPEN: if the diff cannot be computed the job runs everything,
# rather than reporting green having linted nothing (objectstack#4928).
- name: Decide whether this change needs a full run
id: relevant
run: |
if [ "${{ github.event_name }}" != 'pull_request' ]; then
echo 'should_run=true' >> "$GITHUB_OUTPUT"
echo 'Not a pull request: push is filtered at the trigger, and a merge_group build is the last validation before main. Running everything.'
exit 0
fi
if ! CHANGED=$(git diff --name-only \
'${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}' -- \
. \
':(exclude,glob)**/*.md' \
':(exclude,glob)content/**' \
':(exclude,glob)docs/**' \
':(exclude,glob).changeset/**'); then
echo 'should_run=true' >> "$GITHUB_OUTPUT"
echo 'Could not diff against the merge base. Running everything rather than skipping silently.'
exit 0
fi
if [ -n "$CHANGED" ]; then
echo 'should_run=true' >> "$GITHUB_OUTPUT"
echo "$CHANGED"
else
echo 'should_run=false' >> "$GITHUB_OUTPUT"
echo 'Only ignored paths changed. Skipping the steps below; this check still reports.'
fi

- name: Enable Corepack
if: steps.relevant.outputs.should_run == 'true'
run: corepack enable

- name: Verify pnpm version
if: steps.relevant.outputs.should_run == 'true'
run: pnpm --version

- name: Setup Node.js
if: steps.relevant.outputs.should_run == 'true'
uses: actions/setup-node@v7
with:
node-version: '22.x'
Expand All @@ -77,9 +156,11 @@ jobs:
# scriptless packages silently, so without this a package reads as clean
# because nothing linted it. Runs before install: only reads package.json.
- name: Verify lint coverage
if: steps.relevant.outputs.should_run == 'true'
run: node scripts/check-lint-coverage.mjs

- name: Turbo Cache
if: steps.relevant.outputs.should_run == 'true'
uses: actions/cache@v6
with:
path: .turbo/cache
Expand All @@ -88,7 +169,9 @@ jobs:
turbo-${{ runner.os }}-

- name: Install dependencies
if: steps.relevant.outputs.should_run == 'true'
run: pnpm install --frozen-lockfile

- name: Run linter
if: steps.relevant.outputs.should_run == 'true'
run: pnpm lint
Loading
Loading