Skip to content

ci(docs-deploy): deploy the Object UI docs site (www.objectui.org) from ui/apps/site - #27005

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-e7al7l
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-e7al7l

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #26993. This PR carries the code half. The card stays open for its proof: one workflow_dispatch run of the new job on main that prints the deployment URL, after the maintainer action below. Tier H (.github/workflows/**): draft until green, GOVERNED_APPROVERS review it, the maintainer merges, and no seat enqueues it.

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:根仓 .github/workflows/docs-deploy.yml 加第二个作业 Deploy objectui docs to Vercel (production),和现有 apps/docs 作业同一套做法(runner 上 vercel pull → vercel build --prod → vercel deploy --prebuilt --prod),部署 ui/apps/site 到 Vercel 项目 objectui(www.objectui.org)。原 apps/docs 作业一个字没改。
  • 为什么改:objectui 仓自己的部署 workflow 随 ui/.github/workflows/ 一起删了,ui/apps/site/vercel.json 又关了 Git 自动部署,所以合并后没有任何流程发布 www.objectui.org,18.2 之后的文档改动都上不了线。
  • 风险与回滚:只在 main 上按定时(每 6 小时)或手动触发运行,不影响任何 PR 检查。两个作业互相独立,新作业红了不影响 apps/docs 的部署。回滚 = revert 本 PR。
  • 先看:.github/workflows/docs-deploy.yml 末尾的 deploy-ui-site 作业,重点看 "Require the project's Root Directory to be ui/apps/site" 这一步和 build 步骤不带 token 的写法。
  • 两分钟验证:合并后在 Actions 页对 Docs Deploy 点 "Run workflow"(选 main)。Root Directory 改好之前,新作业会在 Root Directory 那一步红,报错里写明要改的设置;改好之后应该绿,并在 summary 里打印部署 URL。
  • 需要你做的:在 Vercel 控制台把项目 objectui(prj_v1rXIczaI7YGmXmseQCLFUFOFxTB)的 Settings → Build and Deployment → Root Directory 从 apps/site 改成 ui/apps/site,然后批准合并本 PR。

What changes

  • .github/workflows/docs-deploy.yml:
    • The header now names both sites and says the two jobs are independent.
    • New job deploy-ui-site, named Deploy objectui docs to Vercel (production). It keeps the shape of the existing deploy job:
      • the same refusal steps: a ref other than main, then a missing VERCEL_TOKEN, each an ::error:: and a red run;
      • checkout and ./.github/actions/setup-workspace;
      • vercel pull → vercel build --prod → vercel deploy --prebuilt --prod, all run from the repository root;
      • the same pinned VERCEL_CLI_VERSION (60.1.3) and the same team id; the project id is objectui's old one, prj_v1rXIczaI7YGmXmseQCLFUFOFxTB;
      • the post-build assertions against ui/apps/site/.next/BUILD_ID and .vercel/output/config.json;
      • the step-summary line with the deployment URL.
    • Two differences from deploy, both explained in the job's header:
      • A step after vercel pull reads settings.rootDirectory from the .vercel/project.json the pull wrote. It goes red, naming the dashboard setting and the value it needs, unless that reads ui/apps/site. The project's setting still reads apps/site from when the site lived in objectui. Without this step that would fail later inside vercel build, with an error that does not name the cause.
      • vercel build gets no VERCEL_TOKEN. Only pull and deploy call the Vercel API. This is the card's ask and objectui's original choice: the build runs every workspace package's lifecycle scripts.
    • Not ported, per the card: objectui's pinned-@objectstack restore, build and save steps. Every @objectstack package the site imports is a workspace package here.
  • The deploy job (apps/docs) is byte-for-byte unchanged.
  • ui/apps/site/vercel.json is unchanged. Its commands work in the merged layout, as measured below.

Tests

Cold local build of the exact production command, run the way Vercel runs it (cwd ui/apps/site, then cd ../..):

`pnpm turbo run build --filter=@object-ui/site` from `ui/`, head 137ca90
  • turbo run --dry=json from ui/ resolved this repository's root and scheduled the site with its whole closure: 67 build tasks, 30 in @object-ui packages and 37 in @objectstack packages (65 of them have work to do, per the run below).
  • The real run at --concurrency=2, under scripts/pm/os-verify-lock.sh: Tasks: 65 successful, 65 total · Cached: 0 cached, 65 total · WALL 596s EXIT 0.
  • ui/apps/site/.next/BUILD_ID was written.
  • pnpm exec turbo --version reads 2.11.5 from both the root and ui/, so the cd ../.. into ui/ does not pick up a different turbo.

The YAML parses: the jobs are deploy and deploy-ui-site, with 10 steps in the new job.

Verification

Derived gates (node scripts/pm/dispatch-gates.mjs --commands, 36 commands for this one-file diff). The 35 fast ones were run locally on 137ca90 and every one exited 0. They were run again on the final head 2defad3 (a comment-only change on top), and all 35 exited 0 again. The long one, GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push pnpm check:pm-dispatch-gates, ran on 2defad3: ✓ dispatch-gates self-test: 2224 cases pass. (fast 1935, slow 289), EXIT 0, 1191 s.

Local gate list, each `:: exit 0`
  • node scripts/check-closing-keyword-parity.mjs
  • node scripts/check-closing-keyword-parity.mjs --self-test
  • node scripts/check-comment-mask-corpus.mjs
  • node scripts/check-self-test-wired.mjs
  • node scripts/check-self-test-wired.mjs --self-test
  • node scripts/check-self-test-workflow-commands.mjs
  • node scripts/check-self-test-workflow-commands.mjs --self-test
  • node scripts/check-step-collectors.mjs
  • node scripts/check-step-collectors.mjs --self-test
  • node scripts/check-whole-set-label-write.mjs
  • node scripts/check-whole-set-label-write.mjs --self-test
  • node scripts/docs-audit/check-drift-comment.mjs
  • node scripts/pm/ci-failure.mjs --self-test
  • pnpm check:agent-test-spelling
  • pnpm check:aggregator-roster
  • pnpm check:cross-package-test-inputs
  • pnpm check:declared-population-live
  • pnpm check:driver-memory-census
  • pnpm check:gitlink-declared
  • pnpm check:node-version
  • pnpm check:nul-bytes
  • pnpm check:pm-expected-skips
  • pnpm check:pm-governed-merges
  • pnpm check:pm-post-stamped
  • pnpm check:pm-record-recognisers
  • pnpm check:pnpm-acquisition
  • pnpm check:pnpm-filter-targets
  • pnpm check:refd-timer-probe
  • pnpm check:required-contexts
  • pnpm check:shard-attestation
  • pnpm check:stall-guard-budget
  • pnpm check:stall-guard-headroom
  • pnpm check:watch-hint-literal
  • pnpm check:workflow-status-functions
  • pnpm check:workflow-step-name-quoting

CI-only families are read from this PR's check-runs.

NOT MEASURED, and what proves it:

  • vercel pull, the Root Directory step's reading of .vercel/project.json and the upload. They need VERCEL_TOKEN, which this container does not have. The settings.rootDirectory key is what the CLI's pull writes. The first workflow_dispatch run on main after the merge is the proof the card asks for.
  • Whether the organization's VERCEL_TOKEN can deploy to the objectui project. The token belongs to the same team as the objectstack project. This is part of the card's maintainer action.

Acceptance notes


Generated by Claude Code

objectui's own docs-deploy workflow went with the rest of the nested
ui/.github/workflows, and ui/apps/site/vercel.json keeps Vercel's Git
deployments off, so nothing has published www.objectui.org since the
merge. Add a second job to the root Docs Deploy workflow that builds the
site on the runner and uploads it with vercel deploy --prebuilt, the way
the apps/docs job does, with a step that refuses loudly when the Vercel
project's Root Directory is not ui/apps/site.

Claude-Session: https://claude.ai/code/session_01L9qibyAYPFxpNP4BCr7Z82
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 28, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review October 28, 2026 01:02
@github-actions github-actions Bot added the ci/cd label Oct 28, 2026
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 4eb1140 Oct 28, 2026
59 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-e7al7l branch October 28, 2026 01:56
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2defad3c9ad5d7f1ff1c7a0e96b5bf73af901694
Local-runs: none
Scope: .github/workflows/docs-deploy.yml + ui/apps/site/vercel.json, .github/actions/setup-workspace/action.yml, apps/docs/vercel.json, pnpm-workspace.yaml, scripts/check-aggregator-roster.mjs (read for the job-list pin the card names), the deleted ui/.github/workflows/docs-deploy.yml at be7897799b (the shape the card asks to keep)

① Derived judgments

Inputs read: card #26993 body + both comments (claim 6457381366, os-dev-report 6457838917); PR #27005 body, file list (one file), net diff vs main; the 59 check-runs on the head (all completed, none pending: every gate success or skipped, no failure; Governed Surface Queue Guard success x2; PR-body guards success); rulings cited by the card read by gh api GET: #25753 (item 2 "the docs site serves from the merged repo"), #26058 (roster row 10, "none; objectui's site is a separate Vercel project"), #26717 (row 7 "Build Docs — decide with F14 / docs-deploy"), and the deleted objectui workflow at #26058's parent be7897799b.

Accept-set and public-surface changes the diff implies — each judged:

  1. Trigger set: unchanged, right. on: stays schedule (43 0,6,12,18 * * *) + workflow_dispatch: {}. No push, pull_request, pull_request_target, workflow_run or merge_group is added anywhere in the hunks. A fork PR therefore never runs this workflow and has no path to secrets.VERCEL_TOKEN; there is no pull_request_target checkout-of-head pattern to misuse. Consistent with the maintainer's ruling quoted in the header (only main deploys, on free compute) and with the deleted objectui workflow's own trigger set.
  2. Permissions: unchanged, right. Workflow-level permissions: contents: read; the new job adds no job-level permissions: override and uses no GITHUB_TOKEN. That is the least privilege the job can hold (it only checks out). Repo convention surveyed across all 30 workflows at the head: every deploy/publish workflow (docker-publish, publish-smoke, ui-coverage-nightly, docs-deploy) carries contents: read or {}; this matches.
  3. Secret use: tighter than the existing job, right. VERCEL_TOKEN is bound as a step env in exactly three steps: Require the Vercel token, Pull the production project settings, Deploy the prebuilt output to production. The Build the site (vercel build --prod) step gets no token and no --token= flag, which is what card item 1 asks ("the token passed only to the steps that call the Vercel API") and what the deleted objectui workflow did. The build runs pnpm i + the full turbo closure (every workspace package's lifecycle scripts) with no credential in its environment: right. The token is passed as --token="$VERCEL_TOKEN" on the command line in pull/deploy, the same convention the existing deploy job uses; GitHub masks it in logs. Not a regression. (Observation, pre-existing and outside this card's acceptance "apps/docs job unchanged": the deploy job still hands VERCEL_TOKEN to its vercel build step. The asymmetry is now visible side by side and is a candidate follow-up, not a must-fix here.)
  4. Ref guard before token: right. Refuse any ref but main (github.ref != 'refs/heads/main' → ::error:: + exit 1) is the first step, before the token is bound to any step. A workflow_dispatch from another branch fails red in seconds. Pre-existing property, unchanged by the PR and inherent to workflow_dispatch: a dispatch from another branch runs THAT branch's copy of the file, so the guard protects only while the dispatched branch keeps it; only accounts with write access can dispatch, and no environment protection is used. Same exposure as the existing deploy job and the deleted objectui job; not introduced here.
  5. Action pinning: matches the repo convention, right. The new job uses actions/checkout@v7 (68/68 uses in .github/ at the head are @v7; the repo pins first-party actions/* to major tags and SHA-pins third-party actions — docker/*, lychee, changesets, osv-scanner, dorny/paths-filter) and the local composite ./.github/actions/setup-workspace. No third-party action is introduced. The Vercel CLI is pinned by exact version '60.1.3', the same literal as deploy, with a comment to move the two together.
  6. Concurrency: unchanged, right, and the header's independence claim holds. group: docs-deploy-production, cancel-in-progress: true is workflow-level; the two jobs have no needs: and no matrix, so one job going red does not cancel the other, as the new header says. A dispatch while a scheduled run is in flight cancels both jobs of the older run — the pre-existing, documented behaviour ("newest wins").
  7. Root Directory refusal step: right, and fails in the safe direction. It reads settings.rootDirectory from .vercel/project.json after vercel pull, and goes red naming the dashboard path and the required value unless it reads ui/apps/site. If the file is absent or the key is unset the string compares unequal and the step is still red — never a quiet green. This is the step the card's "Who acts" paragraph anticipates ("expected to fail ... on the old Root Directory ... do not treat it as a code defect"); it only makes the cause explicit. The dev marks the pull write of that key NOT MEASURED (no token in the container) — accepted as stated; the dispatch run is the proof the card already owes.
  8. Build/output assertions: right. ui/apps/site/.next/BUILD_ID and root .vercel/output/config.json, mirroring deploy. From the repository root, the CLI applies the project's Root Directory relative to its cwd and writes .vercel/output at the cwd — the same mechanics the existing deploy job relies on and its ::error file=... convention.
  9. ui/apps/site/vercel.json left unchanged: right. installCommand: pnpm i from ui/apps/site installs the whole workspace (pnpm-workspace.yaml at the head lists ui, ui/packages/*, ui/apps/*); buildCommand: cd ../.. && pnpm turbo run build --filter=@object-ui/site lands in ui/, which has no turbo.json (only turbo.objectui.json), so turbo resolves the repository root's turbo.json (build → dependsOn ^build, outputs include .next/**). The dev's cold measurement (65 tasks, exit 0, BUILD_ID written) is consistent with that reading. Card item 2's "fix that file if needed" → not needed: right.
  10. Pin steps not ported: right. The deleted workflow's objectstack-pin.mjs restore/build/save steps have no counterpart here because @objectstack/spec is workspace:^ in the site's package.json; card item 2 says so explicitly.
  11. No roster/self-test pins the docs-deploy job list: right, by reading. check-aggregator-roster.mjs reads WORKFLOW_FILES = ['ci.yml', 'lint.yml'] only; git grep -iE 'docs-deploy|Docs Deploy|deploy-ui-site' over scripts/, ui/scripts/, package.json finds no roster entry. Card item 3 reduces to the header text, which is updated; the derived gates (Lint & Repo Gates, Lint · CI, release & engine gates, Lint · repo & tooling gates) are green on the head.
  12. Scope vs the card and its claim: inside. The claim comment's file surface is docs-deploy.yml, vercel.json, the ci.yml ui-lane region (if a PR build were added), and the rosters/self-tests; the diff touches docs-deploy.yml only. ⛔-list (AGENTS.md, docs/adr/**, packages/**) untouched. Project id prj_v1rXIczaI7YGmXmseQCLFUFOFxTB and team id team_06nvlGKZCkwhzdXmRdxeCNnB match the card and the deleted workflow verbatim. The apps/docs deploy job is byte-for-byte unchanged (the diff's only hunk in that region is the file header), as the card's acceptance requires.
  13. Tier H landing: on course. .github/workflows/** is GOVERNED_TIER_H in GOVERNED_SURFACES; GOVERNED_APPROVERS = ['os-zhuang', 'hotlong']. An APPROVED review by os-zhuang exists at this exact head (2defad3c..., 01:02:30Z), hotlong is requested; the PR is no longer draft (consistent with "draft until green" — all 59 check-runs completed green/skipped). The maintainer merges; no seat enqueues it. The queue guard is green on the head.

Watch item (not a must-fix): timeout-minutes: 30 for a build the dev measured at 596 s at --concurrency=2 on a shared container, plus setup-workspace install, the pnpm i re-run inside vercel build, and the upload. The 4-vCPU runner at turbo's default concurrency should fit, and a timeout reds only this job; read the first dispatch run's duration before relying on the margin.

② Semver level

The diff publishes nothing: one CI workflow file, no packages/**, no schema, no public API. skip-changeset label present and correct; Check Changeset success on the pr-automation run (the second Check Changeset row is the skipped duplicate-event run, not a failure). Clause-②: no on the PR body and on the card: right — the change narrows no accept-set and widens no public surface. Metadata-protocol: none: right.

③ Boundary flags

Implemented-by: claude/project-thread-e7al7l
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants