@kb-labs/release-manager-cli 2.82.0 → 2.87.0
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/cli/commands/build.d.ts +10 -0
- package/dist/cli/commands/build.js +87 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/changelog.js +65 -57
- package/dist/cli/commands/changelog.js.map +1 -1
- package/dist/cli/commands/checks.d.ts +10 -0
- package/dist/cli/commands/checks.js +93 -0
- package/dist/cli/commands/checks.js.map +1 -0
- package/dist/cli/commands/git.d.ts +13 -0
- package/dist/cli/commands/git.js +93 -0
- package/dist/cli/commands/git.js.map +1 -0
- package/dist/cli/commands/pack.d.ts +10 -0
- package/dist/cli/commands/pack.js +83 -0
- package/dist/cli/commands/pack.js.map +1 -0
- package/dist/cli/commands/plan.js +40 -41
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/publish.js +225 -194
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/report.js +28 -31
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/rollback.js +0 -3
- package/dist/cli/commands/rollback.js.map +1 -1
- package/dist/cli/commands/run.js +386 -300
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/verify.js +29 -28
- package/dist/cli/commands/verify.js.map +1 -1
- package/dist/cli/commands/version.d.ts +12 -0
- package/dist/cli/commands/version.js +111 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +146 -13
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +9 -0
- package/dist/manifest.js +131 -0
- package/dist/manifest.js.map +1 -1
- package/dist/rest/handlers/build-handler.js +22 -20
- package/dist/rest/handlers/build-handler.js.map +1 -1
- package/dist/rest/handlers/changelog-generate-handler.js +75 -88
- package/dist/rest/handlers/changelog-generate-handler.js.map +1 -1
- package/dist/rest/handlers/changelog-handler.js +1 -4
- package/dist/rest/handlers/changelog-handler.js.map +1 -1
- package/dist/rest/handlers/changelog-save-handler.js +0 -2
- package/dist/rest/handlers/changelog-save-handler.js.map +1 -1
- package/dist/rest/handlers/checklist-handler.js +113 -99
- package/dist/rest/handlers/checklist-handler.js.map +1 -1
- package/dist/rest/handlers/generate-handler.js +0 -3
- package/dist/rest/handlers/generate-handler.js.map +1 -1
- package/dist/rest/handlers/git-timeline-handler.js.map +1 -1
- package/dist/rest/handlers/history-changelog-handler.js +0 -1
- package/dist/rest/handlers/history-changelog-handler.js.map +1 -1
- package/dist/rest/handlers/history-handler.js +2 -3
- package/dist/rest/handlers/history-handler.js.map +1 -1
- package/dist/rest/handlers/history-plan-handler.js +0 -1
- package/dist/rest/handlers/history-plan-handler.js.map +1 -1
- package/dist/rest/handlers/history-report-handler.js +0 -1
- package/dist/rest/handlers/history-report-handler.js.map +1 -1
- package/dist/rest/handlers/plan-handler.js +0 -3
- package/dist/rest/handlers/plan-handler.js.map +1 -1
- package/dist/rest/handlers/preview-handler.js +0 -1
- package/dist/rest/handlers/preview-handler.js.map +1 -1
- package/dist/rest/handlers/report-handler.js +1 -1
- package/dist/rest/handlers/report-handler.js.map +1 -1
- package/dist/rest/handlers/reset-handler.js +0 -1
- package/dist/rest/handlers/reset-handler.js.map +1 -1
- package/dist/rest/handlers/run-checks-handler.js +0 -1
- package/dist/rest/handlers/run-checks-handler.js.map +1 -1
- package/dist/rest/handlers/run-handler.js +72 -52
- package/dist/rest/handlers/run-handler.js.map +1 -1
- package/dist/rest/handlers/status-handler.js +0 -2
- package/dist/rest/handlers/status-handler.js.map +1 -1
- package/dist/setup/handler.js.map +1 -1
- package/package.json +9 -8
package/dist/cli/commands/run.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import * as readline from 'readline/promises';
|
|
2
|
-
import { defineCommand, findRepoRoot, useLoader, useConfig,
|
|
2
|
+
import { defineCommand, findRepoRoot, useLoader, useConfig, useEnv, useLLM, useLogger } from '@kb-labs/sdk';
|
|
3
3
|
import { resolveScopePath, planRelease, runReleasePipeline } from '@kb-labs/release-manager-core';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
import 'fs/promises';
|
|
6
|
-
import 'globby';
|
|
7
4
|
import { generateChangelog, generateSimpleChangelog } from '@kb-labs/release-manager-changelog';
|
|
8
5
|
import { spawn } from 'child_process';
|
|
9
6
|
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
10
8
|
|
|
11
9
|
// src/cli/commands/run.ts
|
|
12
10
|
function createChangelogGenerator(config, llm) {
|
|
@@ -52,6 +50,44 @@ function createChangelogGenerator(config, llm) {
|
|
|
52
50
|
}
|
|
53
51
|
};
|
|
54
52
|
}
|
|
53
|
+
function rewriteLinkDep(deps, depName, val, pkgPath, versionMap) {
|
|
54
|
+
const pinned = versionMap.get(depName);
|
|
55
|
+
if (pinned) {
|
|
56
|
+
deps[depName] = `^${pinned}`;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const linkPath = val.slice("link:".length);
|
|
61
|
+
const linked = JSON.parse(readFileSync(join(pkgPath, linkPath, "package.json"), "utf-8"));
|
|
62
|
+
deps[depName] = `^${linked.version ?? "*"}`;
|
|
63
|
+
} catch {
|
|
64
|
+
deps[depName] = "*";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function rewriteWorkspaceDep(deps, depName, val, versionMap) {
|
|
68
|
+
const pinned = versionMap.get(depName);
|
|
69
|
+
if (!pinned) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
deps[depName] = val === "workspace:*" ? `^${pinned}` : val.replace("workspace:", "");
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function rewriteDepsSection(deps, pkgPath, versionMap, packageManager) {
|
|
76
|
+
let modified = false;
|
|
77
|
+
for (const depName of Object.keys(deps)) {
|
|
78
|
+
const val = deps[depName];
|
|
79
|
+
if (typeof val !== "string") {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (val.startsWith("link:")) {
|
|
83
|
+
rewriteLinkDep(deps, depName, val, pkgPath, versionMap);
|
|
84
|
+
modified = true;
|
|
85
|
+
} else if (val.startsWith("workspace:") && packageManager !== "pnpm" && rewriteWorkspaceDep(deps, depName, val, versionMap)) {
|
|
86
|
+
modified = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return modified;
|
|
90
|
+
}
|
|
55
91
|
function rewriteWorkspaceDeps(pkgPath, versionMap, packageManager) {
|
|
56
92
|
const pkgJsonPath = join(pkgPath, "package.json");
|
|
57
93
|
const original = readFileSync(pkgJsonPath, "utf-8");
|
|
@@ -62,32 +98,8 @@ function rewriteWorkspaceDeps(pkgPath, versionMap, packageManager) {
|
|
|
62
98
|
if (!deps) {
|
|
63
99
|
continue;
|
|
64
100
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (typeof val !== "string") {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (val.startsWith("link:")) {
|
|
71
|
-
const pinned = versionMap.get(depName);
|
|
72
|
-
if (pinned) {
|
|
73
|
-
deps[depName] = `^${pinned}`;
|
|
74
|
-
} else {
|
|
75
|
-
try {
|
|
76
|
-
const linkPath = val.slice("link:".length);
|
|
77
|
-
const linked = JSON.parse(readFileSync(join(pkgPath, linkPath, "package.json"), "utf-8"));
|
|
78
|
-
deps[depName] = `^${linked.version ?? "*"}`;
|
|
79
|
-
} catch {
|
|
80
|
-
deps[depName] = "*";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
modified = true;
|
|
84
|
-
} else if (val.startsWith("workspace:") && packageManager !== "pnpm") {
|
|
85
|
-
const pinned = versionMap.get(depName);
|
|
86
|
-
if (pinned) {
|
|
87
|
-
deps[depName] = val === "workspace:*" ? `^${pinned}` : val.replace("workspace:", "");
|
|
88
|
-
modified = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
101
|
+
if (rewriteDepsSection(deps, pkgPath, versionMap, packageManager)) {
|
|
102
|
+
modified = true;
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
if (modified) {
|
|
@@ -95,8 +107,7 @@ function rewriteWorkspaceDeps(pkgPath, versionMap, packageManager) {
|
|
|
95
107
|
}
|
|
96
108
|
return () => writeFileSync(pkgJsonPath, original);
|
|
97
109
|
}
|
|
98
|
-
function
|
|
99
|
-
const nameSet = new Set(packages.map((p) => p.name));
|
|
110
|
+
function buildIntraDeps(packages, nameSet) {
|
|
100
111
|
const deps = /* @__PURE__ */ new Map();
|
|
101
112
|
for (const pkg of packages) {
|
|
102
113
|
const intra = /* @__PURE__ */ new Set();
|
|
@@ -113,12 +124,9 @@ function topoSort(packages, packageManager) {
|
|
|
113
124
|
}
|
|
114
125
|
deps.set(pkg.name, intra);
|
|
115
126
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
127
|
+
return deps;
|
|
128
|
+
}
|
|
129
|
+
function buildDependantsMap(packages, deps) {
|
|
122
130
|
const dependants = /* @__PURE__ */ new Map();
|
|
123
131
|
for (const pkg of packages) {
|
|
124
132
|
for (const dep of deps.get(pkg.name) ?? []) {
|
|
@@ -128,10 +136,13 @@ function topoSort(packages, packageManager) {
|
|
|
128
136
|
dependants.get(dep).add(pkg.name);
|
|
129
137
|
}
|
|
130
138
|
}
|
|
139
|
+
return dependants;
|
|
140
|
+
}
|
|
141
|
+
function computeWaves(packages, deps, dependants) {
|
|
142
|
+
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
131
143
|
const remaining = new Map(
|
|
132
144
|
packages.map((p) => [p.name, (deps.get(p.name) ?? /* @__PURE__ */ new Set()).size])
|
|
133
145
|
);
|
|
134
|
-
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
135
146
|
const waves = [];
|
|
136
147
|
while (remaining.size > 0) {
|
|
137
148
|
const wave = [...remaining.entries()].filter(([, count]) => count === 0).map(([name]) => byName.get(name));
|
|
@@ -151,6 +162,12 @@ function topoSort(packages, packageManager) {
|
|
|
151
162
|
}
|
|
152
163
|
return waves.filter((w) => w.length > 0);
|
|
153
164
|
}
|
|
165
|
+
function topoSort(packages, _packageManager) {
|
|
166
|
+
const nameSet = new Set(packages.map((p) => p.name));
|
|
167
|
+
const deps = buildIntraDeps(packages, nameSet);
|
|
168
|
+
const dependants = buildDependantsMap(packages, deps);
|
|
169
|
+
return computeWaves(packages, deps, dependants);
|
|
170
|
+
}
|
|
154
171
|
|
|
155
172
|
// src/shared/publish-programmatic.ts
|
|
156
173
|
var MAX_RETRIES = 5;
|
|
@@ -292,7 +309,9 @@ async function publishOne(pkg, opts, logger) {
|
|
|
292
309
|
logger.warn(
|
|
293
310
|
`Transient error for ${pkg.name}@${pkg.version} (attempt ${attempt + 1}/${MAX_RETRIES}), retrying in ${(delay / 1e3).toFixed(1)}s: ${message.split("\n")[0]}`
|
|
294
311
|
);
|
|
295
|
-
await new Promise((r) =>
|
|
312
|
+
await new Promise((r) => {
|
|
313
|
+
setTimeout(r, delay);
|
|
314
|
+
});
|
|
296
315
|
continue;
|
|
297
316
|
}
|
|
298
317
|
logger.error(`Failed to publish ${pkg.name}@${pkg.version}`, void 0, { error: message });
|
|
@@ -309,6 +328,20 @@ async function publishOne(pkg, opts, logger) {
|
|
|
309
328
|
restore();
|
|
310
329
|
}
|
|
311
330
|
}
|
|
331
|
+
function skipRemainingWaves(waves, waveIdx, waveFailed, logger, allResults) {
|
|
332
|
+
const remaining = waves.slice(waveIdx + 1).reduce((acc, w) => acc + w.length, 0);
|
|
333
|
+
if (remaining === 0) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
logger.warn(
|
|
337
|
+
`Wave ${waveIdx + 1} had ${waveFailed.length} failure(s) \u2014 skipping ${remaining} package(s) in later waves to avoid broken dependency chain`
|
|
338
|
+
);
|
|
339
|
+
for (const laterWave of waves.slice(waveIdx + 1)) {
|
|
340
|
+
for (const pkg of laterWave) {
|
|
341
|
+
allResults.push({ name: pkg.name, version: pkg.version, success: false, error: "Skipped: dependency wave failed" });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
312
345
|
async function publishPackagesProgrammatic(options) {
|
|
313
346
|
const {
|
|
314
347
|
packages,
|
|
@@ -354,22 +387,7 @@ async function publishPackagesProgrammatic(options) {
|
|
|
354
387
|
}
|
|
355
388
|
const waveFailed = allResults.slice(-wave.length).filter((r) => !r.success);
|
|
356
389
|
if (waveFailed.length > 0) {
|
|
357
|
-
|
|
358
|
-
if (remaining > 0) {
|
|
359
|
-
logger.warn(
|
|
360
|
-
`Wave ${waveIdx + 1} had ${waveFailed.length} failure(s) \u2014 skipping ${remaining} package(s) in later waves to avoid broken dependency chain`
|
|
361
|
-
);
|
|
362
|
-
for (const laterWave of waves.slice(waveIdx + 1)) {
|
|
363
|
-
for (const pkg of laterWave) {
|
|
364
|
-
allResults.push({
|
|
365
|
-
name: pkg.name,
|
|
366
|
-
version: pkg.version,
|
|
367
|
-
success: false,
|
|
368
|
-
error: "Skipped: dependency wave failed"
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
390
|
+
skipRemainingWaves(waves, waveIdx, waveFailed, logger, allResults);
|
|
373
391
|
break;
|
|
374
392
|
}
|
|
375
393
|
}
|
|
@@ -461,65 +479,125 @@ function publishSinglePackage(options) {
|
|
|
461
479
|
child.on("error", (err) => reject(err));
|
|
462
480
|
});
|
|
463
481
|
}
|
|
464
|
-
async function
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return {
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
482
|
+
async function fastPublishPkg(pkg, ctx, logger) {
|
|
483
|
+
const restore = rewriteWorkspaceDeps(pkg.path, ctx.versionMap, ctx.packageManager);
|
|
484
|
+
try {
|
|
485
|
+
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
486
|
+
try {
|
|
487
|
+
await publishSinglePackage({ packagePath: pkg.path, packageManager: ctx.packageManager, otp: ctx.otp, dryRun: ctx.dryRun, tag: ctx.tag, access: ctx.access });
|
|
488
|
+
logger?.info("Published (fast-path)", { name: pkg.name, version: pkg.version });
|
|
489
|
+
return { result: { name: pkg.name, version: pkg.version, success: true }, otpFailed: false };
|
|
490
|
+
} catch (err) {
|
|
491
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
492
|
+
if (isAlreadyPublished2(msg)) {
|
|
493
|
+
logger?.info(`${pkg.name}@${pkg.version} already published \u2014 skipping`);
|
|
494
|
+
return { result: { name: pkg.name, version: pkg.version, success: true, alreadyPublished: true }, otpFailed: false };
|
|
495
|
+
}
|
|
496
|
+
if (msg.includes("EOTP") || msg.includes("one-time password")) {
|
|
497
|
+
return { result: null, otpFailed: true };
|
|
498
|
+
}
|
|
499
|
+
if (isRetryable2(msg) && attempt < MAX_RETRIES2) {
|
|
500
|
+
const delay = retryDelay2(attempt);
|
|
501
|
+
logger?.warn(`Transient error for ${pkg.name} (attempt ${attempt + 1}/${MAX_RETRIES2}), retrying in ${(delay / 1e3).toFixed(1)}s`);
|
|
502
|
+
await new Promise((r) => {
|
|
503
|
+
setTimeout(r, delay);
|
|
504
|
+
});
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
logger?.error("Publish failed (fast-path)", void 0, { name: pkg.name, error: msg });
|
|
508
|
+
return { result: { name: pkg.name, version: pkg.version, success: false, error: msg }, otpFailed: false };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return { result: { name: pkg.name, version: pkg.version, success: false, error: `Publish failed after ${MAX_RETRIES2} retries (transient errors)` }, otpFailed: false };
|
|
512
|
+
} finally {
|
|
513
|
+
restore();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async function promptForOtp(ui, loader, pkgName) {
|
|
517
|
+
loader.succeed(`\u{1F510} 2FA required for ${pkgName}`);
|
|
518
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
519
|
+
try {
|
|
520
|
+
const entered = await rl.question(" Enter OTP code: ");
|
|
521
|
+
rl.close();
|
|
522
|
+
if (!entered || entered.trim().length !== 6) {
|
|
523
|
+
ui.write?.(" \u26A0\uFE0F Invalid OTP (must be 6 digits)\n");
|
|
524
|
+
return void 0;
|
|
525
|
+
}
|
|
526
|
+
return entered.trim();
|
|
527
|
+
} catch (e) {
|
|
528
|
+
rl.close();
|
|
529
|
+
throw e;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
async function sequentialPublishPkg(pkg, ctx, otpState, ui, logger) {
|
|
533
|
+
const loader = useLoader(`Publishing ${pkg.name}@${pkg.version}...`);
|
|
534
|
+
loader.start();
|
|
535
|
+
const restore = rewriteWorkspaceDeps(pkg.path, ctx.versionMap, ctx.packageManager);
|
|
536
|
+
try {
|
|
537
|
+
const MAX_OTP_ATTEMPTS = 3;
|
|
538
|
+
for (let otpAttempts = 0; otpAttempts < MAX_OTP_ATTEMPTS; otpAttempts++) {
|
|
539
|
+
try {
|
|
540
|
+
await publishSinglePackage({ packagePath: pkg.path, packageManager: ctx.packageManager, otp: otpState.otp, dryRun: ctx.dryRun, tag: ctx.tag, access: ctx.access });
|
|
541
|
+
loader.succeed(ctx.dryRun ? `Dry-run: ${pkg.name}@${pkg.version}` : `Published ${pkg.name}@${pkg.version}`);
|
|
542
|
+
return { name: pkg.name, version: pkg.version, success: true };
|
|
543
|
+
} catch (err) {
|
|
544
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
545
|
+
if (isAlreadyPublished2(msg)) {
|
|
546
|
+
loader.succeed(`${pkg.name}@${pkg.version} already published`);
|
|
547
|
+
return { name: pkg.name, version: pkg.version, success: true, alreadyPublished: true };
|
|
548
|
+
}
|
|
549
|
+
if (msg.includes("EOTP") || msg.includes("one-time password")) {
|
|
550
|
+
if (otpAttempts + 1 < MAX_OTP_ATTEMPTS) {
|
|
551
|
+
otpState.otp = await promptForOtp(ui, loader, pkg.name);
|
|
552
|
+
loader.update({ text: `Publishing ${pkg.name}@${pkg.version} with OTP...` });
|
|
553
|
+
loader.start();
|
|
554
|
+
} else {
|
|
555
|
+
loader.fail("Max OTP attempts reached");
|
|
556
|
+
return { name: pkg.name, version: pkg.version, success: false, error: "Max OTP attempts reached" };
|
|
497
557
|
}
|
|
498
|
-
|
|
558
|
+
} else if (isRetryable2(msg)) {
|
|
559
|
+
const delay = retryDelay2(MAX_RETRIES2 - 1);
|
|
560
|
+
loader.update({ text: `Rate limited \u2014 waiting ${(delay / 1e3).toFixed(0)}s\u2026` });
|
|
561
|
+
loader.start();
|
|
562
|
+
await new Promise((r) => {
|
|
563
|
+
setTimeout(r, delay);
|
|
564
|
+
});
|
|
565
|
+
otpAttempts--;
|
|
566
|
+
} else {
|
|
567
|
+
loader.fail(`Failed: ${msg.split("\n")[0]}`);
|
|
499
568
|
return { name: pkg.name, version: pkg.version, success: false, error: msg };
|
|
500
569
|
}
|
|
501
570
|
}
|
|
502
|
-
return {
|
|
503
|
-
name: pkg.name,
|
|
504
|
-
version: pkg.version,
|
|
505
|
-
success: false,
|
|
506
|
-
error: `Publish failed after ${MAX_RETRIES2} retries (transient errors)`
|
|
507
|
-
};
|
|
508
|
-
} finally {
|
|
509
|
-
restore();
|
|
510
571
|
}
|
|
511
|
-
|
|
572
|
+
return { name: pkg.name, version: pkg.version, success: false, error: "Publish failed after max OTP attempts" };
|
|
573
|
+
} finally {
|
|
574
|
+
restore();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function publishPackagesWithOTP(options) {
|
|
578
|
+
const { packages, packageManager = "pnpm", dryRun, tag, access, ui, logger } = options;
|
|
579
|
+
const otpState = { otp: options.otp };
|
|
580
|
+
const versionMap = new Map(packages.map((p) => [p.name, p.version]));
|
|
581
|
+
const fastCtx = { packageManager, versionMap, otp: otpState.otp, dryRun, tag, access };
|
|
582
|
+
const seqCtx = { packageManager, versionMap, dryRun, tag, access };
|
|
583
|
+
const results = [];
|
|
584
|
+
const remaining = [];
|
|
585
|
+
const CONCURRENCY = Number(useEnv("KB_PUBLISH_CONCURRENCY") ?? 4);
|
|
586
|
+
let otpFailureDetected = false;
|
|
512
587
|
ui.write?.(` Publishing ${packages.length} package(s) in parallel (concurrency=${CONCURRENCY})...
|
|
513
588
|
`);
|
|
514
589
|
for (let i = 0; i < packages.length; i += CONCURRENCY) {
|
|
515
590
|
const batch = packages.slice(i, i + CONCURRENCY);
|
|
516
|
-
const
|
|
591
|
+
const outcomes = await Promise.all(batch.map((pkg) => fastPublishPkg(pkg, fastCtx, logger)));
|
|
517
592
|
for (let j = 0; j < batch.length; j++) {
|
|
518
|
-
const
|
|
519
|
-
if (
|
|
593
|
+
const outcome = outcomes[j];
|
|
594
|
+
if (outcome.result === null) {
|
|
520
595
|
remaining.push(batch[j]);
|
|
521
596
|
} else {
|
|
522
|
-
results.push(
|
|
597
|
+
results.push(outcome.result);
|
|
598
|
+
}
|
|
599
|
+
if (outcome.otpFailed) {
|
|
600
|
+
otpFailureDetected = true;
|
|
523
601
|
}
|
|
524
602
|
}
|
|
525
603
|
if (otpFailureDetected) {
|
|
@@ -534,68 +612,7 @@ async function publishPackagesWithOTP(options) {
|
|
|
534
612
|
`);
|
|
535
613
|
for (const pkg of remaining) {
|
|
536
614
|
logger?.info("Publishing package (sequential)", { name: pkg.name, version: pkg.version });
|
|
537
|
-
|
|
538
|
-
loader.start();
|
|
539
|
-
const restore = rewriteWorkspaceDeps(pkg.path, versionMap, packageManager);
|
|
540
|
-
try {
|
|
541
|
-
let published = false;
|
|
542
|
-
let otpAttempts = 0;
|
|
543
|
-
const MAX_OTP_ATTEMPTS = 3;
|
|
544
|
-
while (!published && otpAttempts < MAX_OTP_ATTEMPTS) {
|
|
545
|
-
otpAttempts++;
|
|
546
|
-
try {
|
|
547
|
-
await publishSinglePackage({ packagePath: pkg.path, packageManager, otp, dryRun, tag, access });
|
|
548
|
-
published = true;
|
|
549
|
-
results.push({ name: pkg.name, version: pkg.version, success: true });
|
|
550
|
-
loader.succeed(dryRun ? `Dry-run: ${pkg.name}@${pkg.version}` : `Published ${pkg.name}@${pkg.version}`);
|
|
551
|
-
} catch (err) {
|
|
552
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
553
|
-
if (isAlreadyPublished2(msg)) {
|
|
554
|
-
published = true;
|
|
555
|
-
results.push({ name: pkg.name, version: pkg.version, success: true, alreadyPublished: true });
|
|
556
|
-
loader.succeed(`${pkg.name}@${pkg.version} already published`);
|
|
557
|
-
break;
|
|
558
|
-
}
|
|
559
|
-
if (msg.includes("EOTP") || msg.includes("one-time password")) {
|
|
560
|
-
if (otpAttempts < MAX_OTP_ATTEMPTS) {
|
|
561
|
-
loader.succeed(`\u{1F510} 2FA required for ${pkg.name}`);
|
|
562
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
563
|
-
try {
|
|
564
|
-
const entered = await rl.question(" Enter OTP code: ");
|
|
565
|
-
rl.close();
|
|
566
|
-
if (!entered || entered.trim().length !== 6) {
|
|
567
|
-
ui.write?.(" \u26A0\uFE0F Invalid OTP (must be 6 digits)\n");
|
|
568
|
-
otp = void 0;
|
|
569
|
-
} else {
|
|
570
|
-
otp = entered.trim();
|
|
571
|
-
}
|
|
572
|
-
} catch (e) {
|
|
573
|
-
rl.close();
|
|
574
|
-
throw e;
|
|
575
|
-
}
|
|
576
|
-
loader.update({ text: `Publishing ${pkg.name}@${pkg.version} with OTP...` });
|
|
577
|
-
loader.start();
|
|
578
|
-
} else {
|
|
579
|
-
loader.fail("Max OTP attempts reached");
|
|
580
|
-
results.push({ name: pkg.name, version: pkg.version, success: false, error: "Max OTP attempts reached" });
|
|
581
|
-
break;
|
|
582
|
-
}
|
|
583
|
-
} else if (isRetryable2(msg)) {
|
|
584
|
-
const delay = retryDelay2(MAX_RETRIES2 - 1);
|
|
585
|
-
loader.update({ text: `Rate limited \u2014 waiting ${(delay / 1e3).toFixed(0)}s\u2026` });
|
|
586
|
-
loader.start();
|
|
587
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
588
|
-
otpAttempts--;
|
|
589
|
-
} else {
|
|
590
|
-
loader.fail(`Failed: ${msg.split("\n")[0]}`);
|
|
591
|
-
results.push({ name: pkg.name, version: pkg.version, success: false, error: msg });
|
|
592
|
-
break;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
} finally {
|
|
597
|
-
restore();
|
|
598
|
-
}
|
|
615
|
+
results.push(await sequentialPublishPkg(pkg, seqCtx, otpState, ui));
|
|
599
616
|
}
|
|
600
617
|
return buildResult(results, packages, dryRun);
|
|
601
618
|
}
|
|
@@ -617,6 +634,173 @@ var BUMP_SYMBOL = {
|
|
|
617
634
|
patch: "\xB7",
|
|
618
635
|
auto: "?"
|
|
619
636
|
};
|
|
637
|
+
function buildPlanRows(packages) {
|
|
638
|
+
return packages.map((pkg) => {
|
|
639
|
+
const bump = pkg.bump ?? "auto";
|
|
640
|
+
const sym = BUMP_SYMBOL[bump] ?? "?";
|
|
641
|
+
return ` ${sym} ${pkg.name.padEnd(40)} ${pkg.currentVersion.padStart(8)} \u2192 ${pkg.nextVersion}`;
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
function createPublisher(config, token, ctx) {
|
|
645
|
+
const packageManager = config.workspace?.type ?? config.publish?.packageManager ?? "pnpm";
|
|
646
|
+
return {
|
|
647
|
+
async publish(packages, opts) {
|
|
648
|
+
if (token) {
|
|
649
|
+
return publishPackagesProgrammatic({
|
|
650
|
+
packages,
|
|
651
|
+
packageManager,
|
|
652
|
+
dryRun: opts.dryRun,
|
|
653
|
+
access: opts.access ?? "public",
|
|
654
|
+
registry: config.registry,
|
|
655
|
+
token
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
return publishPackagesWithOTP({
|
|
659
|
+
packages,
|
|
660
|
+
packageManager,
|
|
661
|
+
dryRun: opts.dryRun,
|
|
662
|
+
access: opts.access ?? "public",
|
|
663
|
+
ui: ctx.ui,
|
|
664
|
+
logger: ctx.platform?.logger
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function appendCheckDetails(c, checkItems) {
|
|
670
|
+
if (c.details?.packagePath) {
|
|
671
|
+
checkItems.push(` package: ${c.details.packagePath}`);
|
|
672
|
+
}
|
|
673
|
+
if (c.details?.error) {
|
|
674
|
+
checkItems.push(` reason: ${c.details.error}`);
|
|
675
|
+
}
|
|
676
|
+
const output = (c.details?.stderr || c.details?.stdout || "").trim();
|
|
677
|
+
if (output) {
|
|
678
|
+
checkItems.push(` output:`);
|
|
679
|
+
for (const line of output.split("\n").slice(0, 8)) {
|
|
680
|
+
checkItems.push(` ${line}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const failedPkgs = (c.packages ?? []).filter((p) => !p.ok);
|
|
684
|
+
if (failedPkgs.length) {
|
|
685
|
+
checkItems.push(` failed in ${failedPkgs.length}/${c.packages.length} package(s):`);
|
|
686
|
+
for (const pkg of failedPkgs.slice(0, 10)) {
|
|
687
|
+
checkItems.push(` - ${pkg.path}${pkg.details?.error ? `: ${pkg.details.error}` : ""}`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
function buildCheckItems(checkEntries, symbols) {
|
|
692
|
+
const checkItems = [];
|
|
693
|
+
for (const c of checkEntries) {
|
|
694
|
+
const sym = c.ok ? symbols.success : c.hint === "optional" ? symbols.warning : symbols.error;
|
|
695
|
+
const timing = c.timingMs ? ` (${(c.timingMs / 1e3).toFixed(1)}s)` : "";
|
|
696
|
+
checkItems.push(`${sym} ${c.id}${timing}`);
|
|
697
|
+
if (!c.ok && c.hint !== "optional") {
|
|
698
|
+
appendCheckDetails(c, checkItems);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return checkItems;
|
|
702
|
+
}
|
|
703
|
+
function buildReleaseSections(report, dryRun, ctx) {
|
|
704
|
+
const sections = [];
|
|
705
|
+
if (!dryRun && report.result.published?.length) {
|
|
706
|
+
sections.push({
|
|
707
|
+
header: "Published",
|
|
708
|
+
items: report.result.published.map((p) => `${ctx.ui.symbols.success} ${p}`)
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (!dryRun && report.result.alreadyPublished?.length) {
|
|
712
|
+
sections.push({
|
|
713
|
+
header: "Already published (skipped)",
|
|
714
|
+
items: report.result.alreadyPublished.map((p) => `${ctx.ui.symbols.info} ${p}`)
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
if (dryRun && report.result.skipped?.length) {
|
|
718
|
+
sections.push({
|
|
719
|
+
header: "Would publish (dry-run)",
|
|
720
|
+
items: report.result.skipped.map((p) => `${ctx.ui.symbols.info} ${p.replace(" (dry-run)", "")}`)
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
if (report.result.checks) {
|
|
724
|
+
const checkEntries = Object.values(report.result.checks).filter(Boolean);
|
|
725
|
+
const passed = checkEntries.filter((c) => c.ok);
|
|
726
|
+
const failed = checkEntries.filter((c) => !c.ok && c.hint !== "optional");
|
|
727
|
+
const skipped = checkEntries.filter((c) => !c.ok && c.hint === "optional");
|
|
728
|
+
if (checkEntries.length > 0) {
|
|
729
|
+
sections.push({
|
|
730
|
+
header: `Checks \u2014 ${passed.length} passed, ${failed.length} failed${skipped.length ? `, ${skipped.length} skipped` : ""}`,
|
|
731
|
+
items: buildCheckItems(checkEntries, ctx.ui.symbols)
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (report.result.errors?.length && !report.result.checks) {
|
|
736
|
+
sections.push({
|
|
737
|
+
header: "Errors",
|
|
738
|
+
items: report.result.errors.map((e) => `${ctx.ui.symbols.error} ${e}`)
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
if (report.result.git) {
|
|
742
|
+
const g = report.result.git;
|
|
743
|
+
sections.push({
|
|
744
|
+
header: "Git",
|
|
745
|
+
items: [
|
|
746
|
+
`Committed: ${g.committed}`,
|
|
747
|
+
`Tags: ${g.tagged?.join(", ") || "none"}`,
|
|
748
|
+
`Pushed: ${g.pushed}`
|
|
749
|
+
]
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
return sections;
|
|
753
|
+
}
|
|
754
|
+
function resolveChecks(flags, config) {
|
|
755
|
+
if (flags.flow) {
|
|
756
|
+
return config.flows?.[flags.flow]?.checks ?? config.checks ?? [];
|
|
757
|
+
}
|
|
758
|
+
if (flags.scope) {
|
|
759
|
+
return config.scopes?.[flags.scope]?.checks ?? config.checks ?? [];
|
|
760
|
+
}
|
|
761
|
+
return config.checks ?? [];
|
|
762
|
+
}
|
|
763
|
+
async function confirmRelease() {
|
|
764
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
765
|
+
let answer;
|
|
766
|
+
try {
|
|
767
|
+
answer = await rl.question("\nProceed with release? [y/N] ");
|
|
768
|
+
} finally {
|
|
769
|
+
rl.close();
|
|
770
|
+
}
|
|
771
|
+
return answer.trim().toLowerCase() === "y";
|
|
772
|
+
}
|
|
773
|
+
function showPlanBox(ctx, flags, plan, config, dryRun) {
|
|
774
|
+
if (flags.json) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
ctx.ui.sideBox({
|
|
778
|
+
title: dryRun ? "Release Plan (dry-run)" : "Release Plan",
|
|
779
|
+
sections: [
|
|
780
|
+
{
|
|
781
|
+
header: `${plan.packages.length} package(s) \xB7 strategy: ${config.versioningStrategy ?? "independent"}${flags.flow ? ` \xB7 flow: ${flags.flow}` : ""}`,
|
|
782
|
+
items: buildPlanRows(plan.packages)
|
|
783
|
+
}
|
|
784
|
+
],
|
|
785
|
+
status: "info"
|
|
786
|
+
});
|
|
787
|
+
if (flags["no-verify"]) {
|
|
788
|
+
ctx.ui.write?.(` \u26A0\uFE0F --no-verify: git pre-push hooks will be skipped
|
|
789
|
+
`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function reportPipelineResult(ctx, flags, result, dryRun) {
|
|
793
|
+
if (flags.json) {
|
|
794
|
+
ctx.ui?.json?.(result.report);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
ctx.ui.sideBox({
|
|
798
|
+
title: "Release",
|
|
799
|
+
sections: buildReleaseSections(result.report, dryRun, ctx),
|
|
800
|
+
status: result.success ? "success" : "error",
|
|
801
|
+
timing: result.report.result.timingMs
|
|
802
|
+
});
|
|
803
|
+
}
|
|
620
804
|
var run_default = defineCommand({
|
|
621
805
|
id: "release:run",
|
|
622
806
|
description: "Execute release process (plan, check, publish)",
|
|
@@ -636,6 +820,37 @@ var run_default = defineCommand({
|
|
|
636
820
|
...flags.strict !== void 0 && { strict: flags.strict }
|
|
637
821
|
};
|
|
638
822
|
configLoader.succeed("Configuration loaded");
|
|
823
|
+
const token = useEnv("NPM_TOKEN") ?? useEnv("NODE_AUTH_TOKEN");
|
|
824
|
+
if (!dryRun) {
|
|
825
|
+
const preErrors = [];
|
|
826
|
+
const registry = config.registry ?? "https://registry.npmjs.org";
|
|
827
|
+
if (!token) {
|
|
828
|
+
preErrors.push("NPM_TOKEN or NODE_AUTH_TOKEN is not set");
|
|
829
|
+
} else {
|
|
830
|
+
try {
|
|
831
|
+
const res = await fetch(`${registry}/-/whoami`, {
|
|
832
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
833
|
+
signal: AbortSignal.timeout(8e3)
|
|
834
|
+
});
|
|
835
|
+
if (!res.ok) {
|
|
836
|
+
preErrors.push(`npm token invalid or expired (HTTP ${res.status} from ${registry})`);
|
|
837
|
+
}
|
|
838
|
+
} catch (e) {
|
|
839
|
+
preErrors.push(`npm registry unreachable: ${e instanceof Error ? e.message : String(e)}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (flags.flow && config.flows && !(flags.flow in config.flows)) {
|
|
843
|
+
preErrors.push(`Flow "${flags.flow}" is not defined in release.flows config`);
|
|
844
|
+
}
|
|
845
|
+
if (preErrors.length > 0) {
|
|
846
|
+
ctx.ui.sideBox({
|
|
847
|
+
title: "Release \u2014 pre-flight failed",
|
|
848
|
+
sections: [{ items: preErrors.map((e) => `${ctx.ui.symbols.error} ${e}`) }],
|
|
849
|
+
status: "error"
|
|
850
|
+
});
|
|
851
|
+
return { exitCode: 1 };
|
|
852
|
+
}
|
|
853
|
+
}
|
|
639
854
|
const scopeCwd = await resolveScopePath(repoRoot, flags.scope || "root");
|
|
640
855
|
const planLoader = useLoader("Discovering packages...");
|
|
641
856
|
planLoader.start();
|
|
@@ -655,36 +870,10 @@ var run_default = defineCommand({
|
|
|
655
870
|
});
|
|
656
871
|
return { exitCode: 0 };
|
|
657
872
|
}
|
|
658
|
-
|
|
659
|
-
const rows = plan.packages.map((pkg) => {
|
|
660
|
-
const bump = pkg.bump ?? "auto";
|
|
661
|
-
const sym = BUMP_SYMBOL[bump] ?? "?";
|
|
662
|
-
return ` ${sym} ${pkg.name.padEnd(40)} ${pkg.currentVersion.padStart(8)} \u2192 ${pkg.nextVersion}`;
|
|
663
|
-
});
|
|
664
|
-
ctx.ui.sideBox({
|
|
665
|
-
title: dryRun ? "Release Plan (dry-run)" : "Release Plan",
|
|
666
|
-
sections: [
|
|
667
|
-
{
|
|
668
|
-
header: `${plan.packages.length} package(s) \xB7 strategy: ${config.versioningStrategy ?? "independent"}${flags.flow ? ` \xB7 flow: ${flags.flow}` : ""}`,
|
|
669
|
-
items: rows
|
|
670
|
-
}
|
|
671
|
-
],
|
|
672
|
-
status: "info"
|
|
673
|
-
});
|
|
674
|
-
if (flags["no-verify"]) {
|
|
675
|
-
ctx.ui.write?.(` \u26A0\uFE0F --no-verify: git pre-push hooks will be skipped
|
|
676
|
-
`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
873
|
+
showPlanBox(ctx, flags, plan, config, dryRun);
|
|
679
874
|
if (!skipYes) {
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
try {
|
|
683
|
-
answer = await rl.question("\nProceed with release? [y/N] ");
|
|
684
|
-
} finally {
|
|
685
|
-
rl.close();
|
|
686
|
-
}
|
|
687
|
-
if (answer.trim().toLowerCase() !== "y") {
|
|
875
|
+
const confirmed = await confirmRelease();
|
|
876
|
+
if (!confirmed) {
|
|
688
877
|
ctx.ui.sideBox({
|
|
689
878
|
title: "Release",
|
|
690
879
|
sections: [{ items: [`${ctx.ui.symbols.info} Cancelled`] }],
|
|
@@ -695,23 +884,7 @@ var run_default = defineCommand({
|
|
|
695
884
|
}
|
|
696
885
|
const llm = useLLM();
|
|
697
886
|
const changelog = createChangelogGenerator(config, llm ?? void 0);
|
|
698
|
-
const
|
|
699
|
-
const packageManager = config.workspace?.type ?? config.publish?.packageManager ?? "pnpm";
|
|
700
|
-
const publisher = {
|
|
701
|
-
async publish(packages, opts) {
|
|
702
|
-
if (token) {
|
|
703
|
-
return publishPackagesProgrammatic({ packages, packageManager, dryRun: opts.dryRun });
|
|
704
|
-
}
|
|
705
|
-
return publishPackagesWithOTP({
|
|
706
|
-
packages,
|
|
707
|
-
packageManager,
|
|
708
|
-
dryRun: opts.dryRun,
|
|
709
|
-
access: opts.access ?? "public",
|
|
710
|
-
ui: ctx.ui,
|
|
711
|
-
logger: ctx.platform?.logger
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
};
|
|
887
|
+
const publisher = createPublisher(config, token, ctx);
|
|
715
888
|
const pipelineLoader = useLoader("Running release pipeline...");
|
|
716
889
|
pipelineLoader.start();
|
|
717
890
|
const pipelineStart = Date.now();
|
|
@@ -727,7 +900,7 @@ var run_default = defineCommand({
|
|
|
727
900
|
skipBuild: flags["skip-build"],
|
|
728
901
|
skipVerify: flags["skip-verify"],
|
|
729
902
|
noVerify: flags["no-verify"],
|
|
730
|
-
checks: (flags
|
|
903
|
+
checks: resolveChecks(flags, config),
|
|
731
904
|
publisher,
|
|
732
905
|
changelog,
|
|
733
906
|
logger: ctx.platform?.logger,
|
|
@@ -737,94 +910,7 @@ var run_default = defineCommand({
|
|
|
737
910
|
}
|
|
738
911
|
});
|
|
739
912
|
pipelineLoader.succeed(result.success ? "Release completed" : "Release failed");
|
|
740
|
-
|
|
741
|
-
ctx.ui?.json?.(result.report);
|
|
742
|
-
} else {
|
|
743
|
-
const report = result.report;
|
|
744
|
-
const sections = [];
|
|
745
|
-
if (!dryRun && report.result.published?.length) {
|
|
746
|
-
sections.push({
|
|
747
|
-
header: "Published",
|
|
748
|
-
items: report.result.published.map((p) => `${ctx.ui.symbols.success} ${p}`)
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
if (!dryRun && report.result.alreadyPublished?.length) {
|
|
752
|
-
sections.push({
|
|
753
|
-
header: "Already published (skipped)",
|
|
754
|
-
items: report.result.alreadyPublished.map((p) => `${ctx.ui.symbols.info} ${p}`)
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
if (dryRun && report.result.skipped?.length) {
|
|
758
|
-
sections.push({
|
|
759
|
-
header: "Would publish (dry-run)",
|
|
760
|
-
items: report.result.skipped.map((p) => `${ctx.ui.symbols.info} ${p.replace(" (dry-run)", "")}`)
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
if (report.result.checks) {
|
|
764
|
-
const checkEntries = Object.values(report.result.checks).filter(Boolean);
|
|
765
|
-
const passed = checkEntries.filter((c) => c.ok);
|
|
766
|
-
const failed = checkEntries.filter((c) => !c.ok && c.hint !== "optional");
|
|
767
|
-
const skipped = checkEntries.filter((c) => !c.ok && c.hint === "optional");
|
|
768
|
-
if (checkEntries.length > 0) {
|
|
769
|
-
const checkItems = [];
|
|
770
|
-
for (const c of checkEntries) {
|
|
771
|
-
const sym = c.ok ? ctx.ui.symbols.success : c.hint === "optional" ? ctx.ui.symbols.warning : ctx.ui.symbols.error;
|
|
772
|
-
const timing = c.timingMs ? ` (${(c.timingMs / 1e3).toFixed(1)}s)` : "";
|
|
773
|
-
checkItems.push(`${sym} ${c.id}${timing}`);
|
|
774
|
-
if (!c.ok && c.hint !== "optional") {
|
|
775
|
-
if (c.details?.packagePath) {
|
|
776
|
-
checkItems.push(` package: ${c.details.packagePath}`);
|
|
777
|
-
}
|
|
778
|
-
if (c.details?.error) {
|
|
779
|
-
checkItems.push(` reason: ${c.details.error}`);
|
|
780
|
-
}
|
|
781
|
-
const output = (c.details?.stderr || c.details?.stdout || "").trim();
|
|
782
|
-
if (output) {
|
|
783
|
-
const lines = output.split("\n").slice(0, 8);
|
|
784
|
-
checkItems.push(` output:`);
|
|
785
|
-
for (const line of lines) {
|
|
786
|
-
checkItems.push(` ${line}`);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
if (c.packages?.filter((p) => !p.ok).length) {
|
|
790
|
-
const failedPkgs = c.packages.filter((p) => !p.ok);
|
|
791
|
-
checkItems.push(` failed in ${failedPkgs.length}/${c.packages.length} package(s):`);
|
|
792
|
-
for (const pkg of failedPkgs.slice(0, 10)) {
|
|
793
|
-
checkItems.push(` - ${pkg.path}${pkg.details?.error ? `: ${pkg.details.error}` : ""}`);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
sections.push({
|
|
799
|
-
header: `Checks \u2014 ${passed.length} passed, ${failed.length} failed${skipped.length ? `, ${skipped.length} skipped` : ""}`,
|
|
800
|
-
items: checkItems
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
if (report.result.errors?.length && !report.result.checks) {
|
|
805
|
-
sections.push({
|
|
806
|
-
header: "Errors",
|
|
807
|
-
items: report.result.errors.map((e) => `${ctx.ui.symbols.error} ${e}`)
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
if (report.result.git) {
|
|
811
|
-
const g = report.result.git;
|
|
812
|
-
sections.push({
|
|
813
|
-
header: "Git",
|
|
814
|
-
items: [
|
|
815
|
-
`Committed: ${g.committed}`,
|
|
816
|
-
`Tags: ${g.tagged?.join(", ") || "none"}`,
|
|
817
|
-
`Pushed: ${g.pushed}`
|
|
818
|
-
]
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
ctx.ui.sideBox({
|
|
822
|
-
title: "Release",
|
|
823
|
-
sections,
|
|
824
|
-
status: result.success ? "success" : "error",
|
|
825
|
-
timing: report.result.timingMs
|
|
826
|
-
});
|
|
827
|
-
}
|
|
913
|
+
reportPipelineResult(ctx, flags, result, dryRun);
|
|
828
914
|
return { exitCode: result.success ? 0 : 1, report: result.report };
|
|
829
915
|
}
|
|
830
916
|
}
|