@sfmc-bds/cli 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +5 -2
- package/dist/daemon/client.js.map +2 -2
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +4 -3
- package/dist/daemon/server.js.map +2 -2
- package/dist/i18n/locales/en.d.ts +1 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -0
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.js +1 -0
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/management/maintenance.js +3 -4
- package/dist/management/maintenance.js.map +2 -2
- package/dist/management/platform-worker.js +22 -3
- package/dist/management/platform-worker.js.map +2 -2
- package/dist/management/tasks.js +2 -9
- package/dist/management/tasks.js.map +2 -2
- package/dist/module-update/index.d.ts.map +1 -1
- package/dist/module-update/index.js +2 -1
- package/dist/module-update/index.js.map +2 -2
- package/package.json +6 -6
- package/scripts/module-install/lib/module-update.mjs +21 -2
- package/scripts/module-install/test/module-update.test.mjs +94 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/maintenance.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5907\u4EFD\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u4E0E\u4E16\u754C\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n fs.mkdirSync(destination, { recursive: true });\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n return destination;\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/** \u53EA\u56DE\u9000\u7A0B\u5E8F\u5185\u5BB9\uFF0C\u4E16\u754C\u3001\u73A9\u5BB6\u6743\u9650\u548C\u6570\u636E\u5E93\u59CB\u7EC8\u4FDD\u7559\u5728\u5F53\u524D\u72B6\u6001\u3002 */\nasync function restoreProgramSnapshot(id: string, kind: \"modules\" | \"bds\") {\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n const targets = snapshotTargets();\n if (!manifest.complete || path.resolve(manifest.targets[kind] ?? \"\") !== path.resolve(targets[kind])) throw new Error(\"\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D\");\n const saved = path.join(source, kind);\n if (kind === \"modules\") {\n if (!fs.existsSync(saved)) return;\n await fs.promises.rm(targets.modules, { recursive: true, force: true });\n await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });\n return;\n }\n const cfg = readJson<{ preserve?: string[] }>(path.join(ROOT, \"configs\", \"bds_updater.json\"), {});\n const preserved = new Set([\"worlds\", \"config\", \"server.properties\", \"allowlist.json\", \"whitelist.json\", \"permissions.json\", ...(cfg.preserve ?? []).map(name => name.split(/[\\\\/]/)[0]!)]);\n if (!fs.existsSync(saved)) throw new Error(\"\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD\");\n const target = path.resolve(targets.bds);\n if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807\");\n fs.mkdirSync(target, { recursive: true });\n for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });\n for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });\n}\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>, backup = true, rollbackPrograms?: \"modules\" | \"bds\"): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n if (backup) await createSnapshot(context.id, context.phase);\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n let programRolledBack = false;\n let rollbackError: string | undefined;\n if (rollbackPrograms) {\n context.phase(\"rollback-program\", \"\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E\");\n try { await restoreProgramSnapshot(context.id, rollbackPrograms); programRolledBack = true; }\n catch (failure) { rollbackError = String(failure); }\n }\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...(rollbackError ? { rollbackError } : {}), ...((error as { details?: object }).details ?? {}) } });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n fs.mkdirSync(path.join(ROOT, \".sfmc\", \"logs\"), { recursive: true });\n const fd = fs.openSync(path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\"), \"a\");\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT }, detached: true, windowsHide: true, stdio: [\"ignore\", fd, fd] });\n fs.closeSync(fd);\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { spawn } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { ROOT, isMonorepoLayout, isSourceCheckout } from \"../runtime.js\";\nimport { queryServicesRuntimeLocal, services, START_ORDER, type ServiceName } from \"../services.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { atomicJson, type TaskContext, type TaskStore } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport type { AttachmentPlan } from \"@sfmc-bds/management\";\nimport { isDaemonServer } from \"../daemon/role.js\";\nimport { callDaemon } from \"../daemon/client.js\";\nimport { platformReleaseNotes } from \"@sfmc-bds/management/node\";\n\nasync function runtimeRows() {\n if (isDaemonServer()) return queryServicesRuntimeLocal();\n const result = await callDaemon(\"status\");\n if (result.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001\");\n return result.rows;\n}\n\nconst require = createRequire(import.meta.url);\nexport function platformVersion(): string {\n if (isMonorepoLayout(ROOT)) return readJson<{ version: string }>(path.join(ROOT, \"packages\", \"meta\", \"package.json\"), { version: \"unknown\" }).version;\n if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;\n const active = readJson<{ version?: string }>(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), {});\n if (active.version) return active.version;\n const deployed = readJson<{ version?: string }>(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"package.json\"), {});\n if (deployed.version) return deployed.version;\n try { return (require(\"@sfmc-bds/sfmc/package.json\") as { version: string }).version; }\n catch { return (readJson<{ version: string }>(path.join(fileURLToPath(new URL(\"../../..\", import.meta.url)), \"package.json\"), { version: \"unknown\" })).version; }\n}\nexport async function attachmentPlan(includeReleaseNotes = true): Promise<AttachmentPlan> {\n const development = isMonorepoLayout(ROOT) || isSourceCheckout();\n const currentVersion = platformVersion();\n let targetVersion = currentVersion;\n if (!development) {\n const response = await fetch(\"https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest\", { signal: AbortSignal.timeout(15_000) });\n if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n const metadata = await response.json() as { version?: string };\n if (!metadata.version || !/^\\d+\\.\\d+\\.\\d+$/.test(metadata.version)) throw new Error(\"latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C\");\n targetVersion = metadata.version;\n }\n const [rows, releaseNotes] = await Promise.all([runtimeRows(), includeReleaseNotes && !development ? platformReleaseNotes(targetVersion) : undefined]);\n return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter(row => row.running && row.ownership === \"external\").map(row => row.name), steps: development ? [\"\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72\"] : [\"\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408\", \"\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\", \"\u5907\u4EFD\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u4E0E\u4E16\u754C\", \"\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1\", \"\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\"], ...(releaseNotes ? { releaseNotes } : {}) };\n}\nexport async function stopForMaintenance(names: ServiceName[], phase: TaskContext[\"phase\"]): Promise<ServiceName[]> {\n const before = await queryServicesRuntimeLocal();\n const affected = before.filter(row => names.includes(row.name) && row.running);\n if (affected.some(row => row.ownership === \"external\")) throw Object.assign(new Error(\"\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165\"), { code: \"conflict\" });\n phase(\"stop-services\", \"\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA\");\n const running = affected.map(row => row.name);\n for (const name of [...START_ORDER].reverse().filter(name => names.includes(name))) await services[name].stop(false, false);\n const after = await queryServicesRuntimeLocal();\n if (after.some(row => names.includes(row.name) && row.running)) throw new Error(\"\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C\");\n return running;\n}\nexport async function restoreRunning(names: ServiceName[], phase: TaskContext[\"phase\"]) {\n phase(\"restore\", \"\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1\");\n for (const name of START_ORDER.filter(name => names.includes(name))) {\n const result = await services[name].start();\n if (result.status === \"skipped\") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);\n }\n await verifyRunning(names);\n}\nexport async function verifyRunning(names: ServiceName[]) {\n if (!names.length) return;\n await new Promise(resolve => setTimeout(resolve, 2000));\n for (const name of names) {\n if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);\n }\n if (names.includes(\"db\")) {\n const cfg = readJson<{ db_port?: number; http_auth?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5000) });\n if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);\n }\n}\nfunction snapshotTargets() {\n const db = readJson<{ dbDir?: string }>(path.join(ROOT, \"configs\", \"db_config.json\"), {});\n const bds = resolveBdsContext().bdsRoot;\n return { configs: path.join(ROOT, \"configs\"), modules: path.join(ROOT, \"modules\"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? \"data/sfmc_data.db\")), bds };\n}\nexport async function createSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n phase(\"backup\", \"\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD\");\n const destination = path.join(ROOT, \".sfmc\", \"backups\", id);\n fs.mkdirSync(destination, { recursive: true });\n const targets = snapshotTargets();\n for (const target of Object.values(targets)) {\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62\");\n }\n for (const [key, target] of Object.entries(targets)) {\n if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });\n }\n atomicJson(path.join(destination, \"manifest.json\"), { createdAt: new Date().toISOString(), targets, complete: true });\n return destination;\n}\nexport async function restoreSnapshot(id: string, phase: TaskContext[\"phase\"]) {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7\");\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n if (!manifest.complete) throw new Error(\"\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D\");\n const targets = snapshotTargets();\n phase(\"restore-data\", \"\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C\");\n for (const [key, target] of Object.entries(targets)) {\n if (path.resolve(manifest.targets[key] ?? \"\") !== path.resolve(target)) throw new Error(\"\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4\");\n const saved = path.join(source, key);\n if (!fs.existsSync(saved)) continue;\n const absolute = path.resolve(target);\n if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807\");\n await fs.promises.rm(absolute, { recursive: true, force: true });\n await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });\n }\n return { restored: id };\n}\n/** \u53EA\u56DE\u9000\u7A0B\u5E8F\u5185\u5BB9\uFF0C\u4E16\u754C\u3001\u73A9\u5BB6\u6743\u9650\u548C\u6570\u636E\u5E93\u59CB\u7EC8\u4FDD\u7559\u5728\u5F53\u524D\u72B6\u6001\u3002 */\nasync function restoreProgramSnapshot(id: string, kind: \"modules\" | \"bds\") {\n const source = path.join(ROOT, \".sfmc\", \"backups\", id);\n const manifest = readJson<{ complete: boolean; targets: Record<string, string> }>(path.join(source, \"manifest.json\"), { complete: false, targets: {} });\n const targets = snapshotTargets();\n if (!manifest.complete || path.resolve(manifest.targets[kind] ?? \"\") !== path.resolve(targets[kind])) throw new Error(\"\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D\");\n const saved = path.join(source, kind);\n if (kind === \"modules\") {\n if (!fs.existsSync(saved)) return;\n await fs.promises.rm(targets.modules, { recursive: true, force: true });\n await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });\n return;\n }\n const cfg = readJson<{ preserve?: string[] }>(path.join(ROOT, \"configs\", \"bds_updater.json\"), {});\n const preserved = new Set([\"worlds\", \"config\", \"server.properties\", \"allowlist.json\", \"whitelist.json\", \"permissions.json\", ...(cfg.preserve ?? []).map(name => name.split(/[\\\\/]/)[0]!)]);\n if (!fs.existsSync(saved)) throw new Error(\"\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD\");\n const target = path.resolve(targets.bds);\n if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error(\"\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807\");\n fs.mkdirSync(target, { recursive: true });\n for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });\n for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });\n}\nexport async function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>, backup = true, rollbackPrograms?: \"modules\" | \"bds\"): Promise<T> {\n const running = await stopForMaintenance(names, context.phase);\n try {\n if (backup) await createSnapshot(context.id, context.phase);\n context.phase(\"execute\");\n const result = await action();\n context.phase(\"verify\");\n await restoreRunning(running, context.phase);\n return result;\n } catch (error) {\n // \u5931\u8D25\u540E\u4FDD\u6301\u505C\u670D\uFF0C\u4E0D\u731C\u6D4B\u672A\u77E5\u6570\u636E\u8FC1\u79FB\u662F\u5426\u53EF\u4EE5\u5B89\u5168\u6062\u590D\u8FD0\u884C\u3002\n for (const name of [...START_ORDER].reverse().filter(name => running.includes(name))) await services[name].stop(false, false).catch(() => {});\n let programRolledBack = false;\n let rollbackError: string | undefined;\n if (rollbackPrograms) {\n context.phase(\"rollback-program\", \"\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E\");\n try { await restoreProgramSnapshot(context.id, rollbackPrograms); programRolledBack = true; }\n catch (failure) { rollbackError = String(failure); }\n }\n throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: \"io\", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...(rollbackError ? { rollbackError } : {}), ...((error as { details?: object }).details ?? {}) } });\n }\n}\nexport async function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string) {\n if (isMonorepoLayout(ROOT)) throw new Error(\"\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B\");\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(target)) throw new Error(\"\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C\");\n const rows = await runtimeRows();\n if (rows.some(row => row.running && row.ownership === \"external\")) throw new Error(\"\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62\");\n const request = path.join(ROOT, \".sfmc\", \"operations\", `${context.id}.request.json`);\n const previous = readJson(path.join(ROOT, \".sfmc\", \"runtime\", \"active.json\"), { entry: fs.existsSync(path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\")) ? path.join(ROOT, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });\n atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter(row => row.running).map(row => row.name), previous });\n const entry = fileURLToPath(new URL(\"./platform-worker.js\", import.meta.url));\n const log = path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n fs.mkdirSync(path.dirname(log), { recursive: true });\n // \u4E0D\u628A\u7236\u8FDB\u7A0B\u7684\u65E5\u5FD7\u53E5\u67C4\u4EA4\u7ED9\u5B50\u8FDB\u7A0B\u3002Windows \u4E0A\u7EE7\u627F\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u7ED3\u675F\u5C1A\u672A\u8131\u79BB\u7684\u7EF4\u62A4\u8FDB\u7A0B\uFF0C\u5907\u4EFD\u4F1A\u505C\u5728\u534A\u622A\u4E14\u9501\u65E0\u4EBA\u91CA\u653E\u3002\n const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT, SFMC_MAINTENANCE_LOG: log }, detached: true, windowsHide: true, stdio: \"ignore\" });\n await new Promise<void>((resolve, reject) => { child.once(\"spawn\", resolve); child.once(\"error\", reject); });\n child.unref();\n const record = store.get(context.id) as ReturnType<TaskStore[\"get\"]> & { workerPid?: number };\n record.status = \"running\"; if (child.pid) record.workerPid = child.pid;\n store.save(record);\n return { detached: true };\n}\nexport async function runPnpm(args: string[], cwd: string) {\n const script = process.env.SFMC_PNPM_ENTRY;\n if (script) return runProcess(process.execPath, [script, ...args], cwd);\n const command = process.platform === \"win32\" ? \"pnpm.cmd\" : \"pnpm\";\n const crossSpawn = require(\"cross-spawn\") as typeof spawn;\n await new Promise<void>((resolve, reject) => {\n const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: \"ignore\" });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["require", "name"]
|
|
7
7
|
}
|
|
@@ -11,6 +11,15 @@ import { callDaemon, disconnectDaemonClient } from "../daemon/client.js";
|
|
|
11
11
|
import { ROOT } from "../runtime.js";
|
|
12
12
|
import { START_ORDER } from "../services.js";
|
|
13
13
|
import { readDaemonMeta } from "../daemon/paths.js";
|
|
14
|
+
function maintenanceLog(message) {
|
|
15
|
+
const file = process.env.SFMC_MAINTENANCE_LOG || path.join(ROOT, ".sfmc", "logs", "maintenance.log");
|
|
16
|
+
try {
|
|
17
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
18
|
+
fs.appendFileSync(file, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}
|
|
19
|
+
`);
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
async function main() {
|
|
15
24
|
const requestFile = process.argv[2];
|
|
16
25
|
if (!requestFile) throw new Error("\u7F3A\u5C11\u7EF4\u62A4\u4EFB\u52A1\u6587\u4EF6");
|
|
@@ -26,6 +35,7 @@ async function main() {
|
|
|
26
35
|
record.status = "running";
|
|
27
36
|
record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28
37
|
atomicJson(recordFile, record);
|
|
38
|
+
maintenanceLog(`phase ${name}${message ? ` ${message}` : ""}`);
|
|
29
39
|
};
|
|
30
40
|
const runtime = path.join(ROOT, ".sfmc", "runtime");
|
|
31
41
|
const release = path.join(runtime, "releases", `${request.target}-${request.id}`);
|
|
@@ -46,19 +56,26 @@ async function main() {
|
|
|
46
56
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
47
57
|
}
|
|
48
58
|
};
|
|
49
|
-
const
|
|
59
|
+
const stopServices = async () => {
|
|
50
60
|
const meta = readDaemonMeta();
|
|
51
|
-
if (!meta) return;
|
|
61
|
+
if (!meta) return meta;
|
|
52
62
|
const status = await callDaemon("status");
|
|
53
63
|
if (status.kind !== "status") throw new Error("\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u670D\u52A1\u72B6\u6001");
|
|
54
64
|
if (status.rows.some((row) => row.running)) {
|
|
55
65
|
const stopped = await callDaemon("maintenanceStop");
|
|
56
66
|
if (stopped.kind !== "status" || stopped.rows.some((row) => row.running)) throw new Error("\u670D\u52A1\u6CA1\u6709\u4F18\u96C5\u9000\u51FA");
|
|
57
67
|
}
|
|
68
|
+
return meta;
|
|
69
|
+
};
|
|
70
|
+
const exitDaemon = async (meta) => {
|
|
71
|
+
if (!meta) return;
|
|
58
72
|
await callDaemon("shutdown");
|
|
59
73
|
disconnectDaemonClient();
|
|
60
74
|
await waitExit(meta.pid);
|
|
61
75
|
};
|
|
76
|
+
const shutdown = async () => {
|
|
77
|
+
await exitDaemon(await stopServices());
|
|
78
|
+
};
|
|
62
79
|
try {
|
|
63
80
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
64
81
|
let acquired = false;
|
|
@@ -90,8 +107,9 @@ async function main() {
|
|
|
90
107
|
phase("stop-services");
|
|
91
108
|
const lock = readJson(path.join(ROOT, ".sfmc", "maintenance.lock"), { nonce: "" });
|
|
92
109
|
process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;
|
|
93
|
-
await
|
|
110
|
+
const daemon = await stopServices();
|
|
94
111
|
await createSnapshot(request.id, phase);
|
|
112
|
+
await exitDaemon(daemon);
|
|
95
113
|
phase("execute");
|
|
96
114
|
atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY });
|
|
97
115
|
switched = true;
|
|
@@ -138,6 +156,7 @@ async function main() {
|
|
|
138
156
|
record.status = "failed";
|
|
139
157
|
record.error = toManagementError(error);
|
|
140
158
|
record.result ??= { programRolledBack: false, backupId: request.id, dataRestoreRequired: switched, preparedEntry };
|
|
159
|
+
maintenanceLog(`failed ${record.error.message}`);
|
|
141
160
|
} finally {
|
|
142
161
|
record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
143
162
|
const last = record.phases.at(-1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/platform-worker.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\nimport { atomicJson } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport { createSnapshot, runPnpm } from \"./maintenance.js\";\nimport { callDaemon, disconnectDaemonClient } from \"../daemon/client.js\";\nimport { ROOT } from \"../runtime.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { readDaemonMeta } from \"../daemon/paths.js\";\n\n/** \u672C\u8FDB\u7A0B\u5728\u72EC\u7ACB Node \u4E2D\u5E38\u9A7B\uFF0C\u5E73\u53F0\u6362\u5305\u4E0D\u4F1A\u5220\u9664\u5176\u5F53\u524D\u7248\u672C\u76EE\u5F55\u3002 */\nasync function main() {\n const requestFile = process.argv[2];\n if (!requestFile) throw new Error(\"\u7F3A\u5C11\u7EF4\u62A4\u4EFB\u52A1\u6587\u4EF6\");\n const request = readJson<{ id: string; target: string; running: string[]; previous: { entry: string } | null }>(requestFile, { id: \"\", target: \"\", running: [], previous: null });\n if (!/^[\\w-]+$/.test(request.id) || !/^\\d+\\.\\d+\\.\\d+$/.test(request.target)) throw new Error(\"\u975E\u6CD5\u7EF4\u62A4\u8BF7\u6C42\");\n const recordFile = path.join(ROOT, \".sfmc\", \"operations\", `${request.id}.json`);\n let record = readJson<OperationRecord>(recordFile, {} as OperationRecord);\n const phase = (name: string, message?: string) => {\n record = readJson<OperationRecord>(recordFile, record);\n const previous = record.phases.at(-1); if (previous?.status === \"running\") previous.status = \"done\";\n record.phases.push({ name, status: \"running\", ...(message ? { message } : {}) }); record.status = \"running\"; record.updatedAt = new Date().toISOString(); atomicJson(recordFile, record);\n };\n const runtime = path.join(ROOT, \".sfmc\", \"runtime\");\n const release = path.join(runtime, \"releases\", `${request.target}-${request.id}`);\n const activeFile = path.join(runtime, \"active.json\");\n let switched = false;\n let preparedEntry = \"\";\n const waitExit = async (pid: number) => {\n const deadline = Date.now() + 30_000;\n for (;;) {\n let alive = true; try { process.kill(pid, 0); } catch (error) { alive = (error as NodeJS.ErrnoException).code !== \"ESRCH\"; }\n if (!alive) return;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u7981\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n };\n const shutdown = async () => {\n const meta = readDaemonMeta();\n if (!meta) return;\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u670D\u52A1\u72B6\u6001\");\n if (status.rows.some(row => row.running)) {\n const stopped = await callDaemon(\"maintenanceStop\");\n if (stopped.kind !== \"status\" || stopped.rows.some(row => row.running)) throw new Error(\"\u670D\u52A1\u6CA1\u6709\u4F18\u96C5\u9000\u51FA\");\n }\n await callDaemon(\"shutdown\"); disconnectDaemonClient(); await waitExit(meta.pid);\n };\n try {\n // \u63D0\u4EA4\u8FDB\u7A0B\u4FDD\u5B58 workerPid \u540E\u624D\u91CA\u653E\u9501\uFF1B\u7B49\u5F85\u63A5\u7BA1\uFF0C\u7981\u6B62\u7ADE\u6001\u8986\u76D6\u8BB0\u5F55\u3002\n await new Promise(resolve => setTimeout(resolve, 500));\n let acquired = false;\n for (let attempt = 0; attempt < 100 && !acquired; attempt++) {\n try {\n await withMaintenanceLock(ROOT, async () => {\n acquired = true;\n try {\n phase(\"prepare\", `\u51C6\u5907\u5E73\u53F0 ${request.target}`);\n fs.mkdirSync(release, { recursive: true });\n atomicJson(path.join(release, \"package.json\"), { private: true, dependencies: {} });\n await runPnpm([\"add\", \"--ignore-workspace\", \"--prod\", \"--ignore-scripts\", \"--save-exact\", `@sfmc-bds/sfmc@${request.target}`], release);\n const linked = path.join(release, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\");\n if (!fs.existsSync(linked)) throw new Error(\"\u5E73\u53F0\u5165\u53E3\u4E0D\u5B58\u5728\");\n // pnpm \u7684\u4F9D\u8D56\u94FE\u63A5\u5728\u771F\u5B9E\u5305\u76EE\u5F55\u65C1\u3002\u4ECE junction \u8DEF\u5F84 createRequire \u4F1A\u627E\u4E0D\u5230 @sfmc-bds/cli\u3002\n const entry = fs.realpathSync(linked);\n // \u53D1\u5E03\u7684\u5E73\u53F0\u5FC5\u987B\u5305\u542B\u672C\u6B21\u684C\u9762\u6240\u9700\u7BA1\u7406\u534F\u8BAE\u3002\n const resolver = createRequire(entry);\n const cli = path.join(path.dirname(resolver.resolve(\"@sfmc-bds/cli/package.json\")), \"dist\", \"management\", \"stdio.js\");\n if (!fs.existsSync(cli)) throw new Error(\"\u6700\u65B0\u5E73\u53F0\u5C1A\u672A\u53D1\u5E03\u7BA1\u7406\u5165\u53E3\uFF0C\u4FDD\u7559\u5F53\u524D\u90E8\u7F72\uFF1B\u8BF7\u5148\u5B8C\u6210\u5E73\u53F0\u53D1\u7248\");\n const targetSdk = readJson<{ version: string }>(resolver.resolve(\"@sfmc-bds/sdk/package.json\"), { version: \"\" }).version;\n const { satisfiesSdk } = await import(pathToFileURL(path.join(path.dirname(cli), \"../..\", \"scripts/module-install/lib/semver.mjs\")).href) as { satisfiesSdk: (version: string, range: string) => boolean };\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const folder of fs.readdirSync(modules)) {\n const pkg = readJson<{ peerDependencies?: Record<string, string> }>(path.join(modules, folder, \"package.json\"), {});\n const manifest = readJson<{ sdk?: string }>(path.join(modules, folder, \"sapi\", \"manifest.json\"), {});\n const range = pkg.peerDependencies?.[\"@sfmc-bds/sdk\"] ?? manifest.sdk;\n if (range && !satisfiesSdk(targetSdk, range)) throw new Error(`\u6A21\u5757 ${folder} \u4E0E\u76EE\u6807 SDK ${targetSdk} \u4E0D\u517C\u5BB9 (${range})\uFF1B\u4FDD\u7559\u56FA\u5B9A\u7248\u672C\u53CA\u672C\u5730\u94FE\u63A5\uFF0C\u5DF2\u505C\u6B62\u5347\u7EA7`);\n }\n preparedEntry = entry;\n phase(\"stop-services\");\n const lock = readJson<{ nonce: string }>(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), { nonce: \"\" });\n process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;\n await shutdown();\n await createSnapshot(request.id, phase);\n phase(\"execute\"); atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY }); switched = true;\n const launcher = path.join(runtime, \"launcher.mjs\");\n fs.writeFileSync(launcher, \"import fs from 'node:fs'; import path from 'node:path'; import {pathToFileURL,fileURLToPath} from 'node:url'; const base=path.dirname(fileURLToPath(import.meta.url)); const active=JSON.parse(fs.readFileSync(path.join(base,'active.json'),'utf8')); process.env.SFMC_DAEMON_ENTRY=fileURLToPath(import.meta.url); process.env.SFMC_NODE_BINARY=process.execPath; if(active.pnpm) process.env.SFMC_PNPM_ENTRY=active.pnpm; await import(pathToFileURL(active.entry).href);\\n\");\n for (const key of Object.keys(process.env)) if (key.startsWith(\"SFMC_SERVICE_\") && key.endsWith(\"_ENTRY\") || [\"SFMC_FETCH_MODULE\", \"SFMC_PLATFORM_VERSION\"].includes(key)) delete process.env[key];\n process.env.SFMC_DAEMON_ENTRY = launcher;\n phase(\"verify\");\n await runProcess(process.execPath, [launcher, \"status\"], ROOT);\n const handshake = await callDaemon(\"management\", { request: { id: request.id, method: \"handshake\" } });\n if (handshake.kind !== \"management\" || !handshake.response.ok || (handshake.response.result as { platformVersion?: string }).platformVersion !== request.target) throw new Error(\"\u76EE\u6807\u5E73\u53F0\u63E1\u624B\u9A8C\u8BC1\u5931\u8D25\");\n phase(\"restore\");\n for (const service of START_ORDER.filter(name => request.running.includes(name))) await callDaemon(\"start\", { name: service });\n await new Promise(resolve => setTimeout(resolve, 3000));\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\" || request.running.some(name => !status.rows.some(row => row.name === name && row.running))) throw new Error(\"\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\u672A\u5168\u90E8\u6062\u590D\");\n record.result = { version: request.target, backupId: request.id }; record.status = \"succeeded\";\n } catch (error) {\n // \u56DE\u9000\u4E5F\u5FC5\u987B\u5728\u540C\u4E00\u628A\u9501\u5185\u5B8C\u6210\uFF0C\u9632\u6B62\u5176\u4ED6\u5BA2\u6237\u7AEF\u5728\u5165\u53E3\u5207\u6362\u671F\u95F4\u5199\u5165\u3002\n let rolledBack = false;\n if (switched && request.previous) {\n try { await shutdown(); atomicJson(activeFile, request.previous); rolledBack = true; }\n catch (rollbackError) { phase(\"rollback-blocked\", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5165\u53E3\u548C\u5907\u4EFD\uFF0C\u4E0D\u8986\u76D6\u73A9\u5BB6\u6570\u636E: ${String(rollbackError)}`); }\n }\n record.result = { programRolledBack: rolledBack, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n throw error;\n }\n });\n } catch (error) {\n if (!acquired && (error as { code?: string }).code === \"locked\") { await new Promise(resolve => setTimeout(resolve, 200)); continue; }\n throw error;\n }\n }\n if (!acquired) throw new Error(\"\u65E0\u6CD5\u63A5\u7BA1\u7EF4\u62A4\u9501\");\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n record.result ??= { programRolledBack: false, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n } finally {\n record.updatedAt = new Date().toISOString(); const last = record.phases.at(-1); if (last) last.status = record.status === \"succeeded\" ? \"done\" : \"failed\";\n atomicJson(recordFile, record);\n fs.rmSync(requestFile, { force: true });\n disconnectDaemonClient();\n }\n}\nvoid main().catch(error => { process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`); process.exitCode = 1; });\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,yBAA+C;AACxD,SAAS,kBAAkB;AAC3B,SAAS,UAAU,kBAAkB;AACrC,SAAS,gBAAgB,eAAe;AACxC,SAAS,YAAY,8BAA8B;AACnD,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\nimport { atomicJson } from \"./tasks.js\";\nimport { readJson, runProcess } from \"./domain.js\";\nimport { createSnapshot, runPnpm } from \"./maintenance.js\";\nimport { callDaemon, disconnectDaemonClient } from \"../daemon/client.js\";\nimport { ROOT } from \"../runtime.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { readDaemonMeta } from \"../daemon/paths.js\";\n\n/**\n * \u7EF4\u62A4\u8FDB\u7A0B\u81EA\u5DF1\u5199\u65E5\u5FD7\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7236\u8FDB\u7A0B\u628A\u6807\u51C6\u8F93\u51FA\u63A5\u5230\u81EA\u5DF1\u7684\u6587\u4EF6\u53E5\u67C4\u65F6\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5BFC\u81F4\u8FD9\u6BB5\u8F93\u51FA\u4E22\u5931\uFF1B\u8FD9\u91CC\u76F4\u63A5\u8FFD\u52A0\u5230\u65E5\u5FD7\u6587\u4EF6\u3002\n */\nfunction maintenanceLog(message: string) {\n const file = process.env.SFMC_MAINTENANCE_LOG || path.join(ROOT, \".sfmc\", \"logs\", \"maintenance.log\");\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.appendFileSync(file, `${new Date().toISOString()} ${message}\\n`);\n } catch { /* \u65E5\u5FD7\u5199\u5931\u8D25\u65F6\u4ECD\u4EE5\u4EFB\u52A1\u8BB0\u5F55\u4E3A\u51C6 */ }\n}\n\n/** \u672C\u8FDB\u7A0B\u5728\u72EC\u7ACB Node \u4E2D\u5E38\u9A7B\uFF0C\u5E73\u53F0\u6362\u5305\u4E0D\u4F1A\u5220\u9664\u5176\u5F53\u524D\u7248\u672C\u76EE\u5F55\u3002 */\nasync function main() {\n const requestFile = process.argv[2];\n if (!requestFile) throw new Error(\"\u7F3A\u5C11\u7EF4\u62A4\u4EFB\u52A1\u6587\u4EF6\");\n const request = readJson<{ id: string; target: string; running: string[]; previous: { entry: string } | null }>(requestFile, { id: \"\", target: \"\", running: [], previous: null });\n if (!/^[\\w-]+$/.test(request.id) || !/^\\d+\\.\\d+\\.\\d+$/.test(request.target)) throw new Error(\"\u975E\u6CD5\u7EF4\u62A4\u8BF7\u6C42\");\n const recordFile = path.join(ROOT, \".sfmc\", \"operations\", `${request.id}.json`);\n let record = readJson<OperationRecord>(recordFile, {} as OperationRecord);\n const phase = (name: string, message?: string) => {\n record = readJson<OperationRecord>(recordFile, record);\n const previous = record.phases.at(-1); if (previous?.status === \"running\") previous.status = \"done\";\n record.phases.push({ name, status: \"running\", ...(message ? { message } : {}) }); record.status = \"running\"; record.updatedAt = new Date().toISOString(); atomicJson(recordFile, record);\n maintenanceLog(`phase ${name}${message ? ` ${message}` : \"\"}`);\n };\n const runtime = path.join(ROOT, \".sfmc\", \"runtime\");\n const release = path.join(runtime, \"releases\", `${request.target}-${request.id}`);\n const activeFile = path.join(runtime, \"active.json\");\n let switched = false;\n let preparedEntry = \"\";\n const waitExit = async (pid: number) => {\n const deadline = Date.now() + 30_000;\n for (;;) {\n let alive = true; try { process.kill(pid, 0); } catch (error) { alive = (error as NodeJS.ErrnoException).code !== \"ESRCH\"; }\n if (!alive) return;\n if (Date.now() > deadline) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u7981\u6B62\u5207\u6362\u5E73\u53F0\");\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n };\n /** \u53EA\u505C\u670D\u52A1\uFF0C\u4E0D\u9000\u51FA\u5B88\u62A4\u8FDB\u7A0B\u3002\u5907\u4EFD\u5FC5\u987B\u53D1\u751F\u5728\u5B88\u62A4\u8FDB\u7A0B\u8FD8\u6D3B\u7740\u7684\u65F6\u5019\u3002 */\n const stopServices = async () => {\n const meta = readDaemonMeta();\n if (!meta) return meta;\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\") throw new Error(\"\u65E0\u6CD5\u786E\u8BA4\u5F53\u524D\u670D\u52A1\u72B6\u6001\");\n if (status.rows.some(row => row.running)) {\n const stopped = await callDaemon(\"maintenanceStop\");\n if (stopped.kind !== \"status\" || stopped.rows.some(row => row.running)) throw new Error(\"\u670D\u52A1\u6CA1\u6709\u4F18\u96C5\u9000\u51FA\");\n }\n return meta;\n };\n /** \u6587\u4EF6\u5907\u4EFD\u5B8C\u6210\u540E\u518D\u8BA9\u65E7\u5B88\u62A4\u8FDB\u7A0B\u9000\u51FA\uFF0C\u907F\u514D\u9000\u51FA\u65F6\u628A\u672C\u7EF4\u62A4\u8FDB\u7A0B\u4E00\u8D77\u5E26\u8D70\u3001\u5907\u4EFD\u505C\u5728\u534A\u622A\u3002 */\n const exitDaemon = async (meta: { pid: number } | null) => {\n if (!meta) return;\n await callDaemon(\"shutdown\"); disconnectDaemonClient(); await waitExit(meta.pid);\n };\n const shutdown = async () => { await exitDaemon(await stopServices()); };\n try {\n // \u63D0\u4EA4\u8FDB\u7A0B\u4FDD\u5B58 workerPid \u540E\u624D\u91CA\u653E\u9501\uFF1B\u7B49\u5F85\u63A5\u7BA1\uFF0C\u7981\u6B62\u7ADE\u6001\u8986\u76D6\u8BB0\u5F55\u3002\n await new Promise(resolve => setTimeout(resolve, 500));\n let acquired = false;\n for (let attempt = 0; attempt < 100 && !acquired; attempt++) {\n try {\n await withMaintenanceLock(ROOT, async () => {\n acquired = true;\n try {\n phase(\"prepare\", `\u51C6\u5907\u5E73\u53F0 ${request.target}`);\n fs.mkdirSync(release, { recursive: true });\n atomicJson(path.join(release, \"package.json\"), { private: true, dependencies: {} });\n await runPnpm([\"add\", \"--ignore-workspace\", \"--prod\", \"--ignore-scripts\", \"--save-exact\", `@sfmc-bds/sfmc@${request.target}`], release);\n const linked = path.join(release, \"node_modules\", \"@sfmc-bds\", \"sfmc\", \"bin\", \"sfmc.mjs\");\n if (!fs.existsSync(linked)) throw new Error(\"\u5E73\u53F0\u5165\u53E3\u4E0D\u5B58\u5728\");\n // pnpm \u7684\u4F9D\u8D56\u94FE\u63A5\u5728\u771F\u5B9E\u5305\u76EE\u5F55\u65C1\u3002\u4ECE junction \u8DEF\u5F84 createRequire \u4F1A\u627E\u4E0D\u5230 @sfmc-bds/cli\u3002\n const entry = fs.realpathSync(linked);\n // \u53D1\u5E03\u7684\u5E73\u53F0\u5FC5\u987B\u5305\u542B\u672C\u6B21\u684C\u9762\u6240\u9700\u7BA1\u7406\u534F\u8BAE\u3002\n const resolver = createRequire(entry);\n const cli = path.join(path.dirname(resolver.resolve(\"@sfmc-bds/cli/package.json\")), \"dist\", \"management\", \"stdio.js\");\n if (!fs.existsSync(cli)) throw new Error(\"\u6700\u65B0\u5E73\u53F0\u5C1A\u672A\u53D1\u5E03\u7BA1\u7406\u5165\u53E3\uFF0C\u4FDD\u7559\u5F53\u524D\u90E8\u7F72\uFF1B\u8BF7\u5148\u5B8C\u6210\u5E73\u53F0\u53D1\u7248\");\n const targetSdk = readJson<{ version: string }>(resolver.resolve(\"@sfmc-bds/sdk/package.json\"), { version: \"\" }).version;\n const { satisfiesSdk } = await import(pathToFileURL(path.join(path.dirname(cli), \"../..\", \"scripts/module-install/lib/semver.mjs\")).href) as { satisfiesSdk: (version: string, range: string) => boolean };\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const folder of fs.readdirSync(modules)) {\n const pkg = readJson<{ peerDependencies?: Record<string, string> }>(path.join(modules, folder, \"package.json\"), {});\n const manifest = readJson<{ sdk?: string }>(path.join(modules, folder, \"sapi\", \"manifest.json\"), {});\n const range = pkg.peerDependencies?.[\"@sfmc-bds/sdk\"] ?? manifest.sdk;\n if (range && !satisfiesSdk(targetSdk, range)) throw new Error(`\u6A21\u5757 ${folder} \u4E0E\u76EE\u6807 SDK ${targetSdk} \u4E0D\u517C\u5BB9 (${range})\uFF1B\u4FDD\u7559\u56FA\u5B9A\u7248\u672C\u53CA\u672C\u5730\u94FE\u63A5\uFF0C\u5DF2\u505C\u6B62\u5347\u7EA7`);\n }\n preparedEntry = entry;\n phase(\"stop-services\");\n const lock = readJson<{ nonce: string }>(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), { nonce: \"\" });\n process.env.SFMC_MAINTENANCE_TOKEN = lock.nonce;\n const daemon = await stopServices();\n await createSnapshot(request.id, phase);\n await exitDaemon(daemon);\n phase(\"execute\"); atomicJson(activeFile, { entry, version: request.target, pnpm: process.env.SFMC_PNPM_ENTRY }); switched = true;\n const launcher = path.join(runtime, \"launcher.mjs\");\n fs.writeFileSync(launcher, \"import fs from 'node:fs'; import path from 'node:path'; import {pathToFileURL,fileURLToPath} from 'node:url'; const base=path.dirname(fileURLToPath(import.meta.url)); const active=JSON.parse(fs.readFileSync(path.join(base,'active.json'),'utf8')); process.env.SFMC_DAEMON_ENTRY=fileURLToPath(import.meta.url); process.env.SFMC_NODE_BINARY=process.execPath; if(active.pnpm) process.env.SFMC_PNPM_ENTRY=active.pnpm; await import(pathToFileURL(active.entry).href);\\n\");\n for (const key of Object.keys(process.env)) if (key.startsWith(\"SFMC_SERVICE_\") && key.endsWith(\"_ENTRY\") || [\"SFMC_FETCH_MODULE\", \"SFMC_PLATFORM_VERSION\"].includes(key)) delete process.env[key];\n process.env.SFMC_DAEMON_ENTRY = launcher;\n phase(\"verify\");\n await runProcess(process.execPath, [launcher, \"status\"], ROOT);\n const handshake = await callDaemon(\"management\", { request: { id: request.id, method: \"handshake\" } });\n if (handshake.kind !== \"management\" || !handshake.response.ok || (handshake.response.result as { platformVersion?: string }).platformVersion !== request.target) throw new Error(\"\u76EE\u6807\u5E73\u53F0\u63E1\u624B\u9A8C\u8BC1\u5931\u8D25\");\n phase(\"restore\");\n for (const service of START_ORDER.filter(name => request.running.includes(name))) await callDaemon(\"start\", { name: service });\n await new Promise(resolve => setTimeout(resolve, 3000));\n const status = await callDaemon(\"status\");\n if (status.kind !== \"status\" || request.running.some(name => !status.rows.some(row => row.name === name && row.running))) throw new Error(\"\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1\u672A\u5168\u90E8\u6062\u590D\");\n record.result = { version: request.target, backupId: request.id }; record.status = \"succeeded\";\n } catch (error) {\n // \u56DE\u9000\u4E5F\u5FC5\u987B\u5728\u540C\u4E00\u628A\u9501\u5185\u5B8C\u6210\uFF0C\u9632\u6B62\u5176\u4ED6\u5BA2\u6237\u7AEF\u5728\u5165\u53E3\u5207\u6362\u671F\u95F4\u5199\u5165\u3002\n let rolledBack = false;\n if (switched && request.previous) {\n try { await shutdown(); atomicJson(activeFile, request.previous); rolledBack = true; }\n catch (rollbackError) { phase(\"rollback-blocked\", `\u65E0\u6CD5\u5B89\u5168\u505C\u6B62\u76EE\u6807\u7248\u672C\uFF1B\u4FDD\u7559\u5165\u53E3\u548C\u5907\u4EFD\uFF0C\u4E0D\u8986\u76D6\u73A9\u5BB6\u6570\u636E: ${String(rollbackError)}`); }\n }\n record.result = { programRolledBack: rolledBack, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n throw error;\n }\n });\n } catch (error) {\n if (!acquired && (error as { code?: string }).code === \"locked\") { await new Promise(resolve => setTimeout(resolve, 200)); continue; }\n throw error;\n }\n }\n if (!acquired) throw new Error(\"\u65E0\u6CD5\u63A5\u7BA1\u7EF4\u62A4\u9501\");\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n record.result ??= { programRolledBack: false, backupId: request.id, dataRestoreRequired: switched, preparedEntry };\n maintenanceLog(`failed ${record.error.message}`);\n } finally {\n record.updatedAt = new Date().toISOString(); const last = record.phases.at(-1); if (last) last.status = record.status === \"succeeded\" ? \"done\" : \"failed\";\n atomicJson(recordFile, record);\n fs.rmSync(requestFile, { force: true });\n disconnectDaemonClient();\n }\n}\nvoid main().catch(error => { process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`); process.exitCode = 1; });\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/management/tasks.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { EventEmitter } from "node:events";
|
|
5
|
-
import { withMaintenanceLock } from "@sfmc-bds/management/node";
|
|
5
|
+
import { lockOwnerAlive, withMaintenanceLock } from "@sfmc-bds/management/node";
|
|
6
6
|
import { toManagementError } from "@sfmc-bds/management";
|
|
7
7
|
function atomicJson(file, value) {
|
|
8
8
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -19,14 +19,7 @@ class TaskStore extends EventEmitter {
|
|
|
19
19
|
for (const record of this.list()) {
|
|
20
20
|
if (record.status === "queued" || record.status === "running") {
|
|
21
21
|
const pid = record.workerPid;
|
|
22
|
-
|
|
23
|
-
if (pid) {
|
|
24
|
-
try {
|
|
25
|
-
process.kill(pid, 0);
|
|
26
|
-
active = true;
|
|
27
|
-
} catch {
|
|
28
|
-
}
|
|
29
|
-
}
|
|
22
|
+
const active = typeof pid === "number" && lockOwnerAlive(pid);
|
|
30
23
|
if (!active) {
|
|
31
24
|
record.status = "interrupted";
|
|
32
25
|
record.error = { code: "unavailable", message: "\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\u5907\u4EFD\u4E0E\u5B9E\u9645\u72B6\u6001\uFF0C\u4EFB\u52A1\u4E0D\u4F1A\u81EA\u52A8\u91CD\u53D1" };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/tasks.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport { withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\n\nexport function atomicJson(file: string, value: unknown) {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const temp = `${file}.${randomUUID()}.tmp`;\n fs.writeFileSync(temp, JSON.stringify(value, null, 2) + \"\\n\", { mode: 0o600 });\n fs.renameSync(temp, file);\n}\nexport type TaskContext = { phase: (name: string, message?: string) => void; id: string };\n\n/** \u4EFB\u52A1\u5C5E\u4E8E\u5B88\u62A4\u8FDB\u7A0B\u800C\u975E SSH \u4F1A\u8BDD\uFF1B\u5F02\u5E38\u6062\u590D\u53EA\u6807\u8BB0\uFF0C\u4E0D\u91CD\u653E\u5199\u5165\u3002 */\nexport class TaskStore extends EventEmitter {\n private queue: Promise<unknown> = Promise.resolve();\n private directory: string;\n constructor(private root: string) {\n super();\n this.directory = path.join(root, \".sfmc\", \"operations\");\n fs.mkdirSync(this.directory, { recursive: true });\n for (const record of this.list()) {\n if (record.status === \"queued\" || record.status === \"running\") {\n const pid = (record as OperationRecord & { workerPid?: number }).workerPid;\n
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport { lockOwnerAlive, withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\n\nexport function atomicJson(file: string, value: unknown) {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const temp = `${file}.${randomUUID()}.tmp`;\n fs.writeFileSync(temp, JSON.stringify(value, null, 2) + \"\\n\", { mode: 0o600 });\n fs.renameSync(temp, file);\n}\nexport type TaskContext = { phase: (name: string, message?: string) => void; id: string };\n\n/** \u4EFB\u52A1\u5C5E\u4E8E\u5B88\u62A4\u8FDB\u7A0B\u800C\u975E SSH \u4F1A\u8BDD\uFF1B\u5F02\u5E38\u6062\u590D\u53EA\u6807\u8BB0\uFF0C\u4E0D\u91CD\u653E\u5199\u5165\u3002 */\nexport class TaskStore extends EventEmitter {\n private queue: Promise<unknown> = Promise.resolve();\n private directory: string;\n constructor(private root: string) {\n super();\n this.directory = path.join(root, \".sfmc\", \"operations\");\n fs.mkdirSync(this.directory, { recursive: true });\n for (const record of this.list()) {\n if (record.status === \"queued\" || record.status === \"running\") {\n const pid = (record as OperationRecord & { workerPid?: number }).workerPid;\n // \u4E0E\u7EF4\u62A4\u9501\u540C\u4E00\u5957\u63A2\u6D3B\uFF1A\u6743\u9650\u4E0D\u8DB3\u4E0D\u7B97\u9000\u51FA\uFF0C\u907F\u514D\u628A\u8FD8\u5728\u8DD1\u7684\u7EF4\u62A4\u8FDB\u7A0B\u6807\u6210\u4E2D\u65AD\u3002\n const active = typeof pid === \"number\" && lockOwnerAlive(pid);\n if (!active) { record.status = \"interrupted\"; record.error = { code: \"unavailable\", message: \"\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\u5907\u4EFD\u4E0E\u5B9E\u9645\u72B6\u6001\uFF0C\u4EFB\u52A1\u4E0D\u4F1A\u81EA\u52A8\u91CD\u53D1\" }; this.save(record); }\n }\n }\n }\n get(id: string): OperationRecord {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u4EFB\u52A1\u7F16\u53F7\");\n return JSON.parse(fs.readFileSync(path.join(this.directory, `${id}.json`), \"utf8\")) as OperationRecord;\n }\n list(): OperationRecord[] {\n return fs.readdirSync(this.directory).filter(name => /^[a-f0-9-]{36}\\.json$/.test(name)).flatMap(name => {\n try { const record = JSON.parse(fs.readFileSync(path.join(this.directory, name), \"utf8\")) as OperationRecord; return typeof record.createdAt === \"string\" && Array.isArray(record.phases) ? [record] : []; } catch { return []; }\n }).sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, 200);\n }\n save(record: OperationRecord) {\n record.updatedAt = new Date().toISOString();\n atomicJson(path.join(this.directory, `${record.id}.json`), record);\n this.emit(\"operation\", record);\n }\n submit(kind: string, action: (context: TaskContext) => Promise<unknown>) {\n const record: OperationRecord = { id: randomUUID(), kind, title: kind, status: \"queued\", phases: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };\n this.save(record);\n this.queue = this.queue.catch(() => {}).then(async () => {\n try {\n await withMaintenanceLock(this.root, async () => {\n record.status = \"running\"; this.save(record);\n const phase = (name: string, message?: string) => {\n const previous = record.phases.at(-1);\n if (previous?.status === \"running\") { previous.status = \"done\"; previous.finishedAt = new Date().toISOString(); }\n record.phases.push({ name, status: \"running\", startedAt: new Date().toISOString(), ...(message ? { message } : {}) }); this.save(record);\n };\n record.result = await action({ phase, id: record.id });\n // \u72EC\u7ACB\u7EF4\u62A4\u8FDB\u7A0B\u63A5\u7BA1\u8BB0\u5F55\u4E4B\u540E\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u4E0D\u518D\u8986\u76D6\u5176\u72B6\u6001\u3002\n if ((record.result as { detached?: boolean } | undefined)?.detached) return;\n const last = record.phases.at(-1); if (last) { last.status = \"done\"; last.finishedAt = new Date().toISOString(); }\n record.status = \"succeeded\"; this.save(record);\n });\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n const last = record.phases.at(-1); if (last) last.status = \"failed\";\n this.save(record);\n }\n });\n return { operationId: record.id };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/module-update/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/module-update/index.ts"],"names":[],"mappings":"AA6HA,4BAA4B;AAC5B,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQlE;AAyGD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCrE;AAED,yBAAyB;AACzB,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BlE"}
|
|
@@ -21,7 +21,8 @@ const REASON_KEYS = {
|
|
|
21
21
|
"github-manual": "mod.update.reason.githubManual",
|
|
22
22
|
"missing-module": "mod.update.reason.missingModule",
|
|
23
23
|
"missing-dependency": "mod.update.reason.missingDependency",
|
|
24
|
-
"bad-version": "mod.update.reason.badVersion"
|
|
24
|
+
"bad-version": "mod.update.reason.badVersion",
|
|
25
|
+
retired: "mod.update.reason.retired"
|
|
25
26
|
};
|
|
26
27
|
function fetchModuleUrl() {
|
|
27
28
|
const script = resolveFetchModule();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/module-update/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * \u6A21\u5757\u66F4\u65B0\u7684 CLI \u5165\u53E3\u3002\n * \u8BA1\u5212\u4E0E\u6362\u5305\u5728 scripts/module-install/lib/module-update.mjs\uFF1B\n * \u8FD9\u91CC\u8D1F\u8D23\u786E\u8BA4\u4E0E\u6587\u6848\u3002\u5F00\u670D\u8DEF\u5F84\u5DF2\u5E9F\u5F03\uFF0C\u66F4\u65B0\u53EA\u80FD\u8D70\u663E\u5F0F\u547D\u4EE4\u3002\n */\nimport { confirm, isCancel } from \"@clack/prompts\";\nimport { stdin } from \"node:process\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { isCliTty } from \"../command-surface.js\";\nimport { t, type MessageKey } from \"../i18n/index.js\";\nimport { pushLog } from \"../logs.js\";\nimport { ROOT, resolveFetchModule } from \"../runtime.js\";\nimport { c } from \"../theme.js\";\n\n/** \u4E0E module-update.mjs \u7684\u5347\u7EA7\u5019\u9009\u4E00\u81F4\u3002 */\ninterface ModuleUpgrade {\n id: string;\n fromVersion: string | null;\n toVersion: string | null;\n spec: string;\n requires: string[];\n}\n\n/** \u4E0E module-update.mjs \u7684\u8DF3\u8FC7\u8BB0\u5F55\u4E00\u81F4\u3002 */\ninterface ModuleSkip {\n id: string;\n reason: string;\n fromVersion: string | null;\n toVersion: string | null;\n detail: string;\n}\n\n/** planModuleUpdates \u7684\u8FD4\u56DE\u3002 */\ninterface ModuleUpdatePlan {\n configSkipped: \"disabled\" | \"check-off\" | null;\n applyOnStart: boolean;\n failMode: \"continue\" | \"abort\";\n upgrades: ModuleUpgrade[];\n skipped: ModuleSkip[];\n}\n\n/** applyModuleUpgrades \u7684\u8FD4\u56DE\u3002 */\ninterface ModuleUpdateApplyResult {\n applied: ModuleUpgrade[];\n skipped: ModuleSkip[];\n failed: Array<{ id: string; message: string }>;\n}\n\n/** \u5B89\u88C5\u5668\u5BFC\u51FA\u7684\u4FDD\u7559\u542F\u505C\u5B89\u88C5\u53E3\u3002 */\ninterface FetchModuleApi {\n installPreservingLock: (id: string, spec: string) => Promise<void>;\n}\n\n/** module-update.mjs \u5BFC\u51FA\u9762\u3002\u663E\u5F0F\u66F4\u65B0\u547D\u4EE4\u4F9D\u8D56\u8FD9\u4E00\u5C42\u3002 */\ninterface ModuleUpdateApi {\n ensureModuleUpdateConfigFile: (root?: string) => string;\n planModuleUpdates: (options: {\n root?: string;\n startup?: boolean;\n allowMajor?: boolean;\n ids?: string[];\n }) => Promise<ModuleUpdatePlan>;\n applyModuleUpgrades: (\n upgrades: ModuleUpgrade[],\n deps: {\n root: string;\n failMode: \"continue\" | \"abort\";\n install: (id: string, spec: string) => Promise<void>;\n resync?: (id: string) => void;\n }\n ) => Promise<ModuleUpdateApplyResult>;\n resyncInstalledCatalog: (folder: string) => void;\n setModulePinAuto: (\n root: string,\n id: string,\n auto: boolean\n ) => { ok: true; code: \"ok\"; id: string; auto: boolean } | { ok: false; code: \"missing\" | \"locked\"; id?: string };\n QUIET_SKIP_REASONS: Set<string>;\n}\n\nconst REASON_KEYS: Record<string, MessageKey> = {\n \"auto-off\": \"mod.update.reason.autoOff\",\n \"local-source\": \"mod.update.reason.localSource\",\n \"registry-offline\": \"mod.update.reason.registryOffline\",\n \"no-target\": \"mod.update.reason.noTarget\",\n \"no-installed-version\": \"mod.update.reason.noInstalledVersion\",\n \"up-to-date\": \"mod.update.reason.upToDate\",\n \"installed-newer\": \"mod.update.reason.installedNewer\",\n major: \"mod.update.reason.major\",\n sdk: \"mod.update.reason.sdk\",\n \"dependency-failed\": \"mod.update.reason.dependencyFailed\",\n \"github-manual\": \"mod.update.reason.githubManual\",\n \"missing-module\": \"mod.update.reason.missingModule\",\n \"missing-dependency\": \"mod.update.reason.missingDependency\",\n \"bad-version\": \"mod.update.reason.badVersion\",\n};\n\n/** \u5B9A\u4F4D fetch-module.mjs\u3002\u627E\u4E0D\u5230\u65F6\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u7ED9\u51FA\u5B89\u88C5\u63D0\u793A\u3002 */\nfunction fetchModuleUrl(): URL | null {\n const script = resolveFetchModule();\n if (!script) return null;\n return pathToFileURL(script);\n}\n\n/** \u4E0E fetch-module \u540C\u76EE\u5F55\u7684\u66F4\u65B0\u5E93\u3002\u4F7F\u7528\u6587\u4EF6\u7CFB\u7EDF\u8DEF\u5F84\u62FC\u63A5\uFF0C\u907F\u514D Windows \u4E0B file URL \u7684 pathname \u88AB dirname \u62C6\u574F\u3002 */\nfunction updateLibUrl(): URL | null {\n const script = resolveFetchModule();\n if (!script) return null;\n return pathToFileURL(path.join(path.dirname(script), \"lib\", \"module-update.mjs\"));\n}\n\nasync function loadUpdateApi(): Promise<ModuleUpdateApi | null> {\n const url = updateLibUrl();\n if (!url) return null;\n return (await import(url.href)) as ModuleUpdateApi;\n}\n\nasync function loadFetchApi(): Promise<FetchModuleApi | null> {\n const url = fetchModuleUrl();\n if (!url) return null;\n return (await import(url.href)) as FetchModuleApi;\n}\n\n/** \u7F3A\u6587\u4EF6\u65F6\u5199\u5165\u9ED8\u8BA4\u7B56\u7565\u3002\u5931\u8D25\u4E0D\u5F71\u54CD\u5176\u4ED6\u547D\u4EE4\u3002 */\nexport async function ensureModuleUpdateConfigFile(): Promise<void> {\n try {\n const api = await loadUpdateApi();\n api?.ensureModuleUpdateConfigFile(ROOT);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n pushLog(message, \"module\", \"warn\");\n }\n}\n\n/**\n * REPL \u5904\u4E8E raw \u6A21\u5F0F\u65F6\uFF0C\u5148\u6682\u65F6\u5173\u6389\uFF0C\u8BA9\u786E\u8BA4\u63D0\u793A\u80FD\u8BFB\u5230\u6574\u884C\u3002\n */\nasync function withCookedStdin<T>(fn: () => Promise<T>): Promise<T> {\n const wasRaw = stdin.isRaw === true;\n if (wasRaw) stdin.setRawMode(false);\n try {\n return await fn();\n } finally {\n if (wasRaw) stdin.setRawMode(true);\n }\n}\n\n/** \u8DF3\u8FC7\u539F\u56E0\u7684\u53EF\u8BFB\u6587\u6848\u3002\u672A\u77E5\u539F\u56E0\u4FDD\u6301\u539F\u6837\u3002 */\nfunction reasonText(reason: string, detail: string): string {\n const key = REASON_KEYS[reason];\n const label = key ? t(key) : reason;\n return detail ? `${label} (${detail})` : label;\n}\n\nfunction versionText(value: string | null): string {\n return value || \"?\";\n}\n\n/**\n * \u628A\u8BA1\u5212\u683C\u5F0F\u5316\u6210\u591A\u884C\u3002\n * \u6CA1\u70B9\u540D\u6A21\u5757\u3001\u4E5F\u6CA1 --check \u65F6\uFF0C\u9690\u85CF auto-off / local-source / up-to-date\u3002\n * \u70B9\u540D\u4E86 id \u6216 --check \u65F6\u5C55\u5F00\u8FD9\u4E9B\u539F\u56E0\u548C\u7248\u672C\u3002quietEmpty \u4E3A true \u65F6\u4E0D\u8F93\u51FA\u7A7A\u7ED3\u679C\u3002\n */\nfunction formatPlan(\n plan: ModuleUpdatePlan,\n quiet: Set<string>,\n showAll: boolean,\n quietEmpty = false\n): string[] {\n if (plan.configSkipped === \"disabled\") return quietEmpty ? [] : [c.yellow(t(\"mod.update.disabled\"))];\n if (plan.configSkipped === \"check-off\") return quietEmpty ? [] : [c.dim(t(\"mod.update.checkOff\"))];\n const lines: string[] = [];\n for (const item of plan.skipped) {\n if (!showAll && quiet.has(item.reason)) continue;\n /** \u8DF3\u8FC7\u884C\u4E0A\u7684\u7248\u672C\u5BF9\u7167\u3002\u6709\u7D22\u5F15\u76EE\u6807\u65F6\u8BA9\u670D\u4E3B\u770B\u5230 1.0.0 -> 1.2.0\uFF0C\u800C\u4E0D\u662F\u53EA\u770B\u5230\u539F\u56E0\u3002 */\n const versions =\n item.fromVersion || item.toVersion ? ` ${versionText(item.fromVersion)} -> ${versionText(item.toVersion)}` : \"\";\n lines.push(c.dim(` ${item.id}${versions} ${reasonText(item.reason, item.detail)}`));\n }\n for (const item of plan.upgrades) {\n lines.push(\n c.green(\n ` ${t(\"mod.update.upgrade\", {\n id: item.id,\n from: versionText(item.fromVersion),\n to: versionText(item.toVersion),\n })}`\n )\n );\n }\n if (plan.upgrades.length === 0 && lines.length === 0 && !quietEmpty) lines.push(c.dim(t(\"mod.update.none\")));\n return lines;\n}\n\nfunction formatApply(result: ModuleUpdateApplyResult, hintReload: boolean): string[] {\n const lines: string[] = [];\n for (const item of result.applied) {\n lines.push(\n c.green(\n t(\"mod.update.applied\", {\n id: item.id,\n from: versionText(item.fromVersion),\n to: versionText(item.toVersion),\n })\n )\n );\n }\n for (const item of result.skipped) {\n lines.push(c.yellow(` ${item.id} ${reasonText(item.reason, item.detail)}`));\n }\n for (const item of result.failed) {\n lines.push(c.red(t(\"mod.update.failed\", { id: item.id, message: item.message })));\n }\n if (hintReload && result.applied.length > 0) lines.push(c.yellow(t(\"mod.update.reloadHint\")));\n return lines;\n}\n\ninterface UpdateFlags {\n check: boolean;\n yes: boolean;\n allowMajor: boolean;\n ids: string[];\n}\n\n/** \u89E3\u6790 mod update \u7684\u65D7\u6807\u3002\u672A\u77E5\u65D7\u6807\u8FD4\u56DE null\u3002 */\nfunction parseUpdateFlags(args: string[]): UpdateFlags | null {\n const flags: UpdateFlags = { check: false, yes: false, allowMajor: false, ids: [] };\n for (const arg of args) {\n if (arg === \"--check\") flags.check = true;\n else if (arg === \"--yes\") flags.yes = true;\n else if (arg === \"--allow-major\") flags.allowMajor = true;\n else if (arg.startsWith(\"--\")) return null;\n else flags.ids.push(arg);\n }\n return flags;\n}\n\n/**\n * mod update\uFF1A\u5148\u51FA\u8BA1\u5212\uFF0C\u4EA4\u4E92\u786E\u8BA4\u540E\u518D\u6362\u5305\u3002\n * --check \u53EA\u62A5\u544A\u3002\u6A21\u5757\u66F4\u65B0\u53EA\u5728\u6267\u884C\u672C\u547D\u4EE4\u65F6\u8FDB\u884C\u3002\n */\nexport async function cmdModuleUpdate(args: string[]): Promise<string> {\n const flags = parseUpdateFlags(args);\n if (!flags) return c.yellow(t(\"mod.update.usage\"));\n const api = await loadUpdateApi();\n if (!api) return c.red(t(\"mod.fetchMissing\"));\n const plan = await api.planModuleUpdates({\n root: ROOT,\n allowMajor: flags.allowMajor,\n ids: flags.ids,\n });\n /** \u70B9\u540D\u4E86\u6A21\u5757\u6216\u663E\u5F0F --check \u65F6\u5C55\u5F00\u5B89\u9759\u8DF3\u8FC7\uFF1B\u6CA1\u70B9\u540D\u65F6\u4ECD\u53EF\u6536\u6210\u300C\u6CA1\u6709\u53EF\u66F4\u65B0\u7684\u6A21\u5757\u300D\u3002 */\n const revealQuietSkips = flags.check || flags.ids.length > 0;\n const lines = formatPlan(plan, api.QUIET_SKIP_REASONS, revealQuietSkips);\n if (flags.check || plan.configSkipped || plan.upgrades.length === 0) return lines.join(\"\\n\");\n if (!flags.yes) {\n if (!isCliTty()) return [...lines, c.yellow(t(\"mod.update.needYes\"))].join(\"\\n\");\n const ans = await withCookedStdin(() =>\n confirm({\n message: t(\"mod.update.confirm\", { count: plan.upgrades.length }),\n initialValue: true,\n })\n );\n if (isCancel(ans) || !ans) return [...lines, c.dim(t(\"mod.update.cancelled\"))].join(\"\\n\");\n }\n const fetchApi = await loadFetchApi();\n if (!fetchApi) return c.red(t(\"mod.fetchMissing\"));\n const result = await api.applyModuleUpgrades(plan.upgrades, {\n root: ROOT,\n failMode: plan.failMode,\n install: fetchApi.installPreservingLock,\n resync: api.resyncInstalledCatalog,\n });\n return [...lines, ...formatApply(result, true)].join(\"\\n\");\n}\n\n/** \u5F00\u670D\u8DEF\u5F84\u5DF2\u5E9F\u5F03\uFF0C\u66F4\u65B0\u53EA\u80FD\u8D70\u663E\u5F0F\u547D\u4EE4\u3002 */\nexport async function runModuleUpdatesOnBdsStart(): Promise<void> {\n return;\n}\n\n/**\n * mod pin <id> --auto on|off\n * \u94FE\u63A5\u548C\u672C\u5730\u76EE\u5F55\u4E0D\u80FD\u6253\u5F00\u81EA\u52A8\u66F4\u65B0\u3002\n */\nexport async function cmdModulePin(args: string[]): Promise<string> {\n let id = \"\";\n let auto: boolean | null = null;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--auto\") {\n const value = args[++i];\n if (value === \"on\") auto = true;\n else if (value === \"off\") auto = false;\n else return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg?.startsWith(\"--auto=\")) {\n const value = arg.slice(\"--auto=\".length);\n if (value === \"on\") auto = true;\n else if (value === \"off\") auto = false;\n else return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg?.startsWith(\"--\")) {\n return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg) {\n id = arg;\n }\n }\n if (!id || auto === null) return c.yellow(t(\"mod.pin.usage\"));\n const api = await loadUpdateApi();\n if (!api) return c.red(t(\"mod.fetchMissing\"));\n const result = api.setModulePinAuto(ROOT, id, auto);\n if (!result.ok && result.code === \"locked\") return c.yellow(t(\"mod.pin.locked\", { id: result.id || id }));\n if (!result.ok) return c.yellow(t(\"mod.pin.missing\", { id }));\n return c.green(t(\"mod.pin.ok\", { id: result.id, state: t(result.auto ? \"mod.pin.on\" : \"mod.pin.off\") }));\n}\n"],
|
|
5
|
-
"mappings": "AAKA,SAAS,SAAS,gBAAgB;AAClC,SAAS,aAAa;AACtB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,SAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,MAAM,0BAA0B;AACzC,SAAS,SAAS;AAoElB,MAAM,cAA0C;AAAA,EAC9C,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,wBAAwB;AAAA,EACxB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,OAAO;AAAA,EACP,KAAK;AAAA,EACL,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,eAAe;
|
|
4
|
+
"sourcesContent": ["/**\n * \u6A21\u5757\u66F4\u65B0\u7684 CLI \u5165\u53E3\u3002\n * \u8BA1\u5212\u4E0E\u6362\u5305\u5728 scripts/module-install/lib/module-update.mjs\uFF1B\n * \u8FD9\u91CC\u8D1F\u8D23\u786E\u8BA4\u4E0E\u6587\u6848\u3002\u5F00\u670D\u8DEF\u5F84\u5DF2\u5E9F\u5F03\uFF0C\u66F4\u65B0\u53EA\u80FD\u8D70\u663E\u5F0F\u547D\u4EE4\u3002\n */\nimport { confirm, isCancel } from \"@clack/prompts\";\nimport { stdin } from \"node:process\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { isCliTty } from \"../command-surface.js\";\nimport { t, type MessageKey } from \"../i18n/index.js\";\nimport { pushLog } from \"../logs.js\";\nimport { ROOT, resolveFetchModule } from \"../runtime.js\";\nimport { c } from \"../theme.js\";\n\n/** \u4E0E module-update.mjs \u7684\u5347\u7EA7\u5019\u9009\u4E00\u81F4\u3002 */\ninterface ModuleUpgrade {\n id: string;\n fromVersion: string | null;\n toVersion: string | null;\n spec: string;\n requires: string[];\n}\n\n/** \u4E0E module-update.mjs \u7684\u8DF3\u8FC7\u8BB0\u5F55\u4E00\u81F4\u3002 */\ninterface ModuleSkip {\n id: string;\n reason: string;\n fromVersion: string | null;\n toVersion: string | null;\n detail: string;\n}\n\n/** planModuleUpdates \u7684\u8FD4\u56DE\u3002 */\ninterface ModuleUpdatePlan {\n configSkipped: \"disabled\" | \"check-off\" | null;\n applyOnStart: boolean;\n failMode: \"continue\" | \"abort\";\n upgrades: ModuleUpgrade[];\n skipped: ModuleSkip[];\n}\n\n/** applyModuleUpgrades \u7684\u8FD4\u56DE\u3002 */\ninterface ModuleUpdateApplyResult {\n applied: ModuleUpgrade[];\n skipped: ModuleSkip[];\n failed: Array<{ id: string; message: string }>;\n}\n\n/** \u5B89\u88C5\u5668\u5BFC\u51FA\u7684\u4FDD\u7559\u542F\u505C\u5B89\u88C5\u53E3\u3002 */\ninterface FetchModuleApi {\n installPreservingLock: (id: string, spec: string) => Promise<void>;\n}\n\n/** module-update.mjs \u5BFC\u51FA\u9762\u3002\u663E\u5F0F\u66F4\u65B0\u547D\u4EE4\u4F9D\u8D56\u8FD9\u4E00\u5C42\u3002 */\ninterface ModuleUpdateApi {\n ensureModuleUpdateConfigFile: (root?: string) => string;\n planModuleUpdates: (options: {\n root?: string;\n startup?: boolean;\n allowMajor?: boolean;\n ids?: string[];\n }) => Promise<ModuleUpdatePlan>;\n applyModuleUpgrades: (\n upgrades: ModuleUpgrade[],\n deps: {\n root: string;\n failMode: \"continue\" | \"abort\";\n install: (id: string, spec: string) => Promise<void>;\n resync?: (id: string) => void;\n }\n ) => Promise<ModuleUpdateApplyResult>;\n resyncInstalledCatalog: (folder: string) => void;\n setModulePinAuto: (\n root: string,\n id: string,\n auto: boolean\n ) => { ok: true; code: \"ok\"; id: string; auto: boolean } | { ok: false; code: \"missing\" | \"locked\"; id?: string };\n QUIET_SKIP_REASONS: Set<string>;\n}\n\nconst REASON_KEYS: Record<string, MessageKey> = {\n \"auto-off\": \"mod.update.reason.autoOff\",\n \"local-source\": \"mod.update.reason.localSource\",\n \"registry-offline\": \"mod.update.reason.registryOffline\",\n \"no-target\": \"mod.update.reason.noTarget\",\n \"no-installed-version\": \"mod.update.reason.noInstalledVersion\",\n \"up-to-date\": \"mod.update.reason.upToDate\",\n \"installed-newer\": \"mod.update.reason.installedNewer\",\n major: \"mod.update.reason.major\",\n sdk: \"mod.update.reason.sdk\",\n \"dependency-failed\": \"mod.update.reason.dependencyFailed\",\n \"github-manual\": \"mod.update.reason.githubManual\",\n \"missing-module\": \"mod.update.reason.missingModule\",\n \"missing-dependency\": \"mod.update.reason.missingDependency\",\n \"bad-version\": \"mod.update.reason.badVersion\",\n retired: \"mod.update.reason.retired\",\n};\n\n/** \u5B9A\u4F4D fetch-module.mjs\u3002\u627E\u4E0D\u5230\u65F6\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u7ED9\u51FA\u5B89\u88C5\u63D0\u793A\u3002 */\nfunction fetchModuleUrl(): URL | null {\n const script = resolveFetchModule();\n if (!script) return null;\n return pathToFileURL(script);\n}\n\n/** \u4E0E fetch-module \u540C\u76EE\u5F55\u7684\u66F4\u65B0\u5E93\u3002\u4F7F\u7528\u6587\u4EF6\u7CFB\u7EDF\u8DEF\u5F84\u62FC\u63A5\uFF0C\u907F\u514D Windows \u4E0B file URL \u7684 pathname \u88AB dirname \u62C6\u574F\u3002 */\nfunction updateLibUrl(): URL | null {\n const script = resolveFetchModule();\n if (!script) return null;\n return pathToFileURL(path.join(path.dirname(script), \"lib\", \"module-update.mjs\"));\n}\n\nasync function loadUpdateApi(): Promise<ModuleUpdateApi | null> {\n const url = updateLibUrl();\n if (!url) return null;\n return (await import(url.href)) as ModuleUpdateApi;\n}\n\nasync function loadFetchApi(): Promise<FetchModuleApi | null> {\n const url = fetchModuleUrl();\n if (!url) return null;\n return (await import(url.href)) as FetchModuleApi;\n}\n\n/** \u7F3A\u6587\u4EF6\u65F6\u5199\u5165\u9ED8\u8BA4\u7B56\u7565\u3002\u5931\u8D25\u4E0D\u5F71\u54CD\u5176\u4ED6\u547D\u4EE4\u3002 */\nexport async function ensureModuleUpdateConfigFile(): Promise<void> {\n try {\n const api = await loadUpdateApi();\n api?.ensureModuleUpdateConfigFile(ROOT);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n pushLog(message, \"module\", \"warn\");\n }\n}\n\n/**\n * REPL \u5904\u4E8E raw \u6A21\u5F0F\u65F6\uFF0C\u5148\u6682\u65F6\u5173\u6389\uFF0C\u8BA9\u786E\u8BA4\u63D0\u793A\u80FD\u8BFB\u5230\u6574\u884C\u3002\n */\nasync function withCookedStdin<T>(fn: () => Promise<T>): Promise<T> {\n const wasRaw = stdin.isRaw === true;\n if (wasRaw) stdin.setRawMode(false);\n try {\n return await fn();\n } finally {\n if (wasRaw) stdin.setRawMode(true);\n }\n}\n\n/** \u8DF3\u8FC7\u539F\u56E0\u7684\u53EF\u8BFB\u6587\u6848\u3002\u672A\u77E5\u539F\u56E0\u4FDD\u6301\u539F\u6837\u3002 */\nfunction reasonText(reason: string, detail: string): string {\n const key = REASON_KEYS[reason];\n const label = key ? t(key) : reason;\n return detail ? `${label} (${detail})` : label;\n}\n\nfunction versionText(value: string | null): string {\n return value || \"?\";\n}\n\n/**\n * \u628A\u8BA1\u5212\u683C\u5F0F\u5316\u6210\u591A\u884C\u3002\n * \u6CA1\u70B9\u540D\u6A21\u5757\u3001\u4E5F\u6CA1 --check \u65F6\uFF0C\u9690\u85CF auto-off / local-source / up-to-date\u3002\n * \u70B9\u540D\u4E86 id \u6216 --check \u65F6\u5C55\u5F00\u8FD9\u4E9B\u539F\u56E0\u548C\u7248\u672C\u3002quietEmpty \u4E3A true \u65F6\u4E0D\u8F93\u51FA\u7A7A\u7ED3\u679C\u3002\n */\nfunction formatPlan(\n plan: ModuleUpdatePlan,\n quiet: Set<string>,\n showAll: boolean,\n quietEmpty = false\n): string[] {\n if (plan.configSkipped === \"disabled\") return quietEmpty ? [] : [c.yellow(t(\"mod.update.disabled\"))];\n if (plan.configSkipped === \"check-off\") return quietEmpty ? [] : [c.dim(t(\"mod.update.checkOff\"))];\n const lines: string[] = [];\n for (const item of plan.skipped) {\n if (!showAll && quiet.has(item.reason)) continue;\n /** \u8DF3\u8FC7\u884C\u4E0A\u7684\u7248\u672C\u5BF9\u7167\u3002\u6709\u7D22\u5F15\u76EE\u6807\u65F6\u8BA9\u670D\u4E3B\u770B\u5230 1.0.0 -> 1.2.0\uFF0C\u800C\u4E0D\u662F\u53EA\u770B\u5230\u539F\u56E0\u3002 */\n const versions =\n item.fromVersion || item.toVersion ? ` ${versionText(item.fromVersion)} -> ${versionText(item.toVersion)}` : \"\";\n lines.push(c.dim(` ${item.id}${versions} ${reasonText(item.reason, item.detail)}`));\n }\n for (const item of plan.upgrades) {\n lines.push(\n c.green(\n ` ${t(\"mod.update.upgrade\", {\n id: item.id,\n from: versionText(item.fromVersion),\n to: versionText(item.toVersion),\n })}`\n )\n );\n }\n if (plan.upgrades.length === 0 && lines.length === 0 && !quietEmpty) lines.push(c.dim(t(\"mod.update.none\")));\n return lines;\n}\n\nfunction formatApply(result: ModuleUpdateApplyResult, hintReload: boolean): string[] {\n const lines: string[] = [];\n for (const item of result.applied) {\n lines.push(\n c.green(\n t(\"mod.update.applied\", {\n id: item.id,\n from: versionText(item.fromVersion),\n to: versionText(item.toVersion),\n })\n )\n );\n }\n for (const item of result.skipped) {\n lines.push(c.yellow(` ${item.id} ${reasonText(item.reason, item.detail)}`));\n }\n for (const item of result.failed) {\n lines.push(c.red(t(\"mod.update.failed\", { id: item.id, message: item.message })));\n }\n if (hintReload && result.applied.length > 0) lines.push(c.yellow(t(\"mod.update.reloadHint\")));\n return lines;\n}\n\ninterface UpdateFlags {\n check: boolean;\n yes: boolean;\n allowMajor: boolean;\n ids: string[];\n}\n\n/** \u89E3\u6790 mod update \u7684\u65D7\u6807\u3002\u672A\u77E5\u65D7\u6807\u8FD4\u56DE null\u3002 */\nfunction parseUpdateFlags(args: string[]): UpdateFlags | null {\n const flags: UpdateFlags = { check: false, yes: false, allowMajor: false, ids: [] };\n for (const arg of args) {\n if (arg === \"--check\") flags.check = true;\n else if (arg === \"--yes\") flags.yes = true;\n else if (arg === \"--allow-major\") flags.allowMajor = true;\n else if (arg.startsWith(\"--\")) return null;\n else flags.ids.push(arg);\n }\n return flags;\n}\n\n/**\n * mod update\uFF1A\u5148\u51FA\u8BA1\u5212\uFF0C\u4EA4\u4E92\u786E\u8BA4\u540E\u518D\u6362\u5305\u3002\n * --check \u53EA\u62A5\u544A\u3002\u6A21\u5757\u66F4\u65B0\u53EA\u5728\u6267\u884C\u672C\u547D\u4EE4\u65F6\u8FDB\u884C\u3002\n */\nexport async function cmdModuleUpdate(args: string[]): Promise<string> {\n const flags = parseUpdateFlags(args);\n if (!flags) return c.yellow(t(\"mod.update.usage\"));\n const api = await loadUpdateApi();\n if (!api) return c.red(t(\"mod.fetchMissing\"));\n const plan = await api.planModuleUpdates({\n root: ROOT,\n allowMajor: flags.allowMajor,\n ids: flags.ids,\n });\n /** \u70B9\u540D\u4E86\u6A21\u5757\u6216\u663E\u5F0F --check \u65F6\u5C55\u5F00\u5B89\u9759\u8DF3\u8FC7\uFF1B\u6CA1\u70B9\u540D\u65F6\u4ECD\u53EF\u6536\u6210\u300C\u6CA1\u6709\u53EF\u66F4\u65B0\u7684\u6A21\u5757\u300D\u3002 */\n const revealQuietSkips = flags.check || flags.ids.length > 0;\n const lines = formatPlan(plan, api.QUIET_SKIP_REASONS, revealQuietSkips);\n if (flags.check || plan.configSkipped || plan.upgrades.length === 0) return lines.join(\"\\n\");\n if (!flags.yes) {\n if (!isCliTty()) return [...lines, c.yellow(t(\"mod.update.needYes\"))].join(\"\\n\");\n const ans = await withCookedStdin(() =>\n confirm({\n message: t(\"mod.update.confirm\", { count: plan.upgrades.length }),\n initialValue: true,\n })\n );\n if (isCancel(ans) || !ans) return [...lines, c.dim(t(\"mod.update.cancelled\"))].join(\"\\n\");\n }\n const fetchApi = await loadFetchApi();\n if (!fetchApi) return c.red(t(\"mod.fetchMissing\"));\n const result = await api.applyModuleUpgrades(plan.upgrades, {\n root: ROOT,\n failMode: plan.failMode,\n install: fetchApi.installPreservingLock,\n resync: api.resyncInstalledCatalog,\n });\n return [...lines, ...formatApply(result, true)].join(\"\\n\");\n}\n\n/** \u5F00\u670D\u8DEF\u5F84\u5DF2\u5E9F\u5F03\uFF0C\u66F4\u65B0\u53EA\u80FD\u8D70\u663E\u5F0F\u547D\u4EE4\u3002 */\nexport async function runModuleUpdatesOnBdsStart(): Promise<void> {\n return;\n}\n\n/**\n * mod pin <id> --auto on|off\n * \u94FE\u63A5\u548C\u672C\u5730\u76EE\u5F55\u4E0D\u80FD\u6253\u5F00\u81EA\u52A8\u66F4\u65B0\u3002\n */\nexport async function cmdModulePin(args: string[]): Promise<string> {\n let id = \"\";\n let auto: boolean | null = null;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--auto\") {\n const value = args[++i];\n if (value === \"on\") auto = true;\n else if (value === \"off\") auto = false;\n else return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg?.startsWith(\"--auto=\")) {\n const value = arg.slice(\"--auto=\".length);\n if (value === \"on\") auto = true;\n else if (value === \"off\") auto = false;\n else return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg?.startsWith(\"--\")) {\n return c.yellow(t(\"mod.pin.usage\"));\n } else if (arg) {\n id = arg;\n }\n }\n if (!id || auto === null) return c.yellow(t(\"mod.pin.usage\"));\n const api = await loadUpdateApi();\n if (!api) return c.red(t(\"mod.fetchMissing\"));\n const result = api.setModulePinAuto(ROOT, id, auto);\n if (!result.ok && result.code === \"locked\") return c.yellow(t(\"mod.pin.locked\", { id: result.id || id }));\n if (!result.ok) return c.yellow(t(\"mod.pin.missing\", { id }));\n return c.green(t(\"mod.pin.ok\", { id: result.id, state: t(result.auto ? \"mod.pin.on\" : \"mod.pin.off\") }));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfmc-bds/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "SFMC management CLI — REPL, service supervisor, module install, behavior-pack build/deploy",
|
|
5
5
|
"author": "Shiroha7z",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -55,13 +55,13 @@
|
|
|
55
55
|
"esbuild": "^0.28.1",
|
|
56
56
|
"ajv": "8.20.0",
|
|
57
57
|
"jsonc-parser": "3.3.1",
|
|
58
|
-
"@sfmc-bds/bds-tools": "0.2.
|
|
59
|
-
"@sfmc-bds/devkit": "2.0.
|
|
60
|
-
"@sfmc-bds/sdk": "0.3.
|
|
61
|
-
"@sfmc-bds/management": "0.3.
|
|
58
|
+
"@sfmc-bds/bds-tools": "0.2.7",
|
|
59
|
+
"@sfmc-bds/devkit": "2.0.3",
|
|
60
|
+
"@sfmc-bds/sdk": "0.3.2",
|
|
61
|
+
"@sfmc-bds/management": "0.3.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@sfmc-bds/tools": "0.2.
|
|
64
|
+
"@sfmc-bds/tools": "0.2.7"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public",
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* 安装目录若是 symlink / junction,只记 local-source 跳过,不换包。
|
|
5
5
|
* 下载与落盘由调用方注入的 install,本文件不复制第二套安装逻辑。
|
|
6
6
|
*/
|
|
7
|
+
import { isRetiredPlatformModule, RETIRED_PLATFORM_MODULES } from "@sfmc-bds/sdk/contracts";
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
7
9
|
import fs from "node:fs";
|
|
8
10
|
import fsp from "node:fs/promises";
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
import { randomBytes } from "node:crypto";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
|
+
import path from "node:path";
|
|
12
13
|
import { upsertCatalogEntry } from "./catalog.mjs";
|
|
13
14
|
import { readJson } from "./io.mjs";
|
|
14
15
|
import { loadModuleUpdateConfig } from "./module-update-config.mjs";
|
|
@@ -54,6 +55,15 @@ import { compareSemver, isMajorBump, satisfiesSdk } from "./semver.mjs";
|
|
|
54
55
|
/** 这些跳过原因在开服日志里不展开,避免每轮刷屏。 */
|
|
55
56
|
export const QUIET_SKIP_REASONS = new Set(["auto-off", "local-source", "up-to-date"]);
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* 目录名或 manifest.id 是否已收编至平台。
|
|
60
|
+
* @param {string | null | undefined} id
|
|
61
|
+
*/
|
|
62
|
+
function isRetiredModuleId(id) {
|
|
63
|
+
const raw = String(id ?? "");
|
|
64
|
+
return isRetiredPlatformModule(raw) || isRetiredPlatformModule(normalizeModuleId(raw));
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
/**
|
|
58
68
|
* 读取本机 @sfmc-bds/sdk 版本,用来对照索引里的 sdk 范围。
|
|
59
69
|
* @param {string} [cliPkgDir]
|
|
@@ -368,6 +378,7 @@ export async function planModuleUpdates(options = {}) {
|
|
|
368
378
|
installedNorm.add(normalizeModuleId(folder));
|
|
369
379
|
if (pin.manifestId) installedNorm.add(normalizeModuleId(pin.manifestId));
|
|
370
380
|
}
|
|
381
|
+
for (const id of RETIRED_PLATFORM_MODULES) installedNorm.add(normalizeModuleId(id));
|
|
371
382
|
|
|
372
383
|
/** @type {ModuleUpgrade[]} */
|
|
373
384
|
const upgrades = [];
|
|
@@ -390,6 +401,10 @@ export async function planModuleUpdates(options = {}) {
|
|
|
390
401
|
if (wanted.size > 0 && !keys.some((key) => wanted.has(key))) continue;
|
|
391
402
|
const dir = packageDirOf(root, folder);
|
|
392
403
|
const installedVersion = readPackageVersion(dir) ?? pin.installedVersion;
|
|
404
|
+
if ([folder, manifestId].some((id) => isRetiredModuleId(id))) {
|
|
405
|
+
skipped.push({ id: folder, reason: "retired", fromVersion: installedVersion, toVersion: null, detail: "" });
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
393
408
|
const indexEntry = index ? index[folder] || index[normalizeModuleId(manifestId || "")] || null : null;
|
|
394
409
|
/* 目录本身是开发链接时先跳过,避免 pin 写成 npm 后仍去查 dist-tag 并换包。 */
|
|
395
410
|
const linkSkip = skipWhenDevLink(
|
|
@@ -538,6 +553,10 @@ export async function applyModuleUpgrades(upgrades, deps) {
|
|
|
538
553
|
/** @type {{ id: string, message: string }[]} */
|
|
539
554
|
const failed = [];
|
|
540
555
|
for (const item of upgrades) {
|
|
556
|
+
if (isRetiredModuleId(item.id)) {
|
|
557
|
+
skipped.push({ id: item.id, reason: "retired", fromVersion: item.fromVersion, toVersion: item.toVersion, detail: "" });
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
541
560
|
const linkSkip = skipWhenDevLink(item.id, packageDirOf(deps.root, item.id), item.fromVersion, item.toVersion);
|
|
542
561
|
if (linkSkip) {
|
|
543
562
|
skipped.push(linkSkip);
|