From 6ae104818e188e0a5d50f73b300ed197afadb004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E5=91=A8=E6=B6=9B?= Date: Tue, 23 Jun 2026 18:44:53 +0800 Subject: [PATCH] feat(cli)!: remove legacy os publish / os rollback commands The legacy direct-to-environment publish flow (write sys_environment_revision via os publish, switch revisions via os rollback) is superseded by the versioned-package flow (os package publish). Remove both commands, the now-dangling reference in the package publish header comment, and the unused client activateRevision() helper. BREAKING CHANGE: os publish and os rollback are removed; use os package publish. --- packages/cli/src/commands/package/publish.ts | 8 +- packages/cli/src/commands/publish.ts | 141 ------------------- packages/cli/src/commands/rollback.ts | 83 ----------- packages/client/src/index.ts | 12 -- 4 files changed, 4 insertions(+), 240 deletions(-) delete mode 100644 packages/cli/src/commands/publish.ts delete mode 100644 packages/cli/src/commands/rollback.ts diff --git a/packages/cli/src/commands/package/publish.ts b/packages/cli/src/commands/package/publish.ts index 9e6501c996..7e407b1ab6 100644 --- a/packages/cli/src/commands/package/publish.ts +++ b/packages/cli/src/commands/package/publish.ts @@ -12,10 +12,10 @@ * into sys_package_version.manifest_json (status=published). * 3. (optional) auto-install into a target environment via --env. * - * This is the "upload my local code to my org" path. It does NOT write - * sys_environment_revision (that's the legacy `objectstack publish` path, - * which still exists for backward compatibility while ADR-0006 v4 Phase B - * transitions complete). + * This is the "upload my local code to my org" path — the single supported + * way to publish. (The legacy direct-to-environment `os publish` / `os + * rollback` commands, which wrote sys_environment_revision, have been + * removed.) */ import { readFile } from 'node:fs/promises'; diff --git a/packages/cli/src/commands/publish.ts b/packages/cli/src/commands/publish.ts deleted file mode 100644 index 57c22e22b2..0000000000 --- a/packages/cli/src/commands/publish.ts +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { readFile } from 'node:fs/promises'; -import { resolve as resolvePath } from 'node:path'; -import { Args, Command, Flags } from '@oclif/core'; -import { printHeader, printKV, printSuccess, printError, printStep } from '../utils/format.js'; - -export default class Publish extends Command { - static override description = 'Publish a compiled artifact to ObjectStack Cloud'; - - static override args = { - artifact: Args.string({ description: 'Path to compiled artifact (default: dist/objectstack.json)', required: false }), - }; - - static override flags = { - server: Flags.string({ - char: 's', - description: 'ObjectStack Cloud control-plane URL', - env: 'OS_CLOUD_URL', - default: 'http://localhost:4000', - }), - environment: Flags.string({ - char: 'e', - description: 'Environment ID (required)', - env: 'OS_ENVIRONMENT_ID', - required: true, - }), - token: Flags.string({ - char: 't', - description: 'API key for ObjectStack Cloud', - env: 'OS_CLOUD_API_KEY', - }), - timeout: Flags.integer({ - description: 'Upload timeout in milliseconds (use a higher value on slow networks; 0 disables timeout)', - env: 'OS_CLOUD_TIMEOUT_MS', - default: 60_000, - }), - note: Flags.string({ - char: 'n', - description: 'Optional human-readable note to attach to this revision', - }), - branch: Flags.string({ - char: 'b', - description: 'Logical branch this publish belongs to (e.g. main, staging, feature-x). Default: main.', - env: 'OS_PUBLISH_BRANCH', - default: 'main', - }), - }; - - async run(): Promise { - const { args, flags } = await this.parse(Publish); - - printHeader('Publish Artifact'); - - try { - // 1. Locate the compiled artifact - const artifactPath = args.artifact - ? resolvePath(process.cwd(), args.artifact) - : resolvePath(process.cwd(), 'dist/objectstack.json'); - - printStep(`Loading artifact from ${artifactPath}...`); - let artifactRaw: string; - try { - artifactRaw = await readFile(artifactPath, 'utf-8'); - } catch (err: any) { - printError(`Cannot read artifact: ${err.message}. Run \`objectstack build\` first.`); - this.exit(1); - return; - } - - const artifact = JSON.parse(artifactRaw); - printSuccess(`Loaded artifact (${(artifactRaw.length / 1024).toFixed(1)} KB)`); - - // 2. POST to the control-plane publish endpoint - const qsParams = new URLSearchParams(); - if (flags.note) qsParams.set('note', flags.note); - if (flags.branch) qsParams.set('branch', flags.branch); - const qs = qsParams.toString(); - const serverUrl = `${flags.server}/api/v1/cloud/environments/${flags.environment}/metadata${qs ? `?${qs}` : ''}`; - printStep(`Publishing to ${serverUrl}...`); - - const response = await (async () => { - const controller = new AbortController(); - const timer = flags.timeout > 0 - ? setTimeout(() => controller.abort(), flags.timeout) - : undefined; - try { - return await fetch(serverUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(flags.token && { Authorization: `Bearer ${flags.token}` }), - }, - body: artifactRaw, - signal: controller.signal, - }); - } catch (err: any) { - if (err?.name === 'AbortError') { - throw new Error( - `Upload timed out after ${flags.timeout}ms. Use --timeout or set OS_CLOUD_TIMEOUT_MS to extend it (0 disables).`, - ); - } - throw err; - } finally { - if (timer) clearTimeout(timer); - } - })(); - - if (!response.ok) { - let errMsg: string; - try { - const errBody = await response.json() as any; - errMsg = errBody?.error ?? response.statusText; - } catch { - errMsg = response.statusText; - } - printError(`Publish failed (${response.status}): ${errMsg}`); - this.exit(1); - return; - } - - const result = await response.json() as any; - const data = result?.data ?? result; - - console.log(''); - printSuccess('Artifact published successfully'); - printKV(' Environment', flags.environment); - printKV(' Branch', data?.branch ?? flags.branch); - if (data?.commitId) printKV(' Commit', data.commitId); - const checksumStr = typeof data?.checksum === 'string' - ? data.checksum - : (data?.checksum?.value ?? null); - if (checksumStr) printKV(' Checksum', String(checksumStr).slice(0, 16)); - printKV(' Server', flags.server); - - } catch (error) { - printError((error as Error).message); - this.exit(1); - } - } -} diff --git a/packages/cli/src/commands/rollback.ts b/packages/cli/src/commands/rollback.ts deleted file mode 100644 index c842b4faab..0000000000 --- a/packages/cli/src/commands/rollback.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { Command, Flags } from '@oclif/core'; -import { printHeader, printKV, printSuccess, printError, printStep } from '../utils/format.js'; - -export default class Rollback extends Command { - static override description = 'Activate a previously published artifact revision (rollback or roll-forward)'; - - static override examples = [ - '<%= config.bin %> <%= command.id %> --commit 9ce1bd48dd70', - 'OS_ENVIRONMENT_ID=proj_crm <%= config.bin %> <%= command.id %> --commit abcdef123456', - ]; - - static override flags = { - server: Flags.string({ - char: 's', - description: 'ObjectStack Cloud control-plane URL', - env: 'OS_CLOUD_URL', - default: 'http://localhost:4000', - }), - environment: Flags.string({ - char: 'e', - description: 'Environment ID (required)', - env: 'OS_ENVIRONMENT_ID', - required: true, - }), - commit: Flags.string({ - char: 'c', - description: 'Commit ID (full or 12+ char prefix) of the revision to activate', - required: true, - }), - token: Flags.string({ - char: 't', - description: 'API key for ObjectStack Cloud', - env: 'OS_CLOUD_API_KEY', - }), - }; - - async run(): Promise { - const { flags } = await this.parse(Rollback); - - printHeader('Rollback / Activate Revision'); - - try { - const url = `${flags.server}/api/v1/cloud/environments/${flags.environment}/revisions/${flags.commit}/activate`; - printStep(`POST ${url}`); - - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(flags.token && { Authorization: `Bearer ${flags.token}` }), - }, - }); - - if (!response.ok) { - let errMsg: string; - try { - const errBody = await response.json() as any; - errMsg = errBody?.error ?? response.statusText; - } catch { - errMsg = response.statusText; - } - printError(`Activate failed (${response.status}): ${errMsg}`); - this.exit(1); - return; - } - - const result = await response.json() as any; - const data = result?.data ?? result; - - console.log(''); - printSuccess('Revision activated'); - printKV(' Environment', flags.environment); - if (data?.commitId) printKV(' Commit', data.commitId); - if (data?.previousCommitId) printKV(' Previous', data.previousCommitId); - printKV(' Server', flags.server); - } catch (error) { - printError((error as Error).message); - this.exit(1); - } - } -} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index eac6bc4d52..82806855db 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -866,18 +866,6 @@ export class ObjectStackClient { return this.unwrapResponse<{ environmentId: string; branch: string; demoted: number; totalRevisions: number }>(res); }, - /** - * Activate (rollback to) a previously-published revision by commit id. - * Marks the target revision is_current=true and demotes the prior one. - */ - activateRevision: async (id: string, commitId: string) => { - const res = await this.fetch( - `${this.baseUrl}/api/v1/cloud/environments/${encodeURIComponent(id)}/revisions/${encodeURIComponent(commitId)}/activate`, - { method: 'POST' }, - ); - return this.unwrapResponse<{ environmentId: string; commitId: string; activated: boolean; previousCommitId: string | null }>(res); - }, - /** * Retry provisioning for a project stuck in `failed` (or * `provisioning`) state. The server re-runs the driver handshake; on