Repository navigation
ci(docker): publish an on-demand public framework build base image - #31454
Conversation
…ild-base) A new workflow builds ghcr.io/objectstack-ai/framework-build-base:<sha> for one framework commit on main: workspace dependencies installed, every package built typeless, the Console built typed into packages/console/dist, with the same arguments as the framework stage downstream images run today. Downstream images that link the framework by path can start FROM it instead of building the whole framework on every cold cache or pin move. Triggers: workflow_dispatch (sha, optional rebuild) from main, repository_dispatch (framework-build-base, client_payload.sha), and a pull_request dry run that builds the base commit with the PR's Dockerfile and pushes nothing. A tag is written once; an existing tag reports its digest and builds nothing. The published image is read back logged out, and docker/framework-build-base/check-image.sh looks inside it. Claude-Session: https://claude.ai/code/session_01QUs3BuMyARAhKn8scCUz3t Co-authored-by: Claude <noreply@anthropic.com>
… the first build reading The dispatch-gates live guard requires every paths-filtered workflow to discover a check family or declare why not; this one builds an image and checks it with a script that needs the built image, so it declares no-check-families. The publish budget note now cites the dry run's cold reading (build 15m25s, job 18m45s). Claude-Session: https://claude.ai/code/session_01QUs3BuMyARAhKn8scCUz3t Co-authored-by: Claude <noreply@anthropic.com>
… in the marker census The live marker census pins every declaration by name, so the new workflow's declaration takes its row (eleven, from ten). Claude-Session: https://claude.ai/code/session_01QUs3BuMyARAhKn8scCUz3t Co-authored-by: Claude <noreply@anthropic.com>
|
domain:cloud#3 · os-zhuang · session_01QUs3BvJMebPthXe3qaXmcu Contract reviewServed-tier: Independent review of CI cost card 11a (#31441), ordered by seat 3 at the fable tier (Sam 2026-11-08 07:12Z: reviews of the CI cost cards run on fable). Inputs: the PR body, the net diff of the head against ① Derived judgments
② Semver levelNo package is published by this diff (a workflow, a Dockerfile, an ignore file, a shell script, a self-test). ③ Boundary flags
④ Design judgment
Findings:
No must-fix, no should-fix. The nits move no head; this record covers Implemented-by: VERDICT: PASS Reviewed at 2026-11-08T11:48Z · do not approve, merge, ready or label from this record; Tier H lands by the maintainer's hand. Generated by Claude Code |
|
repo:cloud#3 · os-zhuang · session_01QUs3BvRcDHXqRU4TSVE4u6 ACCEPT · objectstack#31441 (card 11a) · head 席位核对
|
Requested by Sam · project thread
Part of #31441. This PR adds the workflow and the image definition. The card's other half stays open after merge: the first dispatch for a named commit on
main, setting the new package Public once (GitHub creates a container package private), and the digest and build time read from that run.维护者速读
framework-build-base.yml和docker/framework-build-base/:按需把 main 上某个框架提交打成公开镜像ghcr.io/objectstack-ai/framework-build-base:SHA,里面是装好依赖、建好全部包和 console 的框架源码树/repo/objectstack。FROM它。.git、checkout 不留凭据、检查脚本断言镜像里没有.git。回滚 = revert 本 PR,已推的标签留在 ghcr 不影响任何东西。.github/workflows/framework-build-base.yml的publish任务(只认 main 上的提交、标签已存在就不重建、匿名读回);docker/framework-build-base/Dockerfile的两个 RUN(参数与 cloud Dockerfile 框架阶段逐项相同);check-image.sh。Framework Build Base Image检查,作业摘要里有 dry run 的构建耗时和镜像读数(node/pnpm 版本、console 构建标识、镜像大小),什么都没推。.github/workflows/**是 Tier H 受管面);合并后的第一次 dispatch 和把 ghcr 包设为 Public(一次,不可撤回)写在 Verification 里。What changes
.github/workflows/framework-build-base.yml(new). Three triggers, none per commit:workflow_dispatchwithsha(a full 40-hex commit onmain) and an optionalrebuildboolean;repository_dispatch, event typeframework-build-base, withclient_payload.sha: the hook a downstream pin move can call later;pull_requesttouching the workflow ordocker/framework-build-base/**: the dry run. Jobverifybuilds the PR's base commit with the PR's Dockerfile, loads it into the runner, looks inside, and pushes nothing. Its permissions arecontents: readonly.publish(the only job withpackages: write) runs only on a dispatch frommainor arepository_dispatch. It:main(the compare API must answeraheadoridentical);rebuild: true;mainand the requested commit as the build context, both withpersist-credentials: false;linux/amd64and pushesghcr.io/objectstack-ai/framework-build-base:SHA(no provenance or SBOM manifests, so the tag is one plain image manifest);check-image.shon it;FROM ...:SHA@DIGESTline.docker/framework-build-base/Dockerfile(new). The framework stage of cloud'sapps/cloud,apps/objectosandapps/objectos-eeDockerfiles at cloudac69aac, with the same arguments:node:22-slim,python3 make g++ ca-certificates git,CI=true,OS_SKIP_DTS=1, corepackpnpm@10.31.0,PNPM_HOME=/pnpm, store/pnpm/storein a cache mount, the in-tree authorable-surface anchor assert,pnpm install --frozen-lockfile --prefer-offline --prod=falseagainst registry.npmjs.org,turbo run build --filter='!@objectstack/docs' --filter='!./ui/**' --concurrency=1at a 12 GB heap, then the typed Consoleturbo run build:dist --filter=@objectstack/console --concurrency=1withVITE_APP_VERSION= first 8 hex of the commit, thenrm -rf ui. The tree lands at/repo/objectstack, the path cloud'slink:dependencies resolve to.docker/framework-build-base/Dockerfile.dockerignore(new):.git,node_modules,.pnpm-store,.turbo, the artifact-only list the three cloud ignore files share.docker/framework-build-base/check-image.sh(new): runs inside the image with no network and fails when there is a.gitanywhere under/repo, the workspace is not pnpm-installed,specorcorehas nodist, the Consoleindex.htmlis missing,ui/is still there, no Console asset carries the commit's 8-hex build identity, orOS_SKIP_DTSis not1. It prints node and pnpm versions, counts, and the image size.Why
Cloud pins the framework by commit, usually between releases, and links it as a workspace by path, so the existing runtime image (
ghcr.io/objectstack-ai/objectstack, an npm install of a released CLI) cannot serve it. Each of cloud's three image builds therefore installs and builds the whole framework and the Console itself, cold on every pin move or cache eviction, in a private repository's billed minutes. This image does that once per commit in this public repository. Cloud card 11b (objectstack-ai/cloud#3792, blocked on this card) switches the three Dockerfiles to startFROMit.Tests
node scripts/pm/dispatch-gates.mjs --commandsatb91dc73dfor this change set: 35 commands, all exit 0 (closing-keyword parity, self-test wiring, step collectors, workflow step-name quoting, workflow status functions, required contexts, stall-guard budget and headroom, pnpm acquisition, node version, NUL bytes and the rest).check:pm-dispatch-gatesruns separately; its result is in the card's report.bash -noncheck-image.sh; the workflow parses as YAML with jobsverify,publish.verifyrun (thepull_requesttrigger matches this PR's paths). Its build wall and image readings are in that run's job summary and in the card's report.Acceptance notes
VITE_SENTRY_*levers (empty DSN, kill switch off, PII default off); the other two did not. This image builds with the levers on. On this tree the Console no longer reads any of the three (client error reporting is runtime configuration,ui/packages/app-shell/src/observability/sentry.ts), so the levers change no behaviour; they stay so a build-time DSN can never ship silently in the customer artefact.apps/cloudandapps/objectosbuilders setnpm_config_registryandCOREPACK_NPM_REGISTRYto npmmirror as image ENV, andapps/objectosinstalls the framework from it. This image installs from registry.npmjs.org and sets no registry ENV. The lockfile pins integrity, so the installed tree is the same; a downstream stage that wants the mirror for its own install sets it afterFROM..gitignore. Two of the three cloud ignore files also exclude**/.gitignore; the self-managed one does not, and the cloud file records that the line moves no turbo hash. This image keeps.gitignore, as the self-managed image does.publish'sif:(main, or a repository_dispatch) is the policy, not a security boundary: anyone who can push a branch could edit it there. The commit check is what keeps a branch head or a fork's pull ref out of a published image.workflow_dispatchop allows only closed, fixed inputs per file, so it could not carry a commit anyway. The first dispatch is the maintainer's click unless a ruling says otherwise. Aworkflow_dispatchtrigger also only exists for this file once it is onmain, so nothing could be dispatched from this branch.timeout-minutes: 60on both jobs, with no reading of this workflow on record yet; the same stage runs about 20 minutes cold inside cloud's image builds on the same runner class. The reading from this PR'sverifyrun should replace the note.Verification
Framework Build Base Image / Build framework build base (dry run, no push)check: job summary shows the build wall and the readings.mainwith a commit (for example cloud's current.objectstack-sha,98583cd692c6a67891617529d2f7aec4963859b8). The summary prints the digest. Then, logged out:docker pull ghcr.io/objectstack-ai/framework-build-base:SHAsucceeds once the package is Public.docker buildx imagetools inspect ghcr.io/objectstack-ai/framework-build-base:SHA, or by dispatching the same commit again (an existing tag reports its digest and builds nothing).Generated by Claude Code