Repository navigation
feat(cli): os deploy and the os dev d key deploy to a self-hosted runtime remembered by os environments use-runtime - #31137
Conversation
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dLQEXWGsE2SZ3Qn5F
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dLQEXWGsE2SZ3Qn5F
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dLQEXWGsE2SZ3Qn5F
📓 Docs Drift CheckThis PR changes 1 package(s): 14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 35 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cad134230d33bfe5e2ec29c25df19d1404a062f1 && git checkout cad134230d33bfe5e2ec29c25df19d1404a062f1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cadd6aa280075295619c6a8ab4da75daab8aab94 b57c03aa03ab30e9502660953d02fc93accf3d4c && git checkout -B drift-repro cadd6aa280075295619c6a8ab4da75daab8aab94 && git merge --no-ff b57c03aa03ab30e9502660953d02fc93accf3d4c
node scripts/docs-audit/affected-docs.mjs --json cadd6aa280075295619c6a8ab4da75daab8aab94
|
|
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F 席位核对 — ACCEPT · domain:server#4 · Head-sha:
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 42381953842 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F Queue ejection read · build 42381953842 · |
Requested by 田騏 · project thread
Fixes #30888
Clause-②: no
Metadata-protocol: none
维护者速读
os environments use-runtime 地址 --token 密钥,记住一个私有部署的运行时(Docker 社区版或企业版)并设为部署目标;之后os deploy和os dev里按d不带任何参数就部署到它:先构建,再请运行时做一次预演(打印升级计划,含renamedFrom声明的改名),然后把构建产物直接装进去,按运行时的hotLoaded回答报告是否已生效。os package install不写-u/-t时也用它。~/.objectstack/cloud.json(0600)里独立的一段,云端登录/登出都保留它,密钥只发给它自己的地址。Q3 的「默认拒绝」由 install-local 自己判(Package upgrade plan: one framework comparison of installed vs new version (fields, types, renames, seeds) as a dry run; declaredrenamedFromis planned, an undeclared rename or type change is refused unless confirmed (epic cloud#3652 Q3) #30884 第二个 PR 尚未合入),本 PR 只负责把它的拒绝原样打印并以非零退出。回滚 = revert 本 PR。packages/cli/src/utils/cloud-config.ts(两半存储,登录/登出不丢目标)、packages/cli/src/commands/deploy.ts的deployToRuntime、packages/cli/src/utils/runtime-target.ts的runtimeInstallTarget(密钥只发给同一地址)。pnpm --filter @objectstack/cli exec vitest run --project unit test/deploy.test.ts test/environments-use-runtime.test.ts,预期全部通过,其中「a self-hosted runtime as the active target」一组 8 条。What changes
os environments use-runtime URL --token KEY(new) records a self-hosted runtime and an API key it issued, and makes it the deploy target. The name is a verb besideos environments switch, and "runtime" is the wordos package installalready uses for this server (OS_RUNTIME_URL/OS_RUNTIME_TOKEN). Before writing, the key is tried with one authenticatedGET /api/v1/marketplace/install-local. A rejected key (401/403), a runtime that does not serve the door (404), or one that cannot be reached is refused and nothing is recorded;--no-verifyrecords anyway.--forgetdrops the record.os environments switch ID, when it records intocloud.json, makes the cloud the target again and keeps the runtime remembered. Whichever ran last wins.Storage (card item 1: "the same protected file the cloud token uses").
~/.objectstack/cloud.json(0600) gains a deploy-target half,runtimeTarget: { url, token, recordedAt }anddeployTarget: 'cloud' | 'runtime'. It is never read by the cloud credential path.writeCloudConfigcarries it across every cloud write, soos cloud loginkeeps it.deleteCloudConfigkeeps it, soos cloud logoutclears only the session. A file that holds only the runtime half reads as "not logged in" to the cloud. OnlywriteDeployTargetRecordchanges the half.login.tsandlogout.tsare untouched.os deploy— when a runtime is the active target and--envis not given (card items 2–4):compileForDeploy, as the cloud path does.dryRun: true, from Package upgrade plan: one framework comparison of installed vs new version (fields, types, renames, seeds) as a dry run; declaredrenamedFromis planned, an undeclared rename or type change is refused unless confirmed (epic cloud#3652 Q3) #30884 PR 1) and prints the upgrade plan with the same renderer asos package install --dry-run. DeclaredrenamedFromrenames are listed.-devversion.--confirm-access-upgrade FINDING_SETpasses through to the install. The flag is refused when the target is the cloud.hotLoadedmeans live, with exit 0. A package the kernel could not load gives exit 1.Steps 2–3 run
os package installin-process, as the cloud path runsos package publish. Every refusal is printed once, in one place, and the retry lines nameos deploy. If the dry run is refused, nothing more is sent. If the runtime answers 404 (a runtime booted from a composed artifact, or one without the door), the deploy names the cause and exits 1. If the runtime predates the dry run, the deploy says so and exits 1.The
dkey names the remembered runtime in its offer line and runs the sameos deploywith no cloud login prompt.os package installwithout-u/$OS_RUNTIME_URLuses the remembered runtime. Without-t/--emailit uses that runtime's key, but only when the URL is the runtime's own (isSameControlPlane). Theurlflag loses its oclifdefaultso that an absent flag can be told from a typed one. With nothing remembered, the URL is stillhttp://localhost:3000. The 404 sentence now names the composed-artifact case.Item 5 (default port). The remembered target makes it moot, so the default stays
http://localhost:3000.Tests
test/deploy.test.ts, new top-level describe "a self-hosted runtime as the active target" (8). Covers: no-flag deploy (dry run, then inline install, plan printed, key sent, no-devsuffix, no cloud call); a dry-run refusal stops before the install; 404 named; runtime predating the dry run; access-upgrade refusal with retryos deploy --confirm-access-upgrade …, and the token passed through on rerun;hotLoaded: falsegives exit 1; a failed build installs nothing; the flag refused on the cloud target.test/environments-use-runtime.test.ts(new, 10). Covers: record after probe (0600, cloud session kept, cloud reads never see the key, still "not logged in" without a cloud login); 401 and 404 refused with nothing recorded;--no-verify; URL validation;--forget. Forcloud.json: login keeps the runtime target, logout keeps it (and deletes the file when nothing else is left), andswitchactivates the cloud.test/package-install-url-token-flags.test.ts, new describe (5). Covers: fallback to the remembered runtime and its key;-uto the same runtime keeps the key;-uto another server never gets the key; explicit-twins;--emailnever mixes the two; nothing remembered falls back to localhost:3000.test/dev-deploy-key.test.ts, new describe (2). Covers: the runtime offer line, andddeploying with no login; the wiring instartDevDeployKey.isSameControlPlane(stored.url, url)was replaced withtrueinruntime-target.ts. The test "-u naming another server never receives the remembered key" went red (1 failed / 13 passed), and the file was restored to its HEAD blob900b6fe7.Acceptance notes
origin/main, install-local has the dry run (Package upgrade plan: one framework comparison of installed vs new version (fields, types, renames, seeds) as a dry run; declaredrenamedFromis planned, an undeclared rename or type change is refused unless confirmed (epic cloud#3652 Q3) #30884 PR 1, feat: the package upgrade plan — compare the installed and incoming versions, an install dry run, os package install --dry-run #30983) but not the Q3 refusal,confirmSchemaUpgradeor--confirm-upgrade(Package upgrade plan: one framework comparison of installed vs new version (fields, types, renames, seeds) as a dry run; declaredrenamedFromis planned, an undeclared rename or type change is refused unless confirmed (epic cloud#3652 Q3) #30884 PR 2, back in the queue per handoff 6688284693). This PR prints the plan and stops on any refusal the door answers, from the dry run or the install. It adds no client-side copy of the verdict and no--confirm-upgradeflag, because the door has no body key for it to pass through. Until PR 2 lands, a deploy whose plan lists a suspected rename installs just asos package installdoes today, with the plan printed first. When PR 2 lands, its--confirm-upgradeshould be forwarded byos deploythe same way--confirm-access-upgradeis.os environments create --activatedoes not switch the deploy target back to the cloud; onlyswitchdoes.content/docs/deployment/cli.mdxforos deploy, the environments table andos package install) and the platform skill'soperations.mdrow are not updated here. Both are outside the claim's file surface and are listed for the seat.Verification
Local gates on head b57c03a (merge base 5f00374; origin/main merged once, already up to date)
First passes that exited 3 for a missing prerequisite were rebuilt and rerun (the exit-0 lines above):
check-plugin-teardown-shape --self-test(shallow clone, so the pinned control commit was fetched),check:dual-build-cjs-loads,check:i18n,check:i18n-coverage,check:i18n-walk-parity(built output missing, so those packages were built), andcheck:type-check-debt(lock queue timeout, rerun with a slot). The CLIintegrationtier is left to CI: the diff touches no spawn entry and no integration-tier file.Manual check on the Community image
NOT RUN: docker is not available in this container. Steps:
docker run -p 8080:8080 ghcr.io/objectstack-ai/objectstack(os start); create an API key in Setup → API Keys; in a fresh scaffold runos environments use-runtime http://localhost:8080 --token KEY, thenos dev, pressd, edit an object label, pressdagain, and confirm thatGET http://localhost:8080/api/v1/meta/object/NAMEserves the second label.🤖 Generated with Claude Code