@kb-labs/release-manager-cli 2.112.0 → 2.116.11
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/README.md +1 -1
- package/dist/cli/commands/deliver.js +3 -2
- package/dist/cli/commands/deliver.js.map +1 -1
- package/dist/cli/commands/promote.js +3 -2
- package/dist/cli/commands/promote.js.map +1 -1
- package/dist/cli/commands/publish.js +3 -2
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/run.js +17 -9
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/stage.js +75 -29
- package/dist/cli/commands/stage.js.map +1 -1
- package/dist/cli/commands/verify-clean-install.d.ts +12 -0
- package/dist/cli/commands/verify-clean-install.js +42 -0
- package/dist/cli/commands/verify-clean-install.js.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest.js +18 -0
- package/dist/manifest.js.map +1 -1
- package/dist/manifest.json +28 -0
- package/dist/rest/handlers/run-handler.js +3 -2
- package/dist/rest/handlers/run-handler.js.map +1 -1
- package/dist/widgets/117.js +2 -2
- package/dist/widgets/370.js +1 -1
- package/dist/widgets/mf-manifest.json +12 -12
- package/dist/widgets/mf-stats.json +16 -16
- package/dist/widgets/remoteEntry.js +2 -2
- package/package.json +26 -26
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { spawnSync } from 'child_process';
|
|
3
|
-
import { mkdirSync, readFileSync,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { mkdirSync, writeFileSync, readFileSync, mkdtempSync, rmSync } from 'fs';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import { join, basename } from 'path';
|
|
6
|
+
import { defineCommand, findRepoRoot, useConfig, useLoader, useEnv } from '@kb-labs/sdk';
|
|
7
|
+
import { mergeConfigWithFlow, discoverCurrentPackages, resolveFlowFromTag, verifyExtractedTarball } from '@kb-labs/release-manager-core';
|
|
7
8
|
|
|
8
9
|
// src/cli/commands/stage.ts
|
|
9
10
|
function rewriteLinkDep(deps, depName, val, pkgPath, versionMap) {
|
|
@@ -25,7 +26,8 @@ function rewriteWorkspaceDep(deps, depName, val, versionMap) {
|
|
|
25
26
|
if (!pinned) {
|
|
26
27
|
return false;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
const range = val === "workspace:*" ? "^" : val.slice("workspace:".length);
|
|
30
|
+
deps[depName] = `${range}${pinned}`;
|
|
29
31
|
return true;
|
|
30
32
|
}
|
|
31
33
|
function rewriteDepsSection(deps, pkgPath, versionMap, packageManager) {
|
|
@@ -54,7 +56,7 @@ function rewriteWorkspaceDeps(pkg, versionMap, packageManager) {
|
|
|
54
56
|
pkgJson.version = pkg.version;
|
|
55
57
|
modified = true;
|
|
56
58
|
}
|
|
57
|
-
for (const section of ["dependencies", "peerDependencies"]) {
|
|
59
|
+
for (const section of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"]) {
|
|
58
60
|
const deps = pkgJson[section];
|
|
59
61
|
if (!deps) {
|
|
60
62
|
continue;
|
|
@@ -83,6 +85,55 @@ function resolveFlowName(config, flags) {
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
// src/cli/commands/stage.ts
|
|
88
|
+
function filenameFromPnpmPack(stdout) {
|
|
89
|
+
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
90
|
+
const lastLine = lines[lines.length - 1];
|
|
91
|
+
return lastLine ? basename(lastLine.trim()) : void 0;
|
|
92
|
+
}
|
|
93
|
+
function filenameFromNpmPack(stdout) {
|
|
94
|
+
const parsed = JSON.parse(stdout);
|
|
95
|
+
return parsed[0]?.filename;
|
|
96
|
+
}
|
|
97
|
+
function packOne(pkg, outDir, packageManager, versionMap) {
|
|
98
|
+
const isPnpm = packageManager === "pnpm";
|
|
99
|
+
const restore = rewriteWorkspaceDeps(
|
|
100
|
+
{ path: pkg.path, version: pkg.currentVersion },
|
|
101
|
+
versionMap,
|
|
102
|
+
packageManager
|
|
103
|
+
);
|
|
104
|
+
const packArgs = isPnpm ? ["pack", "--pack-destination", outDir] : ["pack", "--pack-destination", outDir, "--json"];
|
|
105
|
+
const packResult = spawnSync(isPnpm ? "pnpm" : "npm", packArgs, { cwd: pkg.path, stdio: "pipe" });
|
|
106
|
+
if (packResult.status !== 0) {
|
|
107
|
+
const stderr = packResult.stderr?.toString().trim() || packResult.stdout?.toString().trim();
|
|
108
|
+
throw new Error(`${packageManager} pack failed for ${pkg.path}${stderr ? `: ${stderr}` : ""}`);
|
|
109
|
+
}
|
|
110
|
+
const filename = isPnpm ? filenameFromPnpmPack(packResult.stdout.toString()) : filenameFromNpmPack(packResult.stdout.toString());
|
|
111
|
+
if (!filename) {
|
|
112
|
+
throw new Error(`${packageManager} pack produced no tarball for ${pkg.path}`);
|
|
113
|
+
}
|
|
114
|
+
return { filename, restore };
|
|
115
|
+
}
|
|
116
|
+
async function packStagePackage(pkg, outDir, packageManager, versionMap) {
|
|
117
|
+
const { filename, restore } = packOne(pkg, outDir, packageManager, versionMap);
|
|
118
|
+
try {
|
|
119
|
+
const sha256 = createHash("sha256").update(readFileSync(join(outDir, filename))).digest("hex");
|
|
120
|
+
return { name: pkg.name, version: pkg.currentVersion, tarball: filename, sha256 };
|
|
121
|
+
} finally {
|
|
122
|
+
restore();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function verifyStagePackage(pkg, outDir, artifact) {
|
|
126
|
+
const tarballPath = join(outDir, artifact.tarball);
|
|
127
|
+
const verifyDir = mkdtempSync(join(tmpdir(), "kb-stage-verify-"));
|
|
128
|
+
try {
|
|
129
|
+
const extractResult = spawnSync("tar", ["xzf", tarballPath, "-C", verifyDir], { stdio: "pipe" });
|
|
130
|
+
if (extractResult.status !== 0) throw new Error(`could not extract staged tarball for ${pkg.name}@${pkg.currentVersion}`);
|
|
131
|
+
const issues = verifyExtractedTarball(join(verifyDir, "package"));
|
|
132
|
+
if (issues.length > 0) throw new Error(`staged artifact verification failed for ${pkg.name}@${pkg.currentVersion}: ${issues.join("; ")}`);
|
|
133
|
+
} finally {
|
|
134
|
+
rmSync(verifyDir, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
86
137
|
var stage_default = defineCommand({
|
|
87
138
|
id: "release:stage",
|
|
88
139
|
description: "Pack the currently-committed package versions for a flow into real npm tarballs, once, for `release deliver` to ship",
|
|
@@ -120,33 +171,28 @@ var stage_default = defineCommand({
|
|
|
120
171
|
}
|
|
121
172
|
return { ok: false, error: "Command failed" };
|
|
122
173
|
}
|
|
123
|
-
const
|
|
124
|
-
const
|
|
174
|
+
const allWorkspacePackages = await discoverCurrentPackages(repoRoot, void 0, baseConfig);
|
|
175
|
+
const versionMap = new Map(allWorkspacePackages.map((pkg) => [pkg.name, pkg.currentVersion]));
|
|
176
|
+
const packageManager = config.workspace?.type ?? config.publish?.packageManager ?? "pnpm";
|
|
177
|
+
const CONCURRENCY = Number(useEnv("KB_STAGE_CONCURRENCY") ?? 6);
|
|
125
178
|
const packLoader = useLoader("Packing tarballs...");
|
|
126
179
|
packLoader.start();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (packResult.status !== 0) {
|
|
135
|
-
const stderr = packResult.stderr?.toString().trim() || packResult.stdout?.toString().trim();
|
|
136
|
-
throw new Error(`npm pack failed for ${pkg.name}@${pkg.currentVersion}${stderr ? `: ${stderr}` : ""}`);
|
|
137
|
-
}
|
|
138
|
-
const parsed = JSON.parse(packResult.stdout.toString());
|
|
139
|
-
const filename = parsed[0]?.filename;
|
|
140
|
-
if (!filename) {
|
|
141
|
-
throw new Error(`npm pack produced no tarball for ${pkg.name}@${pkg.currentVersion}`);
|
|
142
|
-
}
|
|
143
|
-
const sha256 = createHash("sha256").update(readFileSync(join(outDir, filename))).digest("hex");
|
|
144
|
-
artifacts.push({ name: pkg.name, version: pkg.currentVersion, tarball: filename, sha256 });
|
|
145
|
-
} finally {
|
|
146
|
-
restore();
|
|
147
|
-
}
|
|
180
|
+
const artifacts = [];
|
|
181
|
+
for (let i = 0; i < discovered.length; i += CONCURRENCY) {
|
|
182
|
+
const batch = discovered.slice(i, i + CONCURRENCY);
|
|
183
|
+
const batchArtifacts = await Promise.all(
|
|
184
|
+
batch.map((pkg) => packStagePackage(pkg, outDir, packageManager, versionMap))
|
|
185
|
+
);
|
|
186
|
+
artifacts.push(...batchArtifacts);
|
|
148
187
|
}
|
|
149
188
|
packLoader.succeed(`Packed ${artifacts.length} tarball(s) to ${outDir}`);
|
|
189
|
+
const verifyLoader = useLoader("Verifying staged tarballs...");
|
|
190
|
+
verifyLoader.start();
|
|
191
|
+
for (let i = 0; i < discovered.length; i += CONCURRENCY) {
|
|
192
|
+
const batch = discovered.slice(i, i + CONCURRENCY);
|
|
193
|
+
batch.forEach((pkg, index) => verifyStagePackage(pkg, outDir, artifacts[i + index]));
|
|
194
|
+
}
|
|
195
|
+
verifyLoader.succeed(`Verified ${artifacts.length} staged tarball(s)`);
|
|
150
196
|
writeFileSync(join(outDir, "manifest.json"), JSON.stringify(artifacts, null, 2) + "\n", "utf-8");
|
|
151
197
|
const result = { outDir, artifacts };
|
|
152
198
|
if (flags.json) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/shared/dep-rewrite.ts","../../../src/shared/resolve-flow.ts","../../../src/cli/commands/stage.ts"],"names":["join","readFileSync","writeFileSync"],"mappings":"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","file":"stage.js","sourcesContent":["/**\n * Rewrite a package's own version and workspace:/link: dependency references\n * before `pnpm publish`, so the tarball on disk matches what's about to be\n * published even when that version was never persisted to package.json.\n *\n * - Own version: stable releases already have the target version on disk\n * (written by updatePackageVersions() before this runs), so this is a\n * no-op for them. Canary releases compute nextVersion in-memory only —\n * never committed to package.json/git — so without this, `pnpm publish`\n * would read the OLD on-disk version, and either fail outright or (worse)\n * get misclassified as \"already published\" by the retry/idempotency\n * patterns since that old version genuinely IS already on the registry.\n * - pnpm handles `workspace:*` natively during `pnpm publish` — no rewrite needed.\n * - npm/yarn do NOT — we must replace with `^version` before publish.\n * - `link:` references are never valid on the npm registry — always rewrite.\n *\n * Returns a restore function that reverts package.json to original content.\n * Call it in a finally block to guarantee cleanup.\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// ── rewriteWorkspaceDeps helpers ──────────────────────────────────────────\n\nfunction rewriteLinkDep(\n deps: Record<string, string>,\n depName: string,\n val: string,\n pkgPath: string,\n versionMap: Map<string, string>,\n): void {\n const pinned = versionMap.get(depName);\n if (pinned) {\n deps[depName] = `^${pinned}`;\n return;\n }\n try {\n const linkPath = val.slice('link:'.length);\n const linked = JSON.parse(readFileSync(join(pkgPath, linkPath, 'package.json'), 'utf-8')) as { version?: string };\n deps[depName] = `^${linked.version ?? '*'}`;\n } catch {\n deps[depName] = '*';\n }\n}\n\nfunction rewriteWorkspaceDep(\n deps: Record<string, string>,\n depName: string,\n val: string,\n versionMap: Map<string, string>,\n): boolean {\n const pinned = versionMap.get(depName);\n if (!pinned) { return false; }\n deps[depName] = val === 'workspace:*' ? `^${pinned}` : val.replace('workspace:', '');\n return true;\n}\n\nfunction rewriteDepsSection(\n deps: Record<string, string>,\n pkgPath: string,\n versionMap: Map<string, string>,\n packageManager: string,\n): boolean {\n let modified = false;\n for (const depName of Object.keys(deps)) {\n const val = deps[depName];\n if (typeof val !== 'string') { continue; }\n if (val.startsWith('link:')) {\n rewriteLinkDep(deps, depName, val, pkgPath, versionMap);\n modified = true;\n } else if (val.startsWith('workspace:') && packageManager !== 'pnpm' && rewriteWorkspaceDep(deps, depName, val, versionMap)) {\n modified = true;\n }\n }\n return modified;\n}\n\nexport function rewriteWorkspaceDeps(\n pkg: { path: string; version: string },\n versionMap: Map<string, string>,\n packageManager: string,\n): () => void {\n const pkgPath = pkg.path;\n const pkgJsonPath = join(pkgPath, 'package.json');\n const original = readFileSync(pkgJsonPath, 'utf-8');\n const pkgJson = JSON.parse(original) as Record<string, unknown> & { version?: string };\n let modified = false;\n\n if (pkgJson.version !== pkg.version) {\n pkgJson.version = pkg.version;\n modified = true;\n }\n\n for (const section of ['dependencies', 'peerDependencies'] as const) {\n const deps = pkgJson[section] as Record<string, string> | undefined;\n if (!deps) { continue; }\n if (rewriteDepsSection(deps, pkgPath, versionMap, packageManager)) { modified = true; }\n }\n\n if (modified) {\n writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\\n');\n }\n\n return () => writeFileSync(pkgJsonPath, original);\n}\n\n// ── topoSort helpers ──────────────────────────────────────────────────────\n\n/**\n * Build a simple topological sort of packages for publish order.\n *\n * Packages are grouped into \"waves\": wave 0 has no intra-release deps,\n * wave 1 depends only on wave 0, etc. Within each wave publish order\n * is arbitrary (safe to parallelise).\n *\n * Cycles (should not exist in a well-formed monorepo) fall into a\n * \"remainder\" group appended at the end.\n */\nexport interface TopoPackage {\n name: string;\n path: string;\n version: string;\n}\n\nfunction buildIntraDeps(packages: TopoPackage[], nameSet: Set<string>): Map<string, Set<string>> {\n const deps = new Map<string, Set<string>>();\n for (const pkg of packages) {\n const intra = new Set<string>();\n try {\n const pkgJson = JSON.parse(readFileSync(join(pkg.path, 'package.json'), 'utf-8')) as {\n dependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n };\n for (const section of ['dependencies', 'peerDependencies'] as const) {\n for (const depName of Object.keys(pkgJson[section] ?? {})) {\n if (nameSet.has(depName)) { intra.add(depName); }\n }\n }\n } catch {\n // If we can't read package.json, treat as no deps\n }\n deps.set(pkg.name, intra);\n }\n return deps;\n}\n\nfunction buildDependantsMap(packages: TopoPackage[], deps: Map<string, Set<string>>): Map<string, Set<string>> {\n const dependants = new Map<string, Set<string>>();\n for (const pkg of packages) {\n for (const dep of deps.get(pkg.name) ?? []) {\n if (!dependants.has(dep)) { dependants.set(dep, new Set()); }\n dependants.get(dep)!.add(pkg.name);\n }\n }\n return dependants;\n}\n\nfunction computeWaves(\n packages: TopoPackage[],\n deps: Map<string, Set<string>>,\n dependants: Map<string, Set<string>>,\n): TopoPackage[][] {\n const byName = new Map(packages.map(p => [p.name, p]));\n const remaining = new Map<string, number>(\n packages.map(p => [p.name, (deps.get(p.name) ?? new Set()).size])\n );\n const waves: TopoPackage[][] = [];\n\n while (remaining.size > 0) {\n const wave = [...remaining.entries()]\n .filter(([, count]) => count === 0)\n .map(([name]) => byName.get(name)!);\n\n if (wave.length === 0) {\n waves.push([...remaining.keys()].map(n => byName.get(n)!));\n break;\n }\n\n waves.push(wave);\n for (const pkg of wave) {\n remaining.delete(pkg.name);\n for (const dependant of dependants.get(pkg.name) ?? []) {\n if (remaining.has(dependant)) {\n remaining.set(dependant, (remaining.get(dependant) ?? 1) - 1);\n }\n }\n }\n }\n\n return waves.filter(w => w.length > 0);\n}\n\nexport function topoSort(packages: TopoPackage[], _packageManager: string): TopoPackage[][] {\n const nameSet = new Set(packages.map(p => p.name));\n const deps = buildIntraDeps(packages, nameSet);\n const dependants = buildDependantsMap(packages, deps);\n return computeWaves(packages, deps, dependants);\n}\n","/**\n * Shared `--release-tag` / `--flow` resolution for commands that operate\n * on an already-decided release (`stage`, `deliver`) — mirrors the tag\n * grammar in `@kb-labs/release-manager-core`'s `resolveFlowFromTag` so CI\n * can pass just the tag and the plugin decides the flow, per the\n * \"plugin prepares, CI delivers\" release plan.\n */\n\nimport { resolveFlowFromTag, type ReleaseConfig } from '@kb-labs/release-manager-core';\n\nexport interface FlowResolvableFlags {\n flow?: string;\n 'release-tag'?: string;\n}\n\nexport function resolveFlowName(config: ReleaseConfig, flags: FlowResolvableFlags): string | { error: string } {\n if (flags.flow) { return flags.flow; }\n if (flags['release-tag']) {\n const resolved = resolveFlowFromTag(config, flags['release-tag']);\n if (!resolved) {\n return { error: `Could not resolve a release.flows entry from tag \"${flags['release-tag']}\"` };\n }\n return resolved.flowName;\n }\n return { error: 'requires --flow or --release-tag' };\n}\n","/**\n * Standalone stage command — packs the flow's currently-committed package\n * versions into real npm tarballs, once, so every downstream `release\n * deliver` job ships the exact same bytes instead of re-packing\n * independently from source. See plugins/release/docs/adr/0001-* and the\n * \"plugin prepares, CI delivers\" release plan.\n *\n * Deliberately does NOT call planRelease() — same reasoning as promote.ts:\n * versions are already fixed by the prepare step that already ran (version\n * bump, changelog, git commit/tag/push). This command's only job is\n * \"pack exactly what's already on disk, unchanged.\"\n *\n * Not to be confused with `release:pack` (`pack.ts`) — that command\n * verifies *proposed* packages via `npm pack` + static checks before a\n * release is decided; this one produces the *actual* tarball artifact for\n * an already-decided release.\n */\n\nimport { createHash } from 'node:crypto';\nimport { spawnSync } from 'node:child_process';\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { defineCommand, type CLIInput, type PluginContextV3, useLoader, useConfig, type CommandResult } from '@kb-labs/sdk';\nimport {\n discoverCurrentPackages,\n mergeConfigWithFlow,\n type ReleaseConfig,\n} from '@kb-labs/release-manager-core';\nimport { findRepoRoot } from '../../shared/utils';\nimport { rewriteWorkspaceDeps } from '../../shared/dep-rewrite';\nimport { resolveFlowName, type FlowResolvableFlags } from '../../shared/resolve-flow';\n\ninterface StageFlags extends FlowResolvableFlags {\n 'out-dir'?: string;\n json?: boolean;\n}\n\nexport interface StagedArtifact {\n name: string;\n version: string;\n tarball: string;\n sha256: string;\n}\n\ninterface StagePayload {\n outDir?: string;\n artifacts?: StagedArtifact[];\n}\n\nexport default defineCommand({\n id: 'release:stage',\n description: 'Pack the currently-committed package versions for a flow into real npm tarballs, once, for `release deliver` to ship',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<StageFlags>): Promise<CommandResult<StagePayload>> {\n const { flags } = input;\n const cwd = ctx.cwd || process.cwd();\n const repoRoot = await findRepoRoot(cwd);\n\n const fileConfig = await useConfig<ReleaseConfig>();\n const baseConfig: ReleaseConfig = fileConfig ?? {};\n\n const flowResult = resolveFlowName(baseConfig, flags);\n if (typeof flowResult !== 'string') {\n const msg = `release:stage ${flowResult.error}`;\n if (flags.json) { ctx.ui?.json?.({ error: msg }); } else { ctx.ui?.error?.(msg); }\n return { ok: false, error: 'Command failed' };\n }\n const flowName = flowResult;\n\n const config: ReleaseConfig = mergeConfigWithFlow(baseConfig, flowName);\n const outDir = join(repoRoot, flags['out-dir'] ?? '.kb/release/artifacts');\n mkdirSync(outDir, { recursive: true });\n\n const discoverLoader = useLoader(`Discovering currently-committed packages for flow \"${flowName}\"...`);\n discoverLoader.start();\n const discovered = await discoverCurrentPackages(repoRoot, undefined, config);\n discoverLoader.succeed(`Found ${discovered.length} package(s) to stage`);\n\n if (discovered.length === 0) {\n const msg = `No packages found for flow \"${flowName}\"`;\n if (flags.json) { ctx.ui?.json?.({ outDir, artifacts: [] }); } else { ctx.ui?.write?.(msg); }\n return { ok: false, error: 'Command failed' };\n }\n\n const versionMap = new Map(discovered.map(pkg => [pkg.name, pkg.currentVersion]));\n const artifacts: StagedArtifact[] = [];\n\n const packLoader = useLoader('Packing tarballs...');\n packLoader.start();\n for (const pkg of discovered) {\n const restore = rewriteWorkspaceDeps({ path: pkg.path, version: pkg.currentVersion }, versionMap, 'npm');\n try {\n const packResult = spawnSync('npm', ['pack', '--pack-destination', outDir, '--json'], {\n cwd: pkg.path,\n stdio: 'pipe',\n });\n if (packResult.status !== 0) {\n const stderr = packResult.stderr?.toString().trim() || packResult.stdout?.toString().trim();\n throw new Error(`npm pack failed for ${pkg.name}@${pkg.currentVersion}${stderr ? `: ${stderr}` : ''}`);\n }\n const parsed = JSON.parse(packResult.stdout.toString()) as Array<{ filename: string }>;\n const filename = parsed[0]?.filename;\n if (!filename) {\n throw new Error(`npm pack produced no tarball for ${pkg.name}@${pkg.currentVersion}`);\n }\n const sha256 = createHash('sha256').update(readFileSync(join(outDir, filename))).digest('hex');\n artifacts.push({ name: pkg.name, version: pkg.currentVersion, tarball: filename, sha256 });\n } finally {\n restore();\n }\n }\n packLoader.succeed(`Packed ${artifacts.length} tarball(s) to ${outDir}`);\n\n writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(artifacts, null, 2) + '\\n', 'utf-8');\n\n const result: StagePayload = { outDir, artifacts };\n if (flags.json) {\n const response = { ok: true as const, result };\n ctx.ui?.json?.(response);\n return response;\n }\n\n ctx.ui?.sideBox?.({\n title: 'Stage',\n sections: [{\n header: `Staged to ${outDir}`,\n items: artifacts.map(a => `${ctx.ui.symbols.success} ${a.name}@${a.version} → ${a.tarball}`),\n }],\n status: 'success',\n });\n\n return { ok: true, result };\n },\n },\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/shared/dep-rewrite.ts","../../../src/shared/resolve-flow.ts","../../../src/cli/commands/stage.ts"],"names":["readFileSync","join","writeFileSync"],"mappings":"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","file":"stage.js","sourcesContent":["/**\n * Rewrite a package's own version and workspace:/link: dependency references\n * before `pnpm publish`, so the tarball on disk matches what's about to be\n * published even when that version was never persisted to package.json.\n *\n * - Own version: stable releases already have the target version on disk\n * (written by updatePackageVersions() before this runs), so this is a\n * no-op for them. Canary releases compute nextVersion in-memory only —\n * never committed to package.json/git — so without this, `pnpm publish`\n * would read the OLD on-disk version, and either fail outright or (worse)\n * get misclassified as \"already published\" by the retry/idempotency\n * patterns since that old version genuinely IS already on the registry.\n * - pnpm handles `workspace:*` natively during `pnpm publish` — no rewrite needed.\n * - npm/yarn do NOT — we must replace with `^version` before publish.\n * - `link:` references are never valid on the npm registry — always rewrite.\n *\n * Returns a restore function that reverts package.json to original content.\n * Call it in a finally block to guarantee cleanup.\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n// ── rewriteWorkspaceDeps helpers ──────────────────────────────────────────\n\nfunction rewriteLinkDep(\n deps: Record<string, string>,\n depName: string,\n val: string,\n pkgPath: string,\n versionMap: Map<string, string>,\n): void {\n const pinned = versionMap.get(depName);\n if (pinned) {\n deps[depName] = `^${pinned}`;\n return;\n }\n try {\n const linkPath = val.slice('link:'.length);\n const linked = JSON.parse(readFileSync(join(pkgPath, linkPath, 'package.json'), 'utf-8')) as { version?: string };\n deps[depName] = `^${linked.version ?? '*'}`;\n } catch {\n deps[depName] = '*';\n }\n}\n\nfunction rewriteWorkspaceDep(\n deps: Record<string, string>,\n depName: string,\n val: string,\n versionMap: Map<string, string>,\n): boolean {\n const pinned = versionMap.get(depName);\n if (!pinned) { return false; }\n const range = val === 'workspace:*' ? '^' : val.slice('workspace:'.length);\n deps[depName] = `${range}${pinned}`;\n return true;\n}\n\nfunction rewriteDepsSection(\n deps: Record<string, string>,\n pkgPath: string,\n versionMap: Map<string, string>,\n packageManager: string,\n): boolean {\n let modified = false;\n for (const depName of Object.keys(deps)) {\n const val = deps[depName];\n if (typeof val !== 'string') { continue; }\n if (val.startsWith('link:')) {\n rewriteLinkDep(deps, depName, val, pkgPath, versionMap);\n modified = true;\n } else if (val.startsWith('workspace:') && packageManager !== 'pnpm' && rewriteWorkspaceDep(deps, depName, val, versionMap)) {\n modified = true;\n }\n }\n return modified;\n}\n\nexport function rewriteWorkspaceDeps(\n pkg: { path: string; version: string },\n versionMap: Map<string, string>,\n packageManager: string,\n): () => void {\n const pkgPath = pkg.path;\n const pkgJsonPath = join(pkgPath, 'package.json');\n const original = readFileSync(pkgJsonPath, 'utf-8');\n const pkgJson = JSON.parse(original) as Record<string, unknown> & { version?: string };\n let modified = false;\n\n if (pkgJson.version !== pkg.version) {\n pkgJson.version = pkg.version;\n modified = true;\n }\n\n // Every dependency-like section must be rewritten: a published tarball's\n // devDependencies/optionalDependencies\n // field is inert for the *installer* of this package, but npm's own manifest\n // normalization (npm-package-arg validation inside @npmcli/arborist) parses\n // EVERY dependency-like section of every manifest it reads while building the\n // ideal tree — including devDependencies of a package several levels deep in\n // someone else's dependency graph. A leftover \"workspace:*\" there throws\n // EUNSUPPORTEDPROTOCOL and aborts the whole install, not just a devDependency\n // installation that would otherwise be skipped. Confirmed live on\n // @kb-labs/plugin-execution-factory@2.114.0's devDependencies.@kb-labs/gateway-core.\n for (const section of ['dependencies', 'optionalDependencies', 'peerDependencies', 'devDependencies'] as const) {\n const deps = pkgJson[section] as Record<string, string> | undefined;\n if (!deps) { continue; }\n if (rewriteDepsSection(deps, pkgPath, versionMap, packageManager)) { modified = true; }\n }\n\n if (modified) {\n writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\\n');\n }\n\n return () => writeFileSync(pkgJsonPath, original);\n}\n\n// ── topoSort helpers ──────────────────────────────────────────────────────\n\n/**\n * Build a simple topological sort of packages for publish order.\n *\n * Packages are grouped into \"waves\": wave 0 has no intra-release deps,\n * wave 1 depends only on wave 0, etc. Within each wave publish order\n * is arbitrary (safe to parallelise).\n *\n * Cycles (should not exist in a well-formed monorepo) fall into a\n * \"remainder\" group appended at the end.\n */\nexport interface TopoPackage {\n name: string;\n path: string;\n version: string;\n}\n\nfunction buildIntraDeps(packages: TopoPackage[], nameSet: Set<string>): Map<string, Set<string>> {\n const deps = new Map<string, Set<string>>();\n for (const pkg of packages) {\n const intra = new Set<string>();\n try {\n const pkgJson = JSON.parse(readFileSync(join(pkg.path, 'package.json'), 'utf-8')) as {\n dependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n };\n for (const section of ['dependencies', 'peerDependencies'] as const) {\n for (const depName of Object.keys(pkgJson[section] ?? {})) {\n if (nameSet.has(depName)) { intra.add(depName); }\n }\n }\n } catch {\n // If we can't read package.json, treat as no deps\n }\n deps.set(pkg.name, intra);\n }\n return deps;\n}\n\nfunction buildDependantsMap(packages: TopoPackage[], deps: Map<string, Set<string>>): Map<string, Set<string>> {\n const dependants = new Map<string, Set<string>>();\n for (const pkg of packages) {\n for (const dep of deps.get(pkg.name) ?? []) {\n if (!dependants.has(dep)) { dependants.set(dep, new Set()); }\n dependants.get(dep)!.add(pkg.name);\n }\n }\n return dependants;\n}\n\nfunction computeWaves(\n packages: TopoPackage[],\n deps: Map<string, Set<string>>,\n dependants: Map<string, Set<string>>,\n): TopoPackage[][] {\n const byName = new Map(packages.map(p => [p.name, p]));\n const remaining = new Map<string, number>(\n packages.map(p => [p.name, (deps.get(p.name) ?? new Set()).size])\n );\n const waves: TopoPackage[][] = [];\n\n while (remaining.size > 0) {\n const wave = [...remaining.entries()]\n .filter(([, count]) => count === 0)\n .map(([name]) => byName.get(name)!);\n\n if (wave.length === 0) {\n waves.push([...remaining.keys()].map(n => byName.get(n)!));\n break;\n }\n\n waves.push(wave);\n for (const pkg of wave) {\n remaining.delete(pkg.name);\n for (const dependant of dependants.get(pkg.name) ?? []) {\n if (remaining.has(dependant)) {\n remaining.set(dependant, (remaining.get(dependant) ?? 1) - 1);\n }\n }\n }\n }\n\n return waves.filter(w => w.length > 0);\n}\n\nexport function topoSort(packages: TopoPackage[], _packageManager: string): TopoPackage[][] {\n const nameSet = new Set(packages.map(p => p.name));\n const deps = buildIntraDeps(packages, nameSet);\n const dependants = buildDependantsMap(packages, deps);\n return computeWaves(packages, deps, dependants);\n}\n","/**\n * Shared `--release-tag` / `--flow` resolution for commands that operate\n * on an already-decided release (`stage`, `deliver`) — mirrors the tag\n * grammar in `@kb-labs/release-manager-core`'s `resolveFlowFromTag` so CI\n * can pass just the tag and the plugin decides the flow, per the\n * \"plugin prepares, CI delivers\" release plan.\n */\n\nimport { resolveFlowFromTag, type ReleaseConfig } from '@kb-labs/release-manager-core';\n\nexport interface FlowResolvableFlags {\n flow?: string;\n 'release-tag'?: string;\n}\n\nexport function resolveFlowName(config: ReleaseConfig, flags: FlowResolvableFlags): string | { error: string } {\n if (flags.flow) { return flags.flow; }\n if (flags['release-tag']) {\n const resolved = resolveFlowFromTag(config, flags['release-tag']);\n if (!resolved) {\n return { error: `Could not resolve a release.flows entry from tag \"${flags['release-tag']}\"` };\n }\n return resolved.flowName;\n }\n return { error: 'requires --flow or --release-tag' };\n}\n","/**\n * Standalone stage command — packs the flow's currently-committed package\n * versions into real npm tarballs, once, so every downstream `release\n * deliver` job ships the exact same bytes instead of re-packing\n * independently from source. See plugins/release/docs/adr/0001-* and the\n * \"plugin prepares, CI delivers\" release plan.\n *\n * Deliberately does NOT call planRelease() — same reasoning as promote.ts:\n * versions are already fixed by the prepare step that already ran (version\n * bump, changelog, git commit/tag/push). This command's only job is\n * \"pack exactly what's already on disk, unchanged.\"\n *\n * Not to be confused with `release:pack` (`pack.ts`) — that command\n * verifies *proposed* packages via `npm pack` + static checks before a\n * release is decided; this one produces the *actual* tarball artifact for\n * an already-decided release.\n */\n\nimport { createHash } from 'node:crypto';\nimport { spawnSync } from 'node:child_process';\nimport { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, join } from 'node:path';\nimport { defineCommand, type CLIInput, type PluginContextV3, useLoader, useConfig, useEnv, type CommandResult } from '@kb-labs/sdk';\nimport {\n discoverCurrentPackages,\n mergeConfigWithFlow,\n verifyExtractedTarball,\n type ReleaseConfig,\n} from '@kb-labs/release-manager-core';\nimport { findRepoRoot } from '../../shared/utils';\nimport { rewriteWorkspaceDeps } from '../../shared/dep-rewrite';\nimport { resolveFlowName, type FlowResolvableFlags } from '../../shared/resolve-flow';\n\ninterface StageFlags extends FlowResolvableFlags {\n 'out-dir'?: string;\n json?: boolean;\n}\n\nexport interface StagedArtifact {\n name: string;\n version: string;\n tarball: string;\n sha256: string;\n}\n\ninterface StagePayload {\n outDir?: string;\n artifacts?: StagedArtifact[];\n}\n\n/**\n * Pack one package into `outDir`, returning the produced tarball's filename.\n *\n * pnpm is the platform default and owns workspace protocol materialization.\n * npm/yarn are explicit opt-in paths and get the rewrite needed for their\n * standalone tarballs.\n */\n/** pnpm prints the produced tarball's absolute path as its last stdout line — no --json output exists. */\nfunction filenameFromPnpmPack(stdout: string): string | undefined {\n const lines = stdout.trim().split('\\n').filter(Boolean);\n const lastLine = lines[lines.length - 1];\n return lastLine ? basename(lastLine.trim()) : undefined;\n}\n\n/** `npm pack --json` prints a JSON array; we only ever pack one package at a time. */\nfunction filenameFromNpmPack(stdout: string): string | undefined {\n const parsed = JSON.parse(stdout) as Array<{ filename: string }>;\n return parsed[0]?.filename;\n}\n\nfunction packOne(\n pkg: { path: string; currentVersion: string },\n outDir: string,\n packageManager: 'pnpm' | 'npm' | 'yarn',\n versionMap: Map<string, string>,\n): { filename: string; restore: () => void } {\n const isPnpm = packageManager === 'pnpm';\n const restore = rewriteWorkspaceDeps(\n { path: pkg.path, version: pkg.currentVersion },\n versionMap,\n packageManager,\n );\n\n const packArgs = isPnpm\n ? ['pack', '--pack-destination', outDir]\n : ['pack', '--pack-destination', outDir, '--json'];\n const packResult = spawnSync(isPnpm ? 'pnpm' : 'npm', packArgs, { cwd: pkg.path, stdio: 'pipe' });\n\n if (packResult.status !== 0) {\n const stderr = packResult.stderr?.toString().trim() || packResult.stdout?.toString().trim();\n throw new Error(`${packageManager} pack failed for ${pkg.path}${stderr ? `: ${stderr}` : ''}`);\n }\n\n const filename = isPnpm\n ? filenameFromPnpmPack(packResult.stdout.toString())\n : filenameFromNpmPack(packResult.stdout.toString());\n\n if (!filename) {\n throw new Error(`${packageManager} pack produced no tarball for ${pkg.path}`);\n }\n return { filename, restore };\n}\n\n/**\n * Pack one package. Static artifact verification happens after all packages\n * have been packed, so Stage never performs a registry-style install per\n * package.\n */\nasync function packStagePackage(\n pkg: { name: string; path: string; currentVersion: string },\n outDir: string,\n packageManager: 'pnpm' | 'npm' | 'yarn',\n versionMap: Map<string, string>,\n): Promise<StagedArtifact> {\n const { filename, restore } = packOne(pkg, outDir, packageManager, versionMap);\n try {\n const sha256 = createHash('sha256').update(readFileSync(join(outDir, filename))).digest('hex');\n return { name: pkg.name, version: pkg.currentVersion, tarball: filename, sha256 };\n } finally { restore(); }\n}\n\nfunction verifyStagePackage(\n pkg: { name: string; currentVersion: string },\n outDir: string,\n artifact: StagedArtifact,\n): void {\n const tarballPath = join(outDir, artifact.tarball);\n const verifyDir = mkdtempSync(join(tmpdir(), 'kb-stage-verify-'));\n try {\n const extractResult = spawnSync('tar', ['xzf', tarballPath, '-C', verifyDir], { stdio: 'pipe' });\n if (extractResult.status !== 0) throw new Error(`could not extract staged tarball for ${pkg.name}@${pkg.currentVersion}`);\n const issues = verifyExtractedTarball(join(verifyDir, 'package'));\n if (issues.length > 0) throw new Error(`staged artifact verification failed for ${pkg.name}@${pkg.currentVersion}: ${issues.join('; ')}`);\n } finally { rmSync(verifyDir, { recursive: true, force: true }); }\n\n}\n\nexport default defineCommand({\n id: 'release:stage',\n description: 'Pack the currently-committed package versions for a flow into real npm tarballs, once, for `release deliver` to ship',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<StageFlags>): Promise<CommandResult<StagePayload>> {\n const { flags } = input;\n const cwd = ctx.cwd || process.cwd();\n const repoRoot = await findRepoRoot(cwd);\n\n const fileConfig = await useConfig<ReleaseConfig>();\n const baseConfig: ReleaseConfig = fileConfig ?? {};\n\n const flowResult = resolveFlowName(baseConfig, flags);\n if (typeof flowResult !== 'string') {\n const msg = `release:stage ${flowResult.error}`;\n if (flags.json) { ctx.ui?.json?.({ error: msg }); } else { ctx.ui?.error?.(msg); }\n return { ok: false, error: 'Command failed' };\n }\n const flowName = flowResult;\n\n const config: ReleaseConfig = mergeConfigWithFlow(baseConfig, flowName);\n const outDir = join(repoRoot, flags['out-dir'] ?? '.kb/release/artifacts');\n mkdirSync(outDir, { recursive: true });\n\n const discoverLoader = useLoader(`Discovering currently-committed packages for flow \"${flowName}\"...`);\n discoverLoader.start();\n const discovered = await discoverCurrentPackages(repoRoot, undefined, config);\n discoverLoader.succeed(`Found ${discovered.length} package(s) to stage`);\n\n if (discovered.length === 0) {\n const msg = `No packages found for flow \"${flowName}\"`;\n if (flags.json) { ctx.ui?.json?.({ outDir, artifacts: [] }); } else { ctx.ui?.write?.(msg); }\n return { ok: false, error: 'Command failed' };\n }\n\n // versionMap must cover the WHOLE workspace, not just this flow's packages:\n // a flow package can depend on a package owned by a different flow (e.g. sdk\n // depends on @kb-labs/core-retry, which is platform-flow-scoped). Building the\n // map from `discovered` (flow-scoped) alone leaves such cross-flow deps out of\n // versionMap, so rewriteWorkspaceDeps silently skips them (no pinned version to\n // substitute). Only used by the npm/yarn packing path below — pnpm resolves\n // workspace:* on its own and never consults this map.\n const allWorkspacePackages = await discoverCurrentPackages(repoRoot, undefined, baseConfig);\n const versionMap = new Map(allWorkspacePackages.map(pkg => [pkg.name, pkg.currentVersion]));\n const packageManager = config.workspace?.type ?? config.publish?.packageManager ?? 'pnpm';\n\n const CONCURRENCY = Number(useEnv('KB_STAGE_CONCURRENCY') ?? 6);\n\n const packLoader = useLoader('Packing tarballs...');\n packLoader.start();\n const artifacts: StagedArtifact[] = [];\n for (let i = 0; i < discovered.length; i += CONCURRENCY) {\n const batch = discovered.slice(i, i + CONCURRENCY);\n const batchArtifacts = await Promise.all(\n batch.map(pkg => packStagePackage(pkg, outDir, packageManager, versionMap)),\n );\n artifacts.push(...batchArtifacts);\n }\n packLoader.succeed(`Packed ${artifacts.length} tarball(s) to ${outDir}`);\n\n const verifyLoader = useLoader('Verifying staged tarballs...');\n verifyLoader.start();\n for (let i = 0; i < discovered.length; i += CONCURRENCY) {\n const batch = discovered.slice(i, i + CONCURRENCY);\n batch.forEach((pkg, index) => verifyStagePackage(pkg, outDir, artifacts[i + index]!));\n }\n verifyLoader.succeed(`Verified ${artifacts.length} staged tarball(s)`);\n\n writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(artifacts, null, 2) + '\\n', 'utf-8');\n\n const result: StagePayload = { outDir, artifacts };\n if (flags.json) {\n const response = { ok: true as const, result };\n ctx.ui?.json?.(response);\n return response;\n }\n\n ctx.ui?.sideBox?.({\n title: 'Stage',\n sections: [{\n header: `Staged to ${outDir}`,\n items: artifacts.map(a => `${ctx.ui.symbols.success} ${a.name}@${a.version} → ${a.tarball}`),\n }],\n status: 'success',\n });\n\n return { ok: true, result };\n },\n },\n});\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
import { CLIInput } from '@kb-labs/sdk';
|
|
3
|
+
import { CleanInstallResult } from '@kb-labs/release-manager-core';
|
|
4
|
+
|
|
5
|
+
interface VerifyCleanInstallFlags {
|
|
6
|
+
tarball: string;
|
|
7
|
+
name: string;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<VerifyCleanInstallFlags>, CleanInstallResult>;
|
|
11
|
+
|
|
12
|
+
export { _default as default };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineCommand } from '@kb-labs/sdk';
|
|
2
|
+
import { verifyCleanInstall } from '@kb-labs/release-manager-core';
|
|
3
|
+
|
|
4
|
+
// src/cli/commands/verify-clean-install.ts
|
|
5
|
+
var verify_clean_install_default = defineCommand({
|
|
6
|
+
id: "release:clean-install",
|
|
7
|
+
description: "Install a packed tarball into a throwaway consumer and confirm it imports cleanly",
|
|
8
|
+
handler: {
|
|
9
|
+
async execute(ctx, input) {
|
|
10
|
+
const { flags } = input;
|
|
11
|
+
if (!flags.tarball || !flags.name) {
|
|
12
|
+
const msg = "release:clean-install requires --tarball <path> and --name <package-name>";
|
|
13
|
+
if (flags.json) {
|
|
14
|
+
ctx.ui?.json?.({ error: msg });
|
|
15
|
+
} else {
|
|
16
|
+
ctx.ui?.error?.(msg);
|
|
17
|
+
}
|
|
18
|
+
return { ok: false, error: "Command failed" };
|
|
19
|
+
}
|
|
20
|
+
const result = await verifyCleanInstall(flags.tarball, flags.name);
|
|
21
|
+
if (!result.ok) {
|
|
22
|
+
if (flags.json) {
|
|
23
|
+
ctx.ui?.json?.({ ok: false, result });
|
|
24
|
+
} else {
|
|
25
|
+
ctx.ui?.error?.(result.error ?? "clean install verification failed");
|
|
26
|
+
}
|
|
27
|
+
return { ok: false, error: "Command failed" };
|
|
28
|
+
}
|
|
29
|
+
if (flags.json) {
|
|
30
|
+
const response = { ok: true, result };
|
|
31
|
+
ctx.ui?.json?.(response);
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
ctx.ui?.write?.(`OK: ${flags.name} installs and imports cleanly`);
|
|
35
|
+
return { ok: true, result };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export { verify_clean_install_default as default };
|
|
41
|
+
//# sourceMappingURL=verify-clean-install.js.map
|
|
42
|
+
//# sourceMappingURL=verify-clean-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/verify-clean-install.ts"],"names":[],"mappings":";;;;AAqBA,IAAO,+BAAQ,aAAA,CAAc;AAAA,EAC3B,EAAA,EAAI,uBAAA;AAAA,EACJ,WAAA,EAAa,mFAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,MAAM,OAAA,CAAQ,GAAA,EAAsB,KAAA,EAAsF;AACxH,MAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAElB,MAAA,IAAI,CAAC,KAAA,CAAM,OAAA,IAAW,CAAC,MAAM,IAAA,EAAM;AACjC,QAAA,MAAM,GAAA,GAAM,2EAAA;AACZ,QAAA,IAAI,MAAM,IAAA,EAAM;AAAE,UAAA,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,EAAE,KAAA,EAAO,KAAK,CAAA;AAAA,QAAG,CAAA,MAAO;AAAE,UAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,GAAG,CAAA;AAAA,QAAG;AACjF,QAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,gBAAA,EAAiB;AAAA,MAC9C;AAEA,MAAA,MAAM,SAAS,MAAM,kBAAA,CAAmB,KAAA,CAAM,OAAA,EAAS,MAAM,IAAI,CAAA;AAEjE,MAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,QAAA,IAAI,MAAM,IAAA,EAAM;AAAE,UAAA,GAAA,CAAI,IAAI,IAAA,GAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,CAAA;AAAA,QAAG,CAAA,MAAO;AAAE,UAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,MAAA,CAAO,KAAA,IAAS,mCAAmC,CAAA;AAAA,QAAG;AACxI,QAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,gBAAA,EAAiB;AAAA,MAC9C;AAEA,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAM,QAAA,GAAW,EAAE,EAAA,EAAI,IAAA,EAAe,MAAA,EAAO;AAC7C,QAAA,GAAA,CAAI,EAAA,EAAI,OAAO,QAAQ,CAAA;AACvB,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,CAAA,IAAA,EAAO,KAAA,CAAM,IAAI,CAAA,6BAAA,CAA+B,CAAA;AAChE,MAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,MAAA,EAAO;AAAA,IAC5B;AAAA;AAEJ,CAAC","file":"verify-clean-install.js","sourcesContent":["/**\n * Standalone clean-install verification — installs a packed tarball into a\n * throwaway consumer project and confirms it can actually be imported.\n *\n * Exists as its own command so `check-pack-install.sh` (the bash release\n * gate) and `release stage` (the CI publish path) share ONE verification\n * implementation instead of the gate re-shelling to `npm install` on its\n * own and getting npm's swallowed, useless error message back (npm's CLI\n * catches the exact EUNSUPPORTEDPROTOCOL failure this exists to catch as an\n * unhandled rejection and prints nothing beyond \"npm error, see log file\").\n */\n\nimport { defineCommand, type CLIInput, type PluginContextV3, type CommandResult } from '@kb-labs/sdk';\nimport { verifyCleanInstall, type CleanInstallResult } from '@kb-labs/release-manager-core';\n\ninterface VerifyCleanInstallFlags {\n tarball: string;\n name: string;\n json?: boolean;\n}\n\nexport default defineCommand({\n id: 'release:clean-install',\n description: 'Install a packed tarball into a throwaway consumer and confirm it imports cleanly',\n\n handler: {\n async execute(ctx: PluginContextV3, input: CLIInput<VerifyCleanInstallFlags>): Promise<CommandResult<CleanInstallResult>> {\n const { flags } = input;\n\n if (!flags.tarball || !flags.name) {\n const msg = 'release:clean-install requires --tarball <path> and --name <package-name>';\n if (flags.json) { ctx.ui?.json?.({ error: msg }); } else { ctx.ui?.error?.(msg); }\n return { ok: false, error: 'Command failed' };\n }\n\n const result = await verifyCleanInstall(flags.tarball, flags.name);\n\n if (!result.ok) {\n if (flags.json) { ctx.ui?.json?.({ ok: false, result }); } else { ctx.ui?.error?.(result.error ?? 'clean install verification failed'); }\n return { ok: false, error: 'Command failed' };\n }\n\n if (flags.json) {\n const response = { ok: true as const, result };\n ctx.ui?.json?.(response);\n return response;\n }\n\n ctx.ui?.write?.(`OK: ${flags.name} installs and imports cleanly`);\n return { ok: true, result };\n },\n },\n});\n"]}
|
package/dist/index.js
CHANGED
|
@@ -192,6 +192,24 @@ var manifest = {
|
|
|
192
192
|
"kb release stage --flow sdk --out-dir .kb/release/artifacts"
|
|
193
193
|
]
|
|
194
194
|
},
|
|
195
|
+
// release:clean-install - Shared install-verification used by both
|
|
196
|
+
// check-pack-install.sh and `release stage`
|
|
197
|
+
{
|
|
198
|
+
path: "release clean install",
|
|
199
|
+
category: "Publish",
|
|
200
|
+
describe: "Install a packed tarball into a throwaway consumer and confirm it imports cleanly",
|
|
201
|
+
operationType: "execute",
|
|
202
|
+
longDescription: "Installs --tarball into a fresh, outside-the-workspace consumer project and confirms --name can be imported afterward. Shared by check-pack-install.sh (local release gate) and `release stage` (CI publish path) so both use identical verification \u2014 catches an already-published PEER dependency that is itself broken, which static manifest checks cannot see. Surfaces the real failure reason via @npmcli/arborist directly; plain `npm install` swallows this exact failure class as an unhandled rejection with no usable error.",
|
|
203
|
+
handler: "./cli/commands/verify-clean-install.js#default",
|
|
204
|
+
flags: defineCommandFlags({
|
|
205
|
+
tarball: { type: "string", description: "Path to the packed tarball to install" },
|
|
206
|
+
name: { type: "string", description: "Package name to import after install" },
|
|
207
|
+
json: { type: "boolean", description: "Output in JSON format" }
|
|
208
|
+
}),
|
|
209
|
+
examples: [
|
|
210
|
+
"kb release clean install --tarball ./kb-labs-sdk-2.115.0.tgz --name @kb-labs/sdk"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
195
213
|
// release:promote - Promote an already-released version to npm
|
|
196
214
|
{
|
|
197
215
|
path: "release promote",
|