Skip to content

Commit b35ec35

Browse files
committed
release(ui-merge): workflows, root scripts and the CLI remedy follow the in-workspace Console build (objectstack#25711, WIP)
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0149BHSXfCdQywGaLdpRAtWe
1 parent 076bb90 commit b35ec35

12 files changed

Lines changed: 110 additions & 354 deletions

File tree

‎.github/workflows/cut-rc.yml‎

Lines changed: 28 additions & 167 deletions
Large diffs are not rendered by default.

‎.github/workflows/lint.yml‎

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,31 +3064,6 @@ jobs:
30643064
continue-on-error: true
30653065
run: pnpm check:startup-registry-verdict
30663066

3067-
# objectui pin-changeset guard (#4731). A frontend-only refresh reaches the
3068-
# platform release record ONLY through the changeset `bump-objectui.sh`
3069-
# writes, and that list used to be guessed off objectui's commit subjects
3070-
# (`feat|fix` + a silent `head -40`). On one real range the guess dropped
3071-
# all 13 releasing non-feat/fix commits — every breaking `refactor(...)!`
3072-
# among them — and pulled in 5 that release nothing. The list and the bump
3073-
# level now come from what objectui DECLARED (`.changeset/*.md` added over
3074-
# the range; empty frontmatter = release-nothing). This self-test builds
3075-
# throwaway git repos carrying those exact shapes, runs the real scripts
3076-
# over them, and asserts the emitted changeset — including that a cap which
3077-
# fires announces itself and that an unwalkable range degrades loudly.
3078-
# #4843 folded `scripts/objectui-range.mjs` — the release page's Console
3079-
# section — onto that same `classifyRange()` criterion, so both self-tests
3080-
# run here: one criterion, two consumers, no room to drift apart.
3081-
- name: objectui pin-changeset digest guard
3082-
id: objectui-pin-changeset-digest-guard
3083-
continue-on-error: true
3084-
run: pnpm check:objectui-changeset
3085-
3086-
# Rationale: `scripts/bump-objectui.selftest.sh` header (#10797).
3087-
- name: objectui pin write-ordering guard
3088-
id: objectui-pin-write-ordering-guard
3089-
continue-on-error: true
3090-
run: pnpm check:objectui-bump
3091-
30923067
# Changeset-family gate self-tests (#6509). The SELF-TEST halves only —
30933068
# the real scans stay in pr-automation.yml's `changeset-check`, and the
30943069
# split is the whole point of this step.

‎.github/workflows/release.yml‎

Lines changed: 15 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,109 +1835,25 @@ jobs:
18351835
run: node scripts/check-changeset-fixed.mjs
18361836

18371837
# ──────────────────────────────────────────────────────────────────────
1838-
# ⛔ TOMBSTONE — the #3340 pin-currency gate that used to sit here is gone
1839-
# (2026-08-20 ruling, #10134). Do not re-add one.
1838+
# ⛔ TOMBSTONE — no objectui pin is read, built or checked here, and none
1839+
# is re-added (objectstack#25711, D1 ruling 「同一个版本号组」).
18401840
#
1841-
# It compared `.objectui-sha` against objectui `main` at publish time and
1842-
# refused a release whose pin lagged. The ruling removed the question, not
1843-
# just the job: WHICH objectui revision this repo pins is a decision taken
1844-
# in an objectstack issue, never derived from another repo's HEAD, so
1845-
# "the pin is behind main" is not a defect a release lane may diagnose.
1846-
#
1847-
# R4's lesson (#6170 — "a gate required on a PR the publishing lane can
1848-
# skip is not a gate") is untouched and does not resurrect this one:
1849-
# #3340's actual invariant is "everything published is covered by the
1850-
# changeset record", and that is carried by `scripts/bump-objectui.sh` +
1851-
# `scripts/objectui-changeset-digest.mjs` at BUMP time — on the manual pin
1852-
# PR, which is now the only way the pin ever moves. Publishing an old
1853-
# console is therefore a published decision, not an unnoticed gap.
1841+
# The Console is a workspace package now: `ui/apps/console`
1842+
# (@object-ui/console) builds against this tree's @objectstack/* through
1843+
# `workspace:^`, and `@objectstack/console#build` (root turbo.json) copies
1844+
# that dist with the tracked sdui.manifest.json, so the `pnpm run build`
1845+
# below builds it with everything else. The pin-currency gate, the
1846+
# vendored-dist cache keyed on `.objectui-sha`, its SHA stamp check and
1847+
# its spec-injection check are gone with the pin: each answered "was the
1848+
# Console built from the objectui commit, and against the spec, this
1849+
# release names", and both are true by construction when the Console
1850+
# source and the spec are one tree at one commit. The single-zod canary
1851+
# survives inside packages/console/build-dist.sh, on the bytes it ships.
18541852
# ──────────────────────────────────────────────────────────────────────
18551853

18561854
- name: Build
18571855
run: pnpm run build
18581856

1859-
- name: Read the spec's declared zod range (Console dist key input)
1860-
id: spec-zod
1861-
# Fails rather than keying on an empty or `undefined` range: a key that
1862-
# silently stopped carrying the range would restore across a zod move.
1863-
run: |
1864-
node -e '
1865-
const range = require("./packages/spec/package.json").dependencies?.zod;
1866-
if (typeof range !== "string" || range.trim() === "") {
1867-
console.error("packages/spec/package.json declares no dependencies.zod, so the Console dist key cannot carry it.");
1868-
process.exit(1);
1869-
}
1870-
console.log("range=" + range.trim());
1871-
' >> "$GITHUB_OUTPUT"
1872-
1873-
# This key hashes the pin and the build script, and carries the spec's
1874-
# DECLARED zod range (the step above). ci.yml's Console Pin Gate (#4290)
1875-
# hashes a SUPERSET of the files: those two, the probe scripts and the
1876-
# spec's entry layout (#20765). So the two workflows no longer share dist
1877-
# entries, and each one builds its own on a miss. Keeping this key narrower
1878-
# is deliberate.
1879-
# Following ci.yml would rebuild the console on every Version Packages
1880-
# merge, and publishing a cached console whose spec content lags is the
1881-
# existing cache design.
1882-
#
1883-
# The zod range is the one spec input the cached dist is NOT allowed to
1884-
# lag (#21108). The console bundles a single zod instance, the console's
1885-
# own copy, and its build refuses a console zod outside the injected spec's
1886-
# declared `zod` range (objectui#11353). A dist restored after that range
1887-
# moved would publish a console the build never judged against it. The
1888-
# range is read out of packages/spec/package.json, not hashed with it: the
1889-
# file's `version` moves on every Version Packages merge, and hashing the
1890-
# whole file would turn every publish into a cold console rebuild.
1891-
#
1892-
# Split restore/save, matching ci.yml, and not the combined actions/cache
1893-
# action: the combined form's post-step saves even when the job FAILED,
1894-
# and build-console.sh writes the SHA stamp BEFORE it asserts the bundle
1895-
# canary — so a canary failure here would seed this shared, repo-scoped
1896-
# key with a stamped-but-broken dist that later runs restore and sail
1897-
# through. See ci.yml's split-restore note for the full rationale.
1898-
- name: Restore vendored Console dist (keyed on the objectui pin)
1899-
id: console-dist-cache
1900-
uses: actions/cache/restore@v6
1901-
with:
1902-
path: packages/console/dist
1903-
key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}
1904-
1905-
- name: Build vendored @objectstack/console SPA
1906-
# Clones objectstack-ai/objectui at the SHA pinned in .objectui-sha,
1907-
# builds @object-ui/console, and copies dist/ into
1908-
# packages/console/dist/. Must run before publish so the prepublishOnly
1909-
# guard in @objectstack/console passes.
1910-
if: steps.console-dist-cache.outputs.cache-hit != 'true'
1911-
run: bash scripts/build-console.sh
1912-
1913-
- name: Verify Console dist stamp matches pin
1914-
run: pnpm check:console-sha
1915-
1916-
# The spec-injection half of the same question, spelled exactly as
1917-
# ci.yml's Console Pin Gate spells it — both consumers of one cache key
1918-
# apply one check. check:console-sha answers only "which objectui SHA",
1919-
# and scripts/assert-console-spec-injection.mjs runs INSIDE
1920-
# build-console.sh, which the `if: cache-hit != 'true'` above skips — so
1921-
# on a cache hit nothing had asked whether the dist this job is about to
1922-
# PUBLISH bundles this tree's @objectstack/spec rather than the published
1923-
# tarball. --require-stamp because the vacuity check:console-sha tolerates
1924-
# (no dist, or a dist no build ever stamped) is wrong on a publish lane.
1925-
- name: Verify the restored Console dist bundles this tree's spec
1926-
env:
1927-
CONSOLE_DIST_CACHE_KEY: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}
1928-
run: pnpm check:console-injection --require-stamp
1929-
1930-
# Reached only with every console assertion above green: Actions steps
1931-
# default to `success()`, and nothing between the restore and here carries
1932-
# `if: always()` or `continue-on-error`. Saving only on a miss keeps an
1933-
# immutable entry from being rewritten on every release run.
1934-
- name: Save vendored Console dist
1935-
if: steps.console-dist-cache.outputs.cache-hit != 'true'
1936-
uses: actions/cache/save@v6
1937-
with:
1938-
path: packages/console/dist
1939-
key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}
1940-
19411857
- name: Downstream backward-compat smoke (live hotcrm)
19421858
# Pre-publish gate (#2035): the about-to-publish @objectstack/spec must
19431859
# not break a real third-party consumer pinned to a published release.
@@ -2031,8 +1947,8 @@ jobs:
20311947
run: bash scripts/release-spec-changes.sh --verify
20321948

20331949
# ──────────────────────────────────────────────────────────────────────
2034-
# The publish itself. `pnpm run release` = build + build-console +
2035-
# scripts/release-publish.sh, which is `changeset publish` followed by ONE
1950+
# The publish itself. `pnpm run release` = build (both scopes; the turbo
1951+
# cache makes it the build above) + scripts/release-publish.sh, which is `changeset publish` followed by ONE
20361952
# atomic `git push origin --tags` (#2191: the action's concurrent per-tag
20371953
# pushes raced GitHub's ref backend and lost ~half the tags).
20381954
#

‎.github/workflows/showcase-smoke.yml‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,13 @@ jobs:
5555
# built `dist/`. Build the whole dependency closure (excluding the
5656
# showcase's own app build, which serve --dev doesn't need).
5757
- run: pnpm turbo run build --filter=@objectstack/example-showcase^...
58-
# The console SPA served at /_console is vendored by build-console.sh
59-
# (shallow-clones objectui@.objectui-sha, builds @object-ui/console, copies
60-
# dist → packages/console/dist). Without it serve --dev warns "Console dist
61-
# not found" and the app shell never renders.
62-
- name: Vendor the pinned Console SPA
63-
run: bash scripts/build-console.sh
64-
# Self-test the drift guard: a fresh build-console.sh must stamp the dist
65-
# with the pinned SHA so `pnpm check:console-sha` reports in-sync. Catches
66-
# a regression where the stamp step is dropped or the pin/stamp diverge.
67-
- name: Verify Console SHA stamp matches pin
68-
run: pnpm check:console-sha
58+
# The console SPA served at /_console is @objectstack/console's dist: a
59+
# copy of the workspace's own ui/apps/console build, which `build` does not
60+
# produce (packages/console/build-dist.sh, run by the `build:dist` task).
61+
# Without it serve --dev warns "Console dist not found" and the app shell
62+
# never renders.
63+
- name: Build the Console SPA from ui/apps/console
64+
run: pnpm console:build
6965
- name: Install Playwright Chromium
7066
working-directory: examples/app-showcase
7167
run: pnpm exec playwright install --with-deps chromium

‎package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
"prepare": "node scripts/setup-git-hooks.mjs",
1919
"check:merge-driver": "node scripts/git-env.mjs --self-test && node scripts/git-merge-regen.mjs --self-test && node scripts/check-regen-pending.mjs --self-test",
2020
"version": "node scripts/release-pending-publish.mjs annotate-shipped; changeset version && node scripts/sync-protocol-version.mjs && node scripts/sync-template-versions.mjs && node scripts/sync-docs-image-tags.mjs && node scripts/sync-release-index-currency.mjs && node scripts/check-skill-compatibility-version.mjs --fix",
21-
"release": "pnpm run build && bash scripts/release-publish.sh",
21+
"release": "pnpm run build && pnpm run console:build && bash scripts/release-publish.sh",
2222
"docs:dev": "pnpm --filter @objectstack/docs dev",
2323
"docs:build": "pnpm --filter @objectstack/docs build",
2424
"docs:start": "pnpm --filter @objectstack/docs start",
2525
"os": "node packages/cli/bin/run.js",
26-
"console:build": "turbo run build --filter=@objectstack/console",
26+
"console:build": "turbo run build:dist --filter=@objectstack/console",
2727
"gen:sdui-lockstep": "node scripts/check-sdui-lockstep.mjs --update",
2828
"lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config",
2929
"i18n:extract": "tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --source-hashes --out=packages/platform-objects/src/apps/translations",

‎packages/cli/src/utils/console.not-built.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const NOT_INSTALLED_HEADLINE =
4747
'The ObjectStack Console is not installed, so this server has no Console to serve at /_console/.';
4848

4949
/** The framework repo's remedy. */
50-
const BUILD_REMEDY = 'pnpm objectui:build';
50+
const BUILD_REMEDY = 'pnpm console:build';
5151

5252
/** The prefix the terminal warning carries and the HTTP answer does not. */
5353
const WARNING_PREFIX = ' ⚠ ';
@@ -84,7 +84,7 @@ function frameworkTree(): { root: string; consoleDir: string } {
8484
writeJson(path.join(root, 'package.json'), {
8585
name: 'framework-root',
8686
private: true,
87-
scripts: { build: 'turbo run build', 'objectui:build': 'bash scripts/build-console.sh' },
87+
scripts: { build: 'turbo run build', 'console:build': 'turbo run build:dist --filter=@objectstack/console' },
8888
});
8989
return { root, consoleDir: writeUnbuiltConsole(path.join(root, 'packages', 'console')) };
9090
}
@@ -124,7 +124,7 @@ describe('a framework checkout with no built Console', () => {
124124
request = await serve(createConsoleNotBuiltPlugin(consoleDir));
125125
});
126126

127-
it.each(CONSOLE_REQUESTS)('GET %s answers 503 naming `pnpm objectui:build`', async (p) => {
127+
it.each(CONSOLE_REQUESTS)('GET %s answers 503 naming `pnpm console:build`', async (p) => {
128128
const res = await request(p);
129129
expect(res.status).toBe(503);
130130
expect(res.headers.get('content-type')).toMatch(/^text\/plain/);

‎packages/cli/src/utils/console.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,10 @@ export function hasConsoleDist(consolePath: string): boolean {
307307
// ─── "No dist" remedy — named for where the server runs ─────────────
308308

309309
/** The framework repo's root script that builds `packages/console/dist`. */
310-
const CONSOLE_BUILD_SCRIPT = 'objectui:build';
310+
const CONSOLE_BUILD_SCRIPT = 'console:build';
311311

312312
/**
313-
* The directory whose `package.json` declares `objectui:build` for this
313+
* The directory whose `package.json` declares `console:build` for this
314314
* console package, or `null`.
315315
*
316316
* Only the NEAREST `package.json` above `consolePath` is asked. In the
@@ -320,8 +320,7 @@ const CONSOLE_BUILD_SCRIPT = 'objectui:build';
320320
* intermediate directories carry none — so a project that merely sits inside
321321
* a framework checkout is never told to run a script that does not build the
322322
* console it actually resolved. The signal is the script itself rather than
323-
* the repo's layout or its `.objectui-sha` pin, so the remedy is named only
324-
* where it exists.
323+
* the repo's layout, so the remedy is named only where it exists.
325324
*/
326325
function findConsoleBuildRoot(consolePath: string): string | null {
327326
let dir = path.dirname(consolePath);
@@ -351,7 +350,7 @@ function findConsoleBuildRoot(consolePath: string): string | null {
351350
* Three places reach it, and each gets the remedy that works there:
352351
*
353352
* - the framework repo, where `packages/console/dist` is a gitignored build
354-
* that `pnpm build` never produces — `pnpm objectui:build`, run at the
353+
* that `pnpm build` never produces — `pnpm console:build`, run at the
355354
* root it was found under (it is a root script, so it does not resolve
356355
* from the example directory `pnpm dev` runs in);
357356
* - anywhere else, where the Console arrives prebuilt as
@@ -384,7 +383,7 @@ function describeConsoleDistMissing(consolePath: string | null, hostPaths: boole
384383
return (
385384
`Console dist not found at ${path.relative(buildRoot, dist)} — \`pnpm build\` does not produce it. ` +
386385
`Build it with \`pnpm ${CONSOLE_BUILD_SCRIPT}\` in ${hostPaths ? buildRoot : 'the root of the ObjectStack checkout this server runs from'} ` +
387-
`(uses a ../objectui checkout if present, else clones objectui at the pinned commit — needs network).`
386+
`(builds ui/apps/console, then copies its dist into packages/console).`
388387
);
389388
}
390389
return `Console dist not found${hostPaths ? ` at ${dist}` : ''} — ${reinstall}`;

‎packages/cli/src/utils/sdui-manifest.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
* ## The console leg is reached through `package.json` (#19922)
5555
*
5656
* The second place looked is the copy `@objectstack/console` ships in its
57-
* `dist/` — objectui's public-tier registry at the pinned commit, copied in by
58-
* `scripts/build-console.sh`. This leg used to ask for that file by its own
57+
* `dist/` — the tracked repo-root `sdui.manifest.json`, copied in by
58+
* `packages/console/build-dist.sh`. This leg used to ask for that file by its own
5959
* subpath, which the console's `exports` map does not publish (it publishes
6060
* `./package.json` alone): the resolve threw `ERR_PACKAGE_PATH_NOT_EXPORTED`, a
6161
* `catch` swallowed it, and a project with no manifest of its own was checked
@@ -105,7 +105,7 @@ export const CONSOLE_SDUI_MANIFEST = '@objectstack/console/dist/sdui.manifest.js
105105
/** The one subpath the console's `exports` map publishes. */
106106
const CONSOLE_PACKAGE_JSON = '@objectstack/console/package.json';
107107

108-
/** Where `scripts/build-console.sh` puts the manifest, relative to the console package root. */
108+
/** Where `packages/console/build-dist.sh` puts the manifest, relative to the console package root. */
109109
const CONSOLE_MANIFEST_IN_PACKAGE = 'dist/sdui.manifest.json';
110110

111111
/**

0 commit comments

Comments
 (0)