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
54 changes: 44 additions & 10 deletions .github/workflows/spec-shape-gate.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Spec Shape Gate

# Compile the downstream consumer -- objectstack-ai/objectui at `main` -- against
# `@objectstack/spec` built from THIS commit, and name the objectui file, the
# objectstack commit and the owning lane when it does not compile.
# `@objectstack/spec` (and every other `@objectstack/*` package it installs)
# built from THIS commit, and name the objectui file, the objectstack commit and
# the owning lane when it does not compile.
#
# ## The ruling this executes
#
Expand Down Expand Up @@ -205,16 +206,30 @@ jobs:
- name: Self-test the gate script
run: pnpm check:spec-shape-gate

# ── Build the spec from THIS commit, and PACK it ──────────────────────
# `--filter @objectstack/spec...` installs the spec and its dependencies
# only; `inject` later reads the dependencies this install resolved
# ── Build what objectui installs, from THIS commit, and PACK the spec ──
# objectui consumes every `@objectstack/*` package BY COMMIT: its root
# `package.json` overrides each to `file:node_modules/.cache/objectstack/
# <name>`, which its `scripts/objectstack-pin.mjs build` fills (below, with
# OBJECTSTACK_ROOT naming this checkout). The names are read from those
# overrides, the list the pin script packs; `--filter-prod` keeps the
# closure to runtime edges as that script's own build does (`...` alone
# would pull in the client's dev closure). The install brings the root's
# toolchain too, and `inject` later reads the dependencies it resolved
# (`packages/spec/node_modules`) to re-point a store sibling the packed
# manifest's range refuses.
- name: Install the spec's dependency closure
run: pnpm install --frozen-lockfile --filter @objectstack/spec...
- name: Install the closure of the @objectstack/* packages objectui installs by commit
run: |
set -euo pipefail
filters=$(node -e '
const m = JSON.parse(require("fs").readFileSync("objectui/package.json", "utf8"));
const names = Object.keys(m.pnpm?.overrides ?? {}).filter((n) => n.startsWith("@objectstack/")).sort();
if (names.length === 0) { console.error("objectui/package.json overrides no @objectstack/* package"); process.exit(2); }
process.stdout.write(names.map((n) => `--filter-prod=${n}...`).join(" "));')
echo "PINNED_FILTERS=$filters" >> "$GITHUB_ENV"
pnpm install --frozen-lockfile $filters

- name: Build @objectstack/spec from this commit
run: pnpm --filter @objectstack/spec build
- name: Build them from this commit
run: pnpm $PINNED_FILTERS run build

# What gets injected is what a RELEASE from this commit would ship -- the
# `files` field's file set, not a working tree. `--ignore-scripts`
Expand All @@ -238,6 +253,22 @@ jobs:
working-directory: objectui
run: pnpm --version

# objectui's `objectstack:build` script (its pin script's `build`) packs
# every package built above into the directories its overrides name;
# without it the frozen install dies on ENOENT there. Called by objectui's
# script NAME: a path here would read as one in this repository. It
# refuses (exit 1, its own `objectstack-pin` annotation) when a packed
# manifest's dependency NAMES differ from what objectui's lockfile records:
# the downstream owes a pin bump with its lockfile, and the report reads the
# missing compile as exit 2. A changed peer, engines or dependency range is
# not refused there -- a frozen install links it and exits 0 -- and the
# spec's ranges stay `inject`'s to judge.
- name: Pack this commit's @objectstack/* into objectui's pinned-build directories
working-directory: objectui
env:
OBJECTSTACK_ROOT: ${{ github.workspace }}
run: pnpm run objectstack:build

- name: Install objectui dependencies
working-directory: objectui
run: pnpm install --frozen-lockfile
Expand All @@ -254,7 +285,10 @@ jobs:
# re-points a store sibling the packed manifest's range refuses at the
# copy this tree's spec build resolved, and proves every workspace
# consumer resolves the injected copy before returning. Nothing tracked
# by git is touched in either checkout.
# by git is touched in either checkout. The pinned build above already
# installed this commit's spec (the same declaration files); what this
# step still adds is that range judgement and the consumer proof, which
# the pin script does not make (it compares dependency names only).
- name: Inject the source-built spec into objectui's install
env:
UPSTREAM_SHA: ${{ steps.subjects.outputs.upstream }}
Expand Down
4 changes: 2 additions & 2 deletions scripts/ci/spec-shape-gate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,12 +1135,12 @@ export function renderSummary({ sha, downstreamSha = null, commit = null, event
const link = `https://github.com/${UPSTREAM_REPO}/commit/${sha}`;
const out = [];
out.push(`### Spec Shape Gate -- \`${UPSTREAM_REPO}@${short}\` against \`${DOWNSTREAM_REPO}@${String(downstreamSha ?? 'main').slice(0, 12)}\``, '');
out.push(`Compiled ${DOWNSTREAM_REPO} at \`main\`${downstreamSha ? ` (\`${String(downstreamSha).slice(0, 12)}\`)` : ''} against \`${SPEC_PACKAGE_NAME}\` built and packed from [${short}](${link})${event ? ` (\`${event}\` run)` : ''}.`);
out.push(`Compiled ${DOWNSTREAM_REPO} at \`main\`${downstreamSha ? ` (\`${String(downstreamSha).slice(0, 12)}\`)` : ''} against \`${SPEC_PACKAGE_NAME}\` and every other \`@objectstack/*\` package it installs, built and packed from [${short}](${link})${event ? ` (\`${event}\` run)` : ''}.`);
if (commit) out.push('', `> ${commit}`);
out.push('');

if (status === 0 && rows.length === 0) {
out.push(`✅ ${DOWNSTREAM_REPO} type-checks against \`${SPEC_PACKAGE_NAME}\` at that commit.`);
out.push(`✅ ${DOWNSTREAM_REPO} type-checks against \`@objectstack/*\` at that commit.`);
} else if (rows.length === 0) {
out.push(
`⛔ The typecheck failed (exit ${status}) and **no diagnostic could be attributed to a file**.`,
Expand Down
Loading