@kb-labs/release-manager-cli 0.5.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/README.md +211 -0
- package/dist/cli/commands/changelog.d.ts +17 -0
- package/dist/cli/commands/changelog.js +156 -0
- package/dist/cli/commands/changelog.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +12 -0
- package/dist/cli/commands/plan.js +117 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/publish.d.ts +15 -0
- package/dist/cli/commands/publish.js +486 -0
- package/dist/cli/commands/publish.js.map +1 -0
- package/dist/cli/commands/report.d.ts +9 -0
- package/dist/cli/commands/report.js +77 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/rollback.d.ts +9 -0
- package/dist/cli/commands/rollback.js +42 -0
- package/dist/cli/commands/rollback.js.map +1 -0
- package/dist/cli/commands/run.d.ts +16 -0
- package/dist/cli/commands/run.js +539 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +14 -0
- package/dist/cli/commands/verify.js +106 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/contracts/release.schema.d.ts +503 -0
- package/dist/contracts/release.schema.js +45 -0
- package/dist/contracts/release.schema.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +689 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +292 -0
- package/dist/manifest.js +604 -0
- package/dist/manifest.js.map +1 -0
- package/dist/rest/handlers/build-handler.d.ts +20 -0
- package/dist/rest/handlers/build-handler.js +68 -0
- package/dist/rest/handlers/build-handler.js.map +1 -0
- package/dist/rest/handlers/changelog-generate-handler.d.ts +20 -0
- package/dist/rest/handlers/changelog-generate-handler.js +169 -0
- package/dist/rest/handlers/changelog-generate-handler.js.map +1 -0
- package/dist/rest/handlers/changelog-handler.d.ts +16 -0
- package/dist/rest/handlers/changelog-handler.js +50 -0
- package/dist/rest/handlers/changelog-handler.js.map +1 -0
- package/dist/rest/handlers/changelog-save-handler.d.ts +13 -0
- package/dist/rest/handlers/changelog-save-handler.js +42 -0
- package/dist/rest/handlers/changelog-save-handler.js.map +1 -0
- package/dist/rest/handlers/checklist-handler.d.ts +39 -0
- package/dist/rest/handlers/checklist-handler.js +155 -0
- package/dist/rest/handlers/checklist-handler.js.map +1 -0
- package/dist/rest/handlers/generate-handler.d.ts +35 -0
- package/dist/rest/handlers/generate-handler.js +206 -0
- package/dist/rest/handlers/generate-handler.js.map +1 -0
- package/dist/rest/handlers/get-checks-handler.d.ts +15 -0
- package/dist/rest/handlers/get-checks-handler.js +22 -0
- package/dist/rest/handlers/get-checks-handler.js.map +1 -0
- package/dist/rest/handlers/git-timeline-handler.d.ts +26 -0
- package/dist/rest/handlers/git-timeline-handler.js +209 -0
- package/dist/rest/handlers/git-timeline-handler.js.map +1 -0
- package/dist/rest/handlers/history-changelog-handler.d.ts +14 -0
- package/dist/rest/handlers/history-changelog-handler.js +34 -0
- package/dist/rest/handlers/history-changelog-handler.js.map +1 -0
- package/dist/rest/handlers/history-handler.d.ts +20 -0
- package/dist/rest/handlers/history-handler.js +75 -0
- package/dist/rest/handlers/history-handler.js.map +1 -0
- package/dist/rest/handlers/history-plan-handler.d.ts +30 -0
- package/dist/rest/handlers/history-plan-handler.js +34 -0
- package/dist/rest/handlers/history-plan-handler.js.map +1 -0
- package/dist/rest/handlers/history-report-handler.d.ts +64 -0
- package/dist/rest/handlers/history-report-handler.js +34 -0
- package/dist/rest/handlers/history-report-handler.js.map +1 -0
- package/dist/rest/handlers/plan-handler.d.ts +29 -0
- package/dist/rest/handlers/plan-handler.js +75 -0
- package/dist/rest/handlers/plan-handler.js.map +1 -0
- package/dist/rest/handlers/preview-handler.d.ts +26 -0
- package/dist/rest/handlers/preview-handler.js +127 -0
- package/dist/rest/handlers/preview-handler.js.map +1 -0
- package/dist/rest/handlers/report-handler.d.ts +65 -0
- package/dist/rest/handlers/report-handler.js +48 -0
- package/dist/rest/handlers/report-handler.js.map +1 -0
- package/dist/rest/handlers/reset-handler.d.ts +12 -0
- package/dist/rest/handlers/reset-handler.js +38 -0
- package/dist/rest/handlers/reset-handler.js.map +1 -0
- package/dist/rest/handlers/run-checks-handler.d.ts +20 -0
- package/dist/rest/handlers/run-checks-handler.js +58 -0
- package/dist/rest/handlers/run-checks-handler.js.map +1 -0
- package/dist/rest/handlers/run-handler.d.ts +68 -0
- package/dist/rest/handlers/run-handler.js +274 -0
- package/dist/rest/handlers/run-handler.js.map +1 -0
- package/dist/rest/handlers/scopes-handler.d.ts +20 -0
- package/dist/rest/handlers/scopes-handler.js +63 -0
- package/dist/rest/handlers/scopes-handler.js.map +1 -0
- package/dist/rest/handlers/status-handler.d.ts +24 -0
- package/dist/rest/handlers/status-handler.js +120 -0
- package/dist/rest/handlers/status-handler.js.map +1 -0
- package/dist/setup/handler.d.ts +19 -0
- package/dist/setup/handler.js +103 -0
- package/dist/setup/handler.js.map +1 -0
- package/dist/widgets/220.js +446 -0
- package/dist/widgets/220.js.map +1 -0
- package/dist/widgets/331.js +2 -0
- package/dist/widgets/331.js.map +1 -0
- package/dist/widgets/403.js +2 -0
- package/dist/widgets/403.js.map +1 -0
- package/dist/widgets/406.js +35 -0
- package/dist/widgets/406.js.map +1 -0
- package/dist/widgets/455.js +2 -0
- package/dist/widgets/455.js.map +1 -0
- package/dist/widgets/482.js +2 -0
- package/dist/widgets/482.js.map +1 -0
- package/dist/widgets/485.js +2 -0
- package/dist/widgets/485.js.map +1 -0
- package/dist/widgets/527.js +2 -0
- package/dist/widgets/527.js.map +1 -0
- package/dist/widgets/628.js +2 -0
- package/dist/widgets/628.js.map +1 -0
- package/dist/widgets/694.js +2 -0
- package/dist/widgets/694.js.map +1 -0
- package/dist/widgets/712.js +2 -0
- package/dist/widgets/712.js.map +1 -0
- package/dist/widgets/866.js +2 -0
- package/dist/widgets/866.js.map +1 -0
- package/dist/widgets/915.js +39 -0
- package/dist/widgets/915.js.map +1 -0
- package/dist/widgets/957.js +10 -0
- package/dist/widgets/957.js.map +1 -0
- package/dist/widgets/983.js +2 -0
- package/dist/widgets/983.js.map +1 -0
- package/dist/widgets/@mf-types.d.ts +3 -0
- package/dist/widgets/@mf-types.zip +0 -0
- package/dist/widgets/__federation_expose_ReleasePage.js +2 -0
- package/dist/widgets/__federation_expose_ReleasePage.js.map +1 -0
- package/dist/widgets/mf-manifest.json +260 -0
- package/dist/widgets/mf-stats.json +302 -0
- package/dist/widgets/remoteEntry.js +7 -0
- package/dist/widgets/remoteEntry.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { defineHandler, findRepoRoot, useConfig, useLLM, useLogger } from '@kb-labs/sdk';
|
|
2
|
+
import { resolveScopePath, runReleasePipeline } from '@kb-labs/release-manager-core';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { readFileSync, readdirSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { generateChangelog, generateSimpleChangelog } from '@kb-labs/release-manager-changelog';
|
|
7
|
+
|
|
8
|
+
// src/rest/handlers/run-handler.ts
|
|
9
|
+
function resolveToken(token) {
|
|
10
|
+
return token ?? process.env.NPM_TOKEN ?? process.env.NODE_AUTH_TOKEN;
|
|
11
|
+
}
|
|
12
|
+
function publishSinglePackage(options) {
|
|
13
|
+
const { packagePath, token, otp, dryRun, tag, access, registry } = options;
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const args = ["publish"];
|
|
16
|
+
if (dryRun) {
|
|
17
|
+
args.push("--dry-run");
|
|
18
|
+
}
|
|
19
|
+
if (tag) {
|
|
20
|
+
args.push(`--tag=${tag}`);
|
|
21
|
+
}
|
|
22
|
+
if (access) {
|
|
23
|
+
args.push(`--access=${access}`);
|
|
24
|
+
}
|
|
25
|
+
if (registry) {
|
|
26
|
+
args.push(`--registry=${registry}`);
|
|
27
|
+
}
|
|
28
|
+
if (otp) {
|
|
29
|
+
args.push(`--otp=${otp}`);
|
|
30
|
+
}
|
|
31
|
+
const env = { ...process.env };
|
|
32
|
+
if (token) {
|
|
33
|
+
env["NODE_AUTH_TOKEN"] = token;
|
|
34
|
+
}
|
|
35
|
+
const child = spawn("npm", args, {
|
|
36
|
+
cwd: packagePath,
|
|
37
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
38
|
+
shell: true,
|
|
39
|
+
env
|
|
40
|
+
});
|
|
41
|
+
let stdout = "";
|
|
42
|
+
let stderr = "";
|
|
43
|
+
child.stdout?.on("data", (data) => {
|
|
44
|
+
stdout += data.toString();
|
|
45
|
+
});
|
|
46
|
+
child.stderr?.on("data", (data) => {
|
|
47
|
+
stderr += data.toString();
|
|
48
|
+
});
|
|
49
|
+
child.on("close", (code) => {
|
|
50
|
+
if (code === 0) {
|
|
51
|
+
resolve();
|
|
52
|
+
} else {
|
|
53
|
+
reject(new Error(stderr || stdout || `npm publish exited with code ${code}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
child.on("error", (err) => {
|
|
57
|
+
reject(err);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async function publishPackagesProgrammatic(options) {
|
|
62
|
+
const { packages, dryRun, otp, tag, access, registry } = options;
|
|
63
|
+
const logger = useLogger();
|
|
64
|
+
const token = resolveToken(options.token);
|
|
65
|
+
if (!token && !dryRun) {
|
|
66
|
+
const error = "No npm token found. Set NPM_TOKEN (or NODE_AUTH_TOKEN) in environment.";
|
|
67
|
+
logger.error(error);
|
|
68
|
+
return {
|
|
69
|
+
results: [],
|
|
70
|
+
published: [],
|
|
71
|
+
failed: packages.map((p) => `${p.name}@${p.version}`),
|
|
72
|
+
skipped: [],
|
|
73
|
+
errors: [error]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const results = [];
|
|
77
|
+
const versionMap = new Map(packages.map((p) => [p.name, p.version]));
|
|
78
|
+
for (const pkg of packages) {
|
|
79
|
+
logger.info(`Publishing ${pkg.name}@${pkg.version}`, { path: pkg.path, dryRun });
|
|
80
|
+
const pkgJsonPath = join(pkg.path, "package.json");
|
|
81
|
+
const originalPkgJson = readFileSync(pkgJsonPath, "utf-8");
|
|
82
|
+
let restored = false;
|
|
83
|
+
try {
|
|
84
|
+
const pkgJson = JSON.parse(originalPkgJson);
|
|
85
|
+
let modified = false;
|
|
86
|
+
for (const section of ["dependencies", "peerDependencies"]) {
|
|
87
|
+
const deps = pkgJson[section];
|
|
88
|
+
if (!deps) continue;
|
|
89
|
+
for (const [depName, depValue] of Object.entries(deps)) {
|
|
90
|
+
if (typeof depValue !== "string") continue;
|
|
91
|
+
const val = depValue;
|
|
92
|
+
if (val.startsWith("link:")) {
|
|
93
|
+
const planVersion = versionMap.get(depName);
|
|
94
|
+
if (planVersion) {
|
|
95
|
+
deps[depName] = `^${planVersion}`;
|
|
96
|
+
modified = true;
|
|
97
|
+
} else {
|
|
98
|
+
try {
|
|
99
|
+
const linkPath = val.replace("link:", "");
|
|
100
|
+
const linkedPkg = JSON.parse(readFileSync(join(pkg.path, linkPath, "package.json"), "utf-8"));
|
|
101
|
+
deps[depName] = `^${linkedPkg.version}`;
|
|
102
|
+
modified = true;
|
|
103
|
+
} catch {
|
|
104
|
+
deps[depName] = "*";
|
|
105
|
+
modified = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} else if (val.startsWith("workspace:")) {
|
|
109
|
+
const planVersion = versionMap.get(depName);
|
|
110
|
+
if (planVersion) {
|
|
111
|
+
deps[depName] = val === "workspace:*" ? `^${planVersion}` : val.replace("workspace:", "");
|
|
112
|
+
modified = true;
|
|
113
|
+
} else {
|
|
114
|
+
try {
|
|
115
|
+
const repoRoot = join(pkg.path, "..");
|
|
116
|
+
const candidates = readdirSync(repoRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join(repoRoot, d.name, "package.json"));
|
|
117
|
+
for (const candidate of candidates) {
|
|
118
|
+
try {
|
|
119
|
+
const cPkg = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
120
|
+
if (cPkg.name === depName) {
|
|
121
|
+
deps[depName] = `^${cPkg.version}`;
|
|
122
|
+
modified = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
deps[depName] = "*";
|
|
130
|
+
modified = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (modified) {
|
|
137
|
+
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + "\n");
|
|
138
|
+
logger.info(`Replaced link: deps for ${pkg.name}`);
|
|
139
|
+
}
|
|
140
|
+
await publishSinglePackage({
|
|
141
|
+
packagePath: pkg.path,
|
|
142
|
+
token,
|
|
143
|
+
otp,
|
|
144
|
+
dryRun,
|
|
145
|
+
tag,
|
|
146
|
+
access: access ?? "public",
|
|
147
|
+
registry
|
|
148
|
+
});
|
|
149
|
+
results.push({ name: pkg.name, version: pkg.version, success: true });
|
|
150
|
+
logger.info(`Published ${pkg.name}@${pkg.version}`);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
153
|
+
logger.error(`Failed to publish ${pkg.name}@${pkg.version}`, void 0, { error: message });
|
|
154
|
+
results.push({
|
|
155
|
+
name: pkg.name,
|
|
156
|
+
version: pkg.version,
|
|
157
|
+
success: false,
|
|
158
|
+
error: message
|
|
159
|
+
});
|
|
160
|
+
} finally {
|
|
161
|
+
if (!restored) {
|
|
162
|
+
writeFileSync(pkgJsonPath, originalPkgJson);
|
|
163
|
+
restored = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const published = results.filter((r) => r.success).map((r) => `${r.name}@${r.version}`);
|
|
168
|
+
const failed = results.filter((r) => !r.success).map((r) => `${r.name}@${r.version}`);
|
|
169
|
+
const errors = results.filter((r) => !r.success && r.error).map((r) => `${r.name}: ${r.error}`);
|
|
170
|
+
return {
|
|
171
|
+
results,
|
|
172
|
+
published,
|
|
173
|
+
failed,
|
|
174
|
+
skipped: dryRun ? packages.map((p) => `${p.name}@${p.version} (dry-run)`) : [],
|
|
175
|
+
errors
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function createChangelogGenerator(config, llm) {
|
|
179
|
+
return {
|
|
180
|
+
async generate(plan, opts) {
|
|
181
|
+
const locale = config.changelog?.locale || "en";
|
|
182
|
+
const packages = plan.packages.map((pkg) => ({
|
|
183
|
+
name: pkg.name,
|
|
184
|
+
path: pkg.path,
|
|
185
|
+
currentVersion: pkg.currentVersion,
|
|
186
|
+
nextVersion: pkg.nextVersion,
|
|
187
|
+
bump: pkg.bump === "auto" ? "patch" : pkg.bump
|
|
188
|
+
}));
|
|
189
|
+
try {
|
|
190
|
+
const result = await generateChangelog({
|
|
191
|
+
repoRoot: opts.repoRoot,
|
|
192
|
+
gitCwd: opts.gitCwd,
|
|
193
|
+
packages,
|
|
194
|
+
range: { to: "HEAD" },
|
|
195
|
+
changelog: {
|
|
196
|
+
template: config.changelog?.template ?? void 0,
|
|
197
|
+
locale,
|
|
198
|
+
metadata: config.changelog?.metadata,
|
|
199
|
+
ignoreAuthors: config.changelog?.ignoreAuthors,
|
|
200
|
+
includeTypes: config.changelog?.includeTypes,
|
|
201
|
+
excludeTypes: config.changelog?.excludeTypes,
|
|
202
|
+
collapseMerges: config.changelog?.collapseMerges,
|
|
203
|
+
collapseReverts: config.changelog?.collapseReverts,
|
|
204
|
+
preferMergeSummary: config.changelog?.preferMergeSummary
|
|
205
|
+
},
|
|
206
|
+
git: {
|
|
207
|
+
autoUnshallow: config.git?.autoUnshallow,
|
|
208
|
+
requireSignedTags: config.git?.requireSignedTags,
|
|
209
|
+
baseUrl: config.git?.baseUrl ?? void 0
|
|
210
|
+
},
|
|
211
|
+
platform: llm ? { llm } : void 0
|
|
212
|
+
});
|
|
213
|
+
return result.markdown;
|
|
214
|
+
} catch {
|
|
215
|
+
return generateSimpleChangelog(packages, locale);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/rest/handlers/run-handler.ts
|
|
222
|
+
var run_handler_default = defineHandler({
|
|
223
|
+
async execute(ctx, input) {
|
|
224
|
+
const scope = input.body?.scope || "root";
|
|
225
|
+
const dryRun = input.body?.dryRun ?? false;
|
|
226
|
+
const skipChecks = input.body?.skipChecks ?? false;
|
|
227
|
+
const otp = input.body?.otp;
|
|
228
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
229
|
+
const repoRoot = await findRepoRoot(cwd);
|
|
230
|
+
const scopeCwd = await resolveScopePath(repoRoot, scope);
|
|
231
|
+
const config = await useConfig() ?? {};
|
|
232
|
+
const llm = useLLM();
|
|
233
|
+
const changelog = createChangelogGenerator(config, llm ?? void 0);
|
|
234
|
+
const publisher = {
|
|
235
|
+
async publish(packages, opts) {
|
|
236
|
+
return publishPackagesProgrammatic({
|
|
237
|
+
packages,
|
|
238
|
+
dryRun: opts.dryRun,
|
|
239
|
+
otp
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const result = await runReleasePipeline({
|
|
244
|
+
cwd,
|
|
245
|
+
repoRoot,
|
|
246
|
+
scopeCwd,
|
|
247
|
+
scope,
|
|
248
|
+
config,
|
|
249
|
+
dryRun,
|
|
250
|
+
skipChecks,
|
|
251
|
+
checks: config.scopes?.[scope]?.checks ?? config.checks ?? [],
|
|
252
|
+
publisher,
|
|
253
|
+
changelog,
|
|
254
|
+
logger: ctx.platform?.logger
|
|
255
|
+
});
|
|
256
|
+
await ctx.platform?.analytics?.track?.(result.success ? "release.completed" : "release.failed", {
|
|
257
|
+
scope,
|
|
258
|
+
packagesCount: result.plan.packages.length,
|
|
259
|
+
publishedCount: result.report.result.published?.length ?? 0,
|
|
260
|
+
dryRun,
|
|
261
|
+
durationMs: result.report.result.timingMs
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
scope,
|
|
265
|
+
report: result.report,
|
|
266
|
+
success: result.success,
|
|
267
|
+
errors: result.report.result.errors
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
export { run_handler_default as default };
|
|
273
|
+
//# sourceMappingURL=run-handler.js.map
|
|
274
|
+
//# sourceMappingURL=run-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/shared/publish-programmatic.ts","../../../src/shared/changelog-factory.ts","../../../src/rest/handlers/run-handler.ts"],"names":[],"mappings":"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","file":"run-handler.js","sourcesContent":["/**\n * Programmatic npm publishing for REST handlers (non-interactive context)\n *\n * Uses `npm publish` CLI via spawn with NODE_AUTH_TOKEN env variable.\n * This is the correct approach for granular access tokens (classic tokens\n * were revoked by npm in December 2025).\n *\n * Token resolution order:\n * 1. options.token (explicit override)\n * 2. NPM_TOKEN env variable\n * 3. NODE_AUTH_TOKEN env variable\n */\n\nimport { spawn } from 'node:child_process';\nimport { readFileSync, writeFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { useLogger } from '@kb-labs/sdk';\n\nexport interface PackageToPublish {\n name: string;\n version: string;\n path: string;\n}\n\nexport interface ProgrammaticPublishOptions {\n packages: PackageToPublish[];\n dryRun?: boolean;\n otp?: string;\n tag?: string;\n access?: 'public' | 'restricted';\n registry?: string;\n token?: string;\n}\n\nexport interface PublishResult {\n name: string;\n version: string;\n success: boolean;\n error?: string;\n}\n\nexport interface ProgrammaticPublishResult {\n results: PublishResult[];\n published: string[];\n failed: string[];\n skipped: string[];\n errors: string[];\n}\n\n/**\n * Resolve npm auth token from options or environment\n */\nfunction resolveToken(token?: string): string | undefined {\n return token ?? process.env.NPM_TOKEN ?? process.env.NODE_AUTH_TOKEN;\n}\n\n/**\n * Publish a single package using npm CLI\n * Passes auth token via NODE_AUTH_TOKEN env (recommended for granular tokens)\n */\nfunction publishSinglePackage(options: {\n packagePath: string;\n token: string | undefined;\n otp?: string;\n dryRun?: boolean;\n tag?: string;\n access?: string;\n registry?: string;\n}): Promise<void> {\n const { packagePath, token, otp, dryRun, tag, access, registry } = options;\n\n return new Promise((resolve, reject) => {\n const args = ['publish'];\n\n if (dryRun) {\n args.push('--dry-run');\n }\n\n if (tag) {\n args.push(`--tag=${tag}`);\n }\n\n if (access) {\n args.push(`--access=${access}`);\n }\n\n if (registry) {\n args.push(`--registry=${registry}`);\n }\n\n if (otp) {\n args.push(`--otp=${otp}`);\n }\n\n // Pass token via env — this is the correct way for granular tokens\n const env: NodeJS.ProcessEnv = { ...process.env };\n if (token) {\n env['NODE_AUTH_TOKEN'] = token;\n }\n\n const child = spawn('npm', args, {\n cwd: packagePath,\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: true,\n env,\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n\n child.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on('close', (code: number | null) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(stderr || stdout || `npm publish exited with code ${code}`));\n }\n });\n\n child.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n\n/**\n * Publish packages programmatically (non-interactive, for REST handlers)\n * Uses npm CLI with NODE_AUTH_TOKEN — works with granular access tokens.\n */\nexport async function publishPackagesProgrammatic(\n options: ProgrammaticPublishOptions\n): Promise<ProgrammaticPublishResult> {\n const { packages, dryRun, otp, tag, access, registry } = options;\n const logger = useLogger();\n\n const token = resolveToken(options.token);\n\n if (!token && !dryRun) {\n const error = 'No npm token found. Set NPM_TOKEN (or NODE_AUTH_TOKEN) in environment.';\n logger.error(error);\n return {\n results: [],\n published: [],\n failed: packages.map((p) => `${p.name}@${p.version}`),\n skipped: [],\n errors: [error],\n };\n }\n\n const results: PublishResult[] = [];\n\n // Build version map from all packages in this release for link: → ^version replacement\n const versionMap = new Map(packages.map(p => [p.name, p.version]));\n\n for (const pkg of packages) {\n logger.info(`Publishing ${pkg.name}@${pkg.version}`, { path: pkg.path, dryRun });\n\n // Replace link: deps with real versions before publish, restore after\n const pkgJsonPath = join(pkg.path, 'package.json');\n const originalPkgJson = readFileSync(pkgJsonPath, 'utf-8');\n let restored = false;\n\n try {\n const pkgJson = JSON.parse(originalPkgJson);\n let modified = false;\n\n for (const section of ['dependencies', 'peerDependencies'] as const) {\n const deps = pkgJson[section];\n if (!deps) continue;\n for (const [depName, depValue] of Object.entries(deps)) {\n if (typeof depValue !== 'string') continue;\n const val = depValue as string;\n\n if (val.startsWith('link:')) {\n // Cross-repo link: → ^version from plan or linked package.json\n const planVersion = versionMap.get(depName);\n if (planVersion) {\n deps[depName] = `^${planVersion}`;\n modified = true;\n } else {\n try {\n const linkPath = val.replace('link:', '');\n const linkedPkg = JSON.parse(readFileSync(join(pkg.path, linkPath, 'package.json'), 'utf-8'));\n deps[depName] = `^${linkedPkg.version}`;\n modified = true;\n } catch {\n deps[depName] = '*';\n modified = true;\n }\n }\n } else if (val.startsWith('workspace:')) {\n // Intra-repo workspace:* → ^version (pnpm does this automatically, but npm doesn't)\n const planVersion = versionMap.get(depName);\n if (planVersion) {\n deps[depName] = val === 'workspace:*' ? `^${planVersion}` : val.replace('workspace:', '');\n modified = true;\n } else {\n // Not in plan — resolve from current version in monorepo\n try {\n // Find the package in the same repo by scanning packages/*/package.json\n const repoRoot = join(pkg.path, '..');\n const candidates = readdirSync(repoRoot, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => join(repoRoot, d.name, 'package.json'));\n for (const candidate of candidates) {\n try {\n const cPkg = JSON.parse(readFileSync(candidate, 'utf-8'));\n if (cPkg.name === depName) {\n deps[depName] = `^${cPkg.version}`;\n modified = true;\n break;\n }\n } catch { /* skip */ }\n }\n } catch {\n deps[depName] = '*';\n modified = true;\n }\n }\n }\n }\n }\n\n if (modified) {\n writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\\n');\n logger.info(`Replaced link: deps for ${pkg.name}`);\n }\n\n await publishSinglePackage({\n packagePath: pkg.path,\n token,\n otp,\n dryRun,\n tag,\n access: access ?? 'public',\n registry,\n });\n\n results.push({ name: pkg.name, version: pkg.version, success: true });\n logger.info(`Published ${pkg.name}@${pkg.version}`);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n\n logger.error(`Failed to publish ${pkg.name}@${pkg.version}`, undefined, { error: message });\n\n results.push({\n name: pkg.name,\n version: pkg.version,\n success: false,\n error: message,\n });\n } finally {\n // Always restore original package.json\n if (!restored) {\n writeFileSync(pkgJsonPath, originalPkgJson);\n restored = true;\n }\n }\n }\n\n const published = results.filter((r) => r.success).map((r) => `${r.name}@${r.version}`);\n const failed = results.filter((r) => !r.success).map((r) => `${r.name}@${r.version}`);\n const errors = results.filter((r) => !r.success && r.error).map((r) => `${r.name}: ${r.error}`);\n\n return {\n results,\n published,\n failed,\n skipped: dryRun ? packages.map((p) => `${p.name}@${p.version} (dry-run)`) : [],\n errors,\n };\n}\n","/**\n * Shared changelog generator factory.\n * Used by both CLI (run.ts) and REST (run-handler.ts) to avoid duplication.\n */\n\nimport {\n generateChangelog,\n generateSimpleChangelog,\n type ChangelogPackageInfo,\n} from '@kb-labs/release-manager-changelog';\nimport type { ReleaseConfig, ChangelogGenerator } from '@kb-labs/release-manager-core';\n\nexport function createChangelogGenerator(config: ReleaseConfig, llm?: any): ChangelogGenerator {\n return {\n async generate(plan, opts) {\n const locale = (config.changelog?.locale as 'en' | 'ru') || 'en';\n const packages: ChangelogPackageInfo[] = plan.packages.map(pkg => ({\n name: pkg.name,\n path: pkg.path,\n currentVersion: pkg.currentVersion,\n nextVersion: pkg.nextVersion,\n bump: pkg.bump === 'auto' ? 'patch' : pkg.bump,\n }));\n try {\n const result = await generateChangelog({\n repoRoot: opts.repoRoot,\n gitCwd: opts.gitCwd,\n packages,\n range: { to: 'HEAD' },\n changelog: {\n template: config.changelog?.template ?? undefined,\n locale,\n metadata: config.changelog?.metadata,\n ignoreAuthors: config.changelog?.ignoreAuthors,\n includeTypes: config.changelog?.includeTypes as string[],\n excludeTypes: config.changelog?.excludeTypes as string[],\n collapseMerges: config.changelog?.collapseMerges,\n collapseReverts: config.changelog?.collapseReverts,\n preferMergeSummary: config.changelog?.preferMergeSummary,\n },\n git: {\n autoUnshallow: config.git?.autoUnshallow,\n requireSignedTags: config.git?.requireSignedTags,\n baseUrl: config.git?.baseUrl ?? undefined,\n },\n platform: llm ? { llm } : undefined,\n });\n return result.markdown;\n } catch {\n return generateSimpleChangelog(packages, locale);\n }\n },\n };\n}\n","/**\n * Run release handler — thin adapter over core runReleasePipeline().\n */\n\nimport { defineHandler, findRepoRoot, type RestInput, useConfig, useLLM } from '@kb-labs/sdk';\nimport type {\n RunReleaseRequest,\n RunReleaseResponse,\n} from '@kb-labs/release-manager-contracts';\nimport {\n runReleasePipeline,\n resolveScopePath,\n type ReleaseConfig,\n type PublishablePackage,\n type PublishResult,\n} from '@kb-labs/release-manager-core';\nimport { publishPackagesProgrammatic } from '../../shared/publish-programmatic';\nimport { createChangelogGenerator } from '../../shared/changelog-factory';\n\nexport default defineHandler({\n async execute(ctx, input: RestInput<unknown, RunReleaseRequest>): Promise<RunReleaseResponse> {\n const scope = input.body?.scope || 'root';\n const dryRun = input.body?.dryRun ?? false;\n const skipChecks = input.body?.skipChecks ?? false;\n const otp = input.body?.otp;\n const cwd = ctx.cwd ?? process.cwd();\n const repoRoot = await findRepoRoot(cwd);\n const scopeCwd = await resolveScopePath(repoRoot, scope);\n\n const config = await useConfig<ReleaseConfig>() ?? {};\n\n // Changelog generator (with LLM if available)\n const llm = useLLM();\n const changelog = createChangelogGenerator(config, llm ?? undefined);\n\n // Programmatic publisher (token-based, no interactive OTP)\n const publisher = {\n async publish(packages: PublishablePackage[], opts: { dryRun?: boolean }): Promise<PublishResult> {\n return publishPackagesProgrammatic({\n packages,\n dryRun: opts.dryRun,\n otp,\n });\n },\n };\n\n const result = await runReleasePipeline({\n cwd,\n repoRoot,\n scopeCwd,\n scope,\n config,\n dryRun,\n skipChecks,\n checks: config.scopes?.[scope]?.checks ?? config.checks ?? [],\n publisher,\n changelog,\n logger: ctx.platform?.logger,\n });\n\n // Track analytics\n await ctx.platform?.analytics?.track?.(result.success ? 'release.completed' : 'release.failed', {\n scope,\n packagesCount: result.plan.packages.length,\n publishedCount: result.report.result.published?.length ?? 0,\n dryRun,\n durationMs: result.report.result.timingMs,\n });\n\n return {\n scope,\n report: result.report as any,\n success: result.success,\n errors: result.report.result.errors,\n };\n },\n});\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scopes handler - List available release scopes
|
|
5
|
+
*
|
|
6
|
+
* Returns list of packages/repos that can be released
|
|
7
|
+
* Analogous to commit-plugin scopes
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: _kb_labs_shared_command_kit.Handler<unknown, unknown, {
|
|
10
|
+
scopes: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
path: string;
|
|
14
|
+
type: "package" | "monorepo" | "root";
|
|
15
|
+
currentVersion?: string | undefined;
|
|
16
|
+
description?: string | undefined;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
|
|
20
|
+
export { _default as default };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { defineHandler, findRepoRoot, discoverSubRepoPaths } from '@kb-labs/sdk';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
// src/rest/handlers/scopes-handler.ts
|
|
6
|
+
var scopes_handler_default = defineHandler({
|
|
7
|
+
async execute(ctx) {
|
|
8
|
+
const cwd = ctx.cwd || process.cwd();
|
|
9
|
+
const repoRoot = await findRepoRoot(cwd);
|
|
10
|
+
const scopes = [];
|
|
11
|
+
try {
|
|
12
|
+
const rootPackageJson = JSON.parse(
|
|
13
|
+
await readFile(join(repoRoot, "package.json"), "utf-8")
|
|
14
|
+
);
|
|
15
|
+
scopes.push({
|
|
16
|
+
id: "root",
|
|
17
|
+
name: rootPackageJson.name || "Root",
|
|
18
|
+
path: repoRoot,
|
|
19
|
+
currentVersion: rootPackageJson.version,
|
|
20
|
+
description: rootPackageJson.description,
|
|
21
|
+
type: "root"
|
|
22
|
+
});
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
const subRepoPaths = discoverSubRepoPaths(repoRoot);
|
|
26
|
+
for (const subRepoPath of subRepoPaths) {
|
|
27
|
+
const pkgPath = join(subRepoPath, "package.json");
|
|
28
|
+
try {
|
|
29
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
30
|
+
if (!pkg.name) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const isMonorepo = await fileExists(join(subRepoPath, "pnpm-workspace.yaml")) || await fileExists(join(subRepoPath, "lerna.json"));
|
|
34
|
+
scopes.push({
|
|
35
|
+
id: pkg.name,
|
|
36
|
+
name: pkg.displayName || pkg.name,
|
|
37
|
+
path: subRepoPath,
|
|
38
|
+
currentVersion: pkg.version,
|
|
39
|
+
description: pkg.description,
|
|
40
|
+
type: isMonorepo ? "monorepo" : "package"
|
|
41
|
+
});
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const uniqueScopes = Array.from(
|
|
46
|
+
new Map(scopes.map((scope) => [scope.id, scope])).values()
|
|
47
|
+
);
|
|
48
|
+
uniqueScopes.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
return { scopes: uniqueScopes };
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
async function fileExists(path) {
|
|
53
|
+
try {
|
|
54
|
+
await readFile(path);
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { scopes_handler_default as default };
|
|
62
|
+
//# sourceMappingURL=scopes-handler.js.map
|
|
63
|
+
//# sourceMappingURL=scopes-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/rest/handlers/scopes-handler.ts"],"names":[],"mappings":";;;;;AAYA,IAAO,yBAAQ,aAAA,CAAc;AAAA,EAC3B,MAAM,QAAQ,GAAA,EAA8B;AAC1C,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AACnC,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,GAAG,CAAA;AAEvC,IAAA,MAAM,SAA6B,EAAC;AAGpC,IAAA,IAAI;AACF,MAAA,MAAM,kBAAkB,IAAA,CAAK,KAAA;AAAA,QAC3B,MAAM,QAAA,CAAS,IAAA,CAAK,QAAA,EAAU,cAAc,GAAG,OAAO;AAAA,OACxD;AAEA,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,gBAAgB,IAAA,IAAQ,MAAA;AAAA,QAC9B,IAAA,EAAM,QAAA;AAAA,QACN,gBAAgB,eAAA,CAAgB,OAAA;AAAA,QAChC,aAAa,eAAA,CAAgB,WAAA;AAAA,QAC7B,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,IACH,CAAA,CAAA,MAAQ;AAAA,IAER;AAGA,IAAA,MAAM,YAAA,GAAe,qBAAqB,QAAQ,CAAA;AAElD,IAAA,KAAA,MAAW,eAAe,YAAA,EAAc;AACtC,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,EAAa,cAAc,CAAA;AAEhD,MAAA,IAAI;AACF,QAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,MAAM,QAAA,CAAS,OAAA,EAAS,OAAO,CAAC,CAAA;AAEvD,QAAA,IAAI,CAAC,IAAI,IAAA,EAAM;AAAC,UAAA;AAAA,QAAS;AAGzB,QAAA,MAAM,UAAA,GACH,MAAM,UAAA,CAAW,IAAA,CAAK,WAAA,EAAa,qBAAqB,CAAC,CAAA,IACzD,MAAM,UAAA,CAAW,IAAA,CAAK,WAAA,EAAa,YAAY,CAAC,CAAA;AAEnD,QAAA,MAAA,CAAO,IAAA,CAAK;AAAA,UACV,IAAI,GAAA,CAAI,IAAA;AAAA,UACR,IAAA,EAAM,GAAA,CAAI,WAAA,IAAe,GAAA,CAAI,IAAA;AAAA,UAC7B,IAAA,EAAM,WAAA;AAAA,UACN,gBAAgB,GAAA,CAAI,OAAA;AAAA,UACpB,aAAa,GAAA,CAAI,WAAA;AAAA,UACjB,IAAA,EAAM,aAAa,UAAA,GAAa;AAAA,SACjC,CAAA;AAAA,MACH,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAGA,IAAA,MAAM,eAAe,KAAA,CAAM,IAAA;AAAA,MACzB,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAA,KAAA,KAAS,CAAC,KAAA,CAAM,EAAA,EAAI,KAAK,CAAC,CAAC,CAAA,CAAE,MAAA;AAAO,KACzD;AAGA,IAAA,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,EAAE,IAAA,CAAK,aAAA,CAAc,CAAA,CAAE,IAAI,CAAC,CAAA;AAExD,IAAA,OAAO,EAAE,QAAQ,YAAA,EAAa;AAAA,EAChC;AACF,CAAC;AAED,eAAe,WAAW,IAAA,EAAgC;AACxD,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAI,CAAA;AACnB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF","file":"scopes-handler.js","sourcesContent":["/**\n * Scopes handler - List available release scopes\n *\n * Returns list of packages/repos that can be released\n * Analogous to commit-plugin scopes\n */\n\nimport { defineHandler, findRepoRoot, discoverSubRepoPaths } from '@kb-labs/sdk';\nimport type { ScopesResponse, ReleaseScopeInfo } from '@kb-labs/release-manager-contracts';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport default defineHandler({\n async execute(ctx): Promise<ScopesResponse> {\n const cwd = ctx.cwd || process.cwd();\n const repoRoot = await findRepoRoot(cwd);\n\n const scopes: ReleaseScopeInfo[] = [];\n\n // 1. Add root scope (umbrella repository)\n try {\n const rootPackageJson = JSON.parse(\n await readFile(join(repoRoot, 'package.json'), 'utf-8')\n );\n\n scopes.push({\n id: 'root',\n name: rootPackageJson.name || 'Root',\n path: repoRoot,\n currentVersion: rootPackageJson.version,\n description: rootPackageJson.description,\n type: 'root',\n });\n } catch {\n // No package.json at root - skip root scope\n }\n\n // 2. Discover sub-repos via .gitmodules (works with nested layout)\n const subRepoPaths = discoverSubRepoPaths(repoRoot);\n\n for (const subRepoPath of subRepoPaths) {\n const pkgPath = join(subRepoPath, 'package.json');\n\n try {\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n\n if (!pkg.name) {continue;}\n\n // Check if it's a monorepo (has pnpm-workspace.yaml or lerna.json)\n const isMonorepo =\n (await fileExists(join(subRepoPath, 'pnpm-workspace.yaml'))) ||\n (await fileExists(join(subRepoPath, 'lerna.json')));\n\n scopes.push({\n id: pkg.name,\n name: pkg.displayName || pkg.name,\n path: subRepoPath,\n currentVersion: pkg.version,\n description: pkg.description,\n type: isMonorepo ? 'monorepo' : 'package',\n });\n } catch {\n // Skip sub-repos without valid package.json\n }\n }\n\n // Deduplicate by id (prevents virtual scroll bugs)\n const uniqueScopes = Array.from(\n new Map(scopes.map(scope => [scope.id, scope])).values()\n );\n\n // Sort by name\n uniqueScopes.sort((a, b) => a.name.localeCompare(b.name));\n\n return { scopes: uniqueScopes };\n },\n});\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await readFile(path);\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
|
|
2
|
+
import { RestInput } from '@kb-labs/sdk';
|
|
3
|
+
|
|
4
|
+
declare const _default: _kb_labs_shared_command_kit.Handler<unknown, RestInput<{
|
|
5
|
+
scope: string;
|
|
6
|
+
}, unknown, unknown>, {
|
|
7
|
+
scope: string;
|
|
8
|
+
hasPlan: boolean;
|
|
9
|
+
hasReport: boolean;
|
|
10
|
+
hasChangelog: boolean;
|
|
11
|
+
planStatus: "idle" | "ready" | "running" | "completed" | "failed";
|
|
12
|
+
packagesInPlan: number;
|
|
13
|
+
scopeInfo?: {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
path: string;
|
|
17
|
+
type: "package" | "monorepo" | "root";
|
|
18
|
+
currentVersion?: string | undefined;
|
|
19
|
+
description?: string | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
lastReleaseAt?: string | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
export { _default as default };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { defineHandler, findRepoRoot } from '@kb-labs/sdk';
|
|
2
|
+
import { readFile, access } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { glob } from 'glob';
|
|
5
|
+
import 'globby';
|
|
6
|
+
|
|
7
|
+
// src/rest/handlers/status-handler.ts
|
|
8
|
+
function scopeToDir(scope) {
|
|
9
|
+
return scope.replace(/^@/, "").replace(/\//g, "-").replace(/:/g, "-").replace(/\*/g, "");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/rest/handlers/status-handler.ts
|
|
13
|
+
var status_handler_default = defineHandler({
|
|
14
|
+
async execute(ctx, input) {
|
|
15
|
+
const scope = input.query?.scope || "root";
|
|
16
|
+
const cwd = ctx.cwd || process.cwd();
|
|
17
|
+
const repoRoot = await findRepoRoot(cwd);
|
|
18
|
+
const scopeInfo = await getScopeInfo(repoRoot, scope);
|
|
19
|
+
const scopeDirName = scopeToDir(scope);
|
|
20
|
+
const scopeDir = join(repoRoot, ".kb/release/plans", scopeDirName, "current");
|
|
21
|
+
const planPath = join(scopeDir, "plan.json");
|
|
22
|
+
join(scopeDir, "status.json");
|
|
23
|
+
const changelogPath = join(scopeDir, "changelog.md");
|
|
24
|
+
const hasPlan = await fileExists(planPath);
|
|
25
|
+
const hasChangelog = await fileExists(changelogPath);
|
|
26
|
+
let planStatus = "idle";
|
|
27
|
+
let packagesInPlan = 0;
|
|
28
|
+
if (hasPlan) {
|
|
29
|
+
try {
|
|
30
|
+
const planContent = await readFile(planPath, "utf-8");
|
|
31
|
+
const plan = JSON.parse(planContent);
|
|
32
|
+
packagesInPlan = plan.packages.length;
|
|
33
|
+
if (plan.packages.some((pkg) => pkg.isPublished)) {
|
|
34
|
+
planStatus = "completed";
|
|
35
|
+
} else if (packagesInPlan > 0) {
|
|
36
|
+
planStatus = "ready";
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
planStatus = "failed";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let lastReleaseAt;
|
|
43
|
+
let hasReport = false;
|
|
44
|
+
try {
|
|
45
|
+
const historyIndexPath = join(repoRoot, ".kb/release/history/index.json");
|
|
46
|
+
const indexContent = await readFile(historyIndexPath, "utf-8");
|
|
47
|
+
const index = JSON.parse(indexContent);
|
|
48
|
+
const scopeReleases = index.releases.filter((r) => r.scope === scope);
|
|
49
|
+
if (scopeReleases.length > 0) {
|
|
50
|
+
hasReport = true;
|
|
51
|
+
lastReleaseAt = scopeReleases[0].timestamp;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
scope,
|
|
57
|
+
scopeInfo,
|
|
58
|
+
hasPlan,
|
|
59
|
+
hasReport,
|
|
60
|
+
hasChangelog,
|
|
61
|
+
planStatus,
|
|
62
|
+
packagesInPlan,
|
|
63
|
+
lastReleaseAt
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
async function getScopeInfo(repoRoot, scope) {
|
|
68
|
+
try {
|
|
69
|
+
if (scope === "root") {
|
|
70
|
+
const rootPkgPath = join(repoRoot, "package.json");
|
|
71
|
+
const pkg = JSON.parse(await readFile(rootPkgPath, "utf-8"));
|
|
72
|
+
return {
|
|
73
|
+
id: "root",
|
|
74
|
+
name: pkg.name || "Root",
|
|
75
|
+
path: repoRoot,
|
|
76
|
+
currentVersion: pkg.version,
|
|
77
|
+
description: pkg.description,
|
|
78
|
+
type: "root"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const packageJsonFiles = await glob("*/package.json", {
|
|
82
|
+
cwd: repoRoot,
|
|
83
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
|
|
84
|
+
absolute: true
|
|
85
|
+
});
|
|
86
|
+
for (const pkgPath of packageJsonFiles) {
|
|
87
|
+
try {
|
|
88
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
89
|
+
if (pkg.name === scope) {
|
|
90
|
+
const pkgDir = join(pkgPath, "..");
|
|
91
|
+
const isMonorepo = await fileExists(join(pkgDir, "pnpm-workspace.yaml")) || await fileExists(join(pkgDir, "lerna.json"));
|
|
92
|
+
return {
|
|
93
|
+
id: pkg.name,
|
|
94
|
+
name: pkg.displayName || pkg.name,
|
|
95
|
+
path: pkgDir,
|
|
96
|
+
currentVersion: pkg.version,
|
|
97
|
+
description: pkg.description,
|
|
98
|
+
type: isMonorepo ? "monorepo" : "package"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return void 0;
|
|
105
|
+
} catch {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function fileExists(path) {
|
|
110
|
+
try {
|
|
111
|
+
await access(path);
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { status_handler_default as default };
|
|
119
|
+
//# sourceMappingURL=status-handler.js.map
|
|
120
|
+
//# sourceMappingURL=status-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/shared/utils.ts","../../../src/rest/handlers/status-handler.ts"],"names":["findRepoRoot","join","readFile"],"mappings":"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","file":"status-handler.js","sourcesContent":["import { join } from 'node:path';\nimport { readFile } from 'node:fs/promises';\nimport globby from 'globby';\n\nexport { findRepoRoot } from '@kb-labs/sdk';\n\n/**\n * Converts scope to safe directory name\n */\nexport function scopeToDir(scope: string): string {\n return scope\n .replace(/^@/, '')\n .replace(/\\//g, '-')\n .replace(/:/g, '-')\n .replace(/\\*/g, '');\n}\n\n/**\n * Converts safe directory name back to scope\n */\nexport function dirToScope(dirName: string): string {\n if (dirName === 'root') {\n return 'root';\n }\n\n const parts = dirName.split('-');\n if (parts.length >= 2) {\n return '@' + parts[0] + '-' + parts[1] + '/' + parts.slice(2).join('-');\n }\n\n return dirName;\n}\n\n/**\n * Resolve scope name to absolute filesystem path.\n * Scans all package.json in repoRoot to find the matching package by name.\n */\nexport async function resolveScopePath(repoRoot: string, scope: string): Promise<string> {\n if (scope === 'root') {\n return repoRoot;\n }\n\n if (scope.startsWith('@')) {\n const packageJsonPaths = await globby('**/package.json', {\n cwd: repoRoot,\n absolute: true,\n onlyFiles: true,\n ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/.*/**'],\n });\n\n for (const pkgJsonPath of packageJsonPaths) {\n try {\n const pkg = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));\n if (pkg.name === scope) {\n return join(pkgJsonPath, '..');\n }\n } catch { /* skip */ }\n }\n }\n\n // Direct path or fallback\n return join(repoRoot, scope);\n}\n","/**\n * Status handler - Get current release status for a scope\n *\n * Checks:\n * - .kb/release/plans/{scope}/current/plan.json\n * - .kb/release/plans/{scope}/current/status.json\n * - .kb/release/plans/{scope}/current/changelog.md\n * - .kb/release/history/ for last release\n */\n\nimport { defineHandler, findRepoRoot, type RestInput } from '@kb-labs/sdk';\nimport type { StatusResponse, ReleasePlan, ReleaseScopeInfo, StatusInput } from '@kb-labs/release-manager-contracts';\nimport { readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { glob } from 'glob';\nimport { scopeToDir } from '../../shared/utils';\n\nexport default defineHandler({\n async execute(ctx, input: RestInput<StatusInput>): Promise<StatusResponse> {\n const scope = input.query?.scope || 'root';\n const cwd = ctx.cwd || process.cwd();\n const repoRoot = await findRepoRoot(cwd);\n\n // Get scope info (package metadata)\n const scopeInfo = await getScopeInfo(repoRoot, scope);\n\n // Paths for scope-specific files\n const scopeDirName = scopeToDir(scope);\n const scopeDir = join(repoRoot, '.kb/release/plans', scopeDirName, 'current');\n const planPath = join(scopeDir, 'plan.json');\n const statusPath = join(scopeDir, 'status.json');\n const changelogPath = join(scopeDir, 'changelog.md');\n\n // Check if plan exists\n const hasPlan = await fileExists(planPath);\n const hasChangelog = await fileExists(changelogPath);\n\n let planStatus: 'idle' | 'ready' | 'running' | 'completed' | 'failed' = 'idle';\n let packagesInPlan = 0;\n\n if (hasPlan) {\n try {\n const planContent = await readFile(planPath, 'utf-8');\n const plan: ReleasePlan = JSON.parse(planContent);\n\n packagesInPlan = plan.packages.length;\n\n // Determine status based on plan metadata\n if (plan.packages.some(pkg => pkg.isPublished)) {\n planStatus = 'completed';\n } else if (packagesInPlan > 0) {\n planStatus = 'ready';\n }\n } catch {\n planStatus = 'failed';\n }\n }\n\n // Check for last release in history\n let lastReleaseAt: string | undefined;\n let hasReport = false;\n\n try {\n const historyIndexPath = join(repoRoot, '.kb/release/history/index.json');\n const indexContent = await readFile(historyIndexPath, 'utf-8');\n const index = JSON.parse(indexContent);\n\n // Find last release for this scope\n const scopeReleases = index.releases.filter((r: any) => r.scope === scope);\n if (scopeReleases.length > 0) {\n hasReport = true;\n lastReleaseAt = scopeReleases[0].timestamp;\n }\n } catch {\n // No history index yet\n }\n\n return {\n scope,\n scopeInfo,\n hasPlan,\n hasReport,\n hasChangelog,\n planStatus,\n packagesInPlan,\n lastReleaseAt,\n };\n },\n});\n\n/**\n * Get scope metadata (name, version, description) from package.json\n */\nasync function getScopeInfo(repoRoot: string, scope: string): Promise<ReleaseScopeInfo | undefined> {\n try {\n if (scope === 'root') {\n // Root scope\n const rootPkgPath = join(repoRoot, 'package.json');\n const pkg = JSON.parse(await readFile(rootPkgPath, 'utf-8'));\n\n return {\n id: 'root',\n name: pkg.name || 'Root',\n path: repoRoot,\n currentVersion: pkg.version,\n description: pkg.description,\n type: 'root',\n };\n }\n\n // Find package by name\n const packageJsonFiles = await glob('*/package.json', {\n cwd: repoRoot,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**'],\n absolute: true,\n });\n\n for (const pkgPath of packageJsonFiles) {\n try {\n const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));\n\n if (pkg.name === scope) {\n const pkgDir = join(pkgPath, '..');\n\n // Check if it's a monorepo\n const isMonorepo =\n (await fileExists(join(pkgDir, 'pnpm-workspace.yaml'))) ||\n (await fileExists(join(pkgDir, 'lerna.json')));\n\n return {\n id: pkg.name,\n name: pkg.displayName || pkg.name,\n path: pkgDir,\n currentVersion: pkg.version,\n description: pkg.description,\n type: isMonorepo ? 'monorepo' : 'package',\n };\n }\n } catch {\n // Skip invalid package.json\n }\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PluginContextV3 } from '@kb-labs/sdk';
|
|
2
|
+
|
|
3
|
+
declare function run(ctx: PluginContextV3): Promise<{
|
|
4
|
+
ok: boolean;
|
|
5
|
+
releaseDir: string;
|
|
6
|
+
created: string[];
|
|
7
|
+
configDefaults: {
|
|
8
|
+
workspace: {
|
|
9
|
+
type: string;
|
|
10
|
+
root: string;
|
|
11
|
+
};
|
|
12
|
+
git: {};
|
|
13
|
+
changelog: {
|
|
14
|
+
locale: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export { run };
|