@williamthorsen/release-kit 10.8.0 → 11.0.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/.meta/changelog.json +100 -3
- package/.readyup/kits/default.js +3 -3
- package/.readyup/kits/npm-auto-publish.js +43 -23
- package/.readyup/manifest.json +9 -9
- package/CHANGELOG.md +82 -3
- package/README.md +46 -11
- package/dist/esm/bin/release-kit.js +46 -120
- package/dist/esm/buildChangelogEntries.js +1 -0
- package/dist/esm/configFlagSchema.d.ts +12 -0
- package/dist/esm/configFlagSchema.js +3 -0
- package/dist/esm/createGithubReleaseCommand.d.ts +2 -1
- package/dist/esm/createGithubReleaseCommand.js +9 -3
- package/dist/esm/createTags.d.ts +2 -0
- package/dist/esm/createTags.js +5 -4
- package/dist/esm/discoverWorkspaces.d.ts +53 -8
- package/dist/esm/discoverWorkspaces.js +57 -37
- package/dist/esm/format.d.ts +5 -5
- package/dist/esm/format.js +5 -4
- package/dist/esm/glyphs.d.ts +3 -0
- package/dist/esm/glyphs.js +6 -0
- package/dist/esm/help/prepareHelp.d.ts +1 -1
- package/dist/esm/help/prepareHelp.js +1 -0
- package/dist/esm/init/initCommand.d.ts +3 -1
- package/dist/esm/init/initCommand.js +15 -15
- package/dist/esm/loadConfig.d.ts +7 -4
- package/dist/esm/loadConfig.js +6 -3
- package/dist/esm/loadValidatedConfig.d.ts +47 -5
- package/dist/esm/loadValidatedConfig.js +32 -15
- package/dist/esm/prepareCommand.d.ts +7 -1
- package/dist/esm/prepareCommand.js +37 -45
- package/dist/esm/previewTagPrefixes.d.ts +9 -6
- package/dist/esm/previewTagPrefixes.js +12 -24
- package/dist/esm/publishCommand.d.ts +4 -3
- package/dist/esm/publishCommand.js +9 -3
- package/dist/esm/pushCommand.d.ts +1 -1
- package/dist/esm/pushCommand.js +2 -2
- package/dist/esm/reportPrepare.d.ts +2 -0
- package/dist/esm/reportPrepare.js +51 -41
- package/dist/esm/resolveCommandTags.d.ts +3 -2
- package/dist/esm/resolveCommandTags.js +10 -6
- package/dist/esm/resolveReleaseNotesConfig.d.ts +10 -11
- package/dist/esm/resolveReleaseNotesConfig.js +10 -29
- package/dist/esm/resolveStylesOrExit.d.ts +10 -0
- package/dist/esm/resolveStylesOrExit.js +10 -0
- package/dist/esm/showTagPrefixesCommand.d.ts +5 -2
- package/dist/esm/showTagPrefixesCommand.js +31 -11
- package/dist/esm/sync-labels/generateCommand.d.ts +18 -8
- package/dist/esm/sync-labels/generateCommand.js +12 -11
- package/dist/esm/sync-labels/initCommand.d.ts +11 -4
- package/dist/esm/sync-labels/initCommand.js +24 -18
- package/dist/esm/sync-labels/retiredConfig.d.ts +4 -2
- package/dist/esm/sync-labels/retiredConfig.js +2 -2
- package/dist/esm/tagCommand.d.ts +2 -1
- package/dist/esm/tagCommand.js +2 -2
- package/dist/esm/validateOverridesCommand.d.ts +13 -7
- package/dist/esm/validateOverridesCommand.js +28 -36
- package/docs/configuration.md +35 -1
- package/docs/labels.md +2 -0
- package/docs/work-types.md +11 -8
- package/package.json +13 -14
- package/presets/labels/common.yaml +2 -2
- package/dist/esm/checkWorkTypesDrift.d.ts +0 -35
- package/dist/esm/checkWorkTypesDrift.js +0 -117
- package/dist/esm/syncWorkTypes.d.ts +0 -28
- package/dist/esm/syncWorkTypes.js +0 -94
- package/dist/esm/work-types.json +0 -149
- package/dist/esm/work-types.schema.json +0 -110
- package/dist/esm/workTypesUtils.d.ts +0 -22
- package/dist/esm/workTypesUtils.js +0 -16
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgsOrExit, readPackageVersion, reportError } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
|
-
import { checkWorkTypesDrift } from "../checkWorkTypesDrift.js";
|
|
5
4
|
import { commitCommand } from "../commitCommand.js";
|
|
5
|
+
import { configFlagSchema } from "../configFlagSchema.js";
|
|
6
6
|
import { createGithubReleaseCommand } from "../createGithubReleaseCommand.js";
|
|
7
7
|
import { showPrepareHelp } from "../help/prepareHelp.js";
|
|
8
8
|
import { initCommand } from "../init/initCommand.js";
|
|
9
9
|
import { prepareCommand } from "../prepareCommand.js";
|
|
10
10
|
import { publishCommand } from "../publishCommand.js";
|
|
11
11
|
import { pushCommand } from "../pushCommand.js";
|
|
12
|
+
import { OUTPUT_STYLE_ENV_VAR, resolveStylesOrExit } from "../resolveStylesOrExit.js";
|
|
12
13
|
import { showTagPrefixesCommand } from "../showTagPrefixesCommand.js";
|
|
13
14
|
import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
14
15
|
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
15
16
|
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
16
|
-
import { syncWorkTypes } from "../syncWorkTypes.js";
|
|
17
17
|
import { tagCommand } from "../tagCommand.js";
|
|
18
18
|
import { validateOverridesCommand } from "../validateOverridesCommand.js";
|
|
19
19
|
const VERSION = readPackageVersion(import.meta.url);
|
|
@@ -32,12 +32,16 @@ Commands:
|
|
|
32
32
|
init Initialize release-kit in the current repository
|
|
33
33
|
overrides Manage editorial changelog overrides
|
|
34
34
|
sync-labels Manage GitHub label synchronization
|
|
35
|
-
work-types Check for or sync work-type taxonomy drift against the upstream canonical
|
|
36
35
|
|
|
37
36
|
Options:
|
|
38
37
|
--dry-run Preview changes without writing files
|
|
39
38
|
--help, -h Show this help message
|
|
40
39
|
--version, -V Show the version number
|
|
40
|
+
|
|
41
|
+
Environment:
|
|
42
|
+
${OUTPUT_STYLE_ENV_VAR}
|
|
43
|
+
Output style: auto (default), plain, or rich. auto prints plain,
|
|
44
|
+
without emoji, when CI is set or the stream is not a terminal.
|
|
41
45
|
`);
|
|
42
46
|
}
|
|
43
47
|
function showSyncLabelsHelp() {
|
|
@@ -65,9 +69,10 @@ retired packages, then generate .github/labels.yaml. When the config file alread
|
|
|
65
69
|
exists, print the block for manual paste instead of rewriting the file.
|
|
66
70
|
|
|
67
71
|
Options:
|
|
68
|
-
--
|
|
69
|
-
--
|
|
70
|
-
--
|
|
72
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
73
|
+
--dry-run Preview changes without writing files
|
|
74
|
+
--force Overwrite existing files instead of skipping them
|
|
75
|
+
--help, -h Show this help message
|
|
71
76
|
`);
|
|
72
77
|
}
|
|
73
78
|
function showSyncLabelsGenerateHelp() {
|
|
@@ -77,8 +82,9 @@ Usage: release-kit sync-labels generate [options]
|
|
|
77
82
|
Regenerate .github/labels.yaml from the repoLabels block of .config/release-kit.config.ts.
|
|
78
83
|
|
|
79
84
|
Options:
|
|
80
|
-
--check
|
|
81
|
-
--
|
|
85
|
+
--check Report whether .github/labels.yaml is stale instead of writing it
|
|
86
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
87
|
+
--help, -h Show this help message
|
|
82
88
|
`);
|
|
83
89
|
}
|
|
84
90
|
function showSyncLabelsSyncHelp() {
|
|
@@ -151,6 +157,7 @@ Create GitHub Releases from changelog.json for tags on HEAD. Private
|
|
|
151
157
|
(package.json#private) workspaces are skipped with a warning and get no Release.
|
|
152
158
|
|
|
153
159
|
Options:
|
|
160
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
154
161
|
--dry-run Preview without creating releases
|
|
155
162
|
--tags=tag1,tag2 Only create releases for the named tags (comma-separated, full tag names)
|
|
156
163
|
--help, -h Show this help message
|
|
@@ -166,7 +173,8 @@ derived prefix nor declared in \`legacyIdentities\`, with a copy-pasteable
|
|
|
166
173
|
config snippet.
|
|
167
174
|
|
|
168
175
|
Options:
|
|
169
|
-
--
|
|
176
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
177
|
+
--help, -h Show this help message
|
|
170
178
|
`);
|
|
171
179
|
}
|
|
172
180
|
function showOverridesHelp() {
|
|
@@ -195,50 +203,8 @@ Exit codes:
|
|
|
195
203
|
2 Schema/parse or ambiguous-prefix errors (errors dominate)
|
|
196
204
|
|
|
197
205
|
Options:
|
|
198
|
-
--
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
function showWorkTypesHelp() {
|
|
202
|
-
console.info(`
|
|
203
|
-
Usage: release-kit work-types <subcommand>
|
|
204
|
-
|
|
205
|
-
Manage the canonical work-types taxonomy used by changelog and release-notes generation.
|
|
206
|
-
|
|
207
|
-
Subcommands:
|
|
208
|
-
check Compare the local work-types.json against the upstream codeassembly canonical
|
|
209
|
-
sync Overwrite the local work-types.json with the upstream contents
|
|
210
|
-
|
|
211
|
-
Exit codes (check):
|
|
212
|
-
0 Match (or upstream not found, with a warning)
|
|
213
|
-
1 Drift detected
|
|
214
|
-
2 Network error
|
|
215
|
-
3 Schema mismatch
|
|
216
|
-
|
|
217
|
-
Options:
|
|
218
|
-
--help, -h Show this help message
|
|
219
|
-
`);
|
|
220
|
-
}
|
|
221
|
-
function showWorkTypesCheckHelp() {
|
|
222
|
-
console.info(`
|
|
223
|
-
Usage: release-kit work-types check
|
|
224
|
-
|
|
225
|
-
Compare the local work-types.json against the upstream codeassembly canonical and report
|
|
226
|
-
drift. Exit 0 on match, 1 on drift, 0 + warning when upstream is not found, 2 on network
|
|
227
|
-
error, 3 on schema mismatch.
|
|
228
|
-
|
|
229
|
-
Options:
|
|
230
|
-
--help, -h Show this help message
|
|
231
|
-
`);
|
|
232
|
-
}
|
|
233
|
-
function showWorkTypesSyncHelp() {
|
|
234
|
-
console.info(`
|
|
235
|
-
Usage: release-kit work-types sync
|
|
236
|
-
|
|
237
|
-
Fetch the upstream work-types.json, validate its top-level shape, and overwrite the local
|
|
238
|
-
copy with the upstream content (formatted with 2-space indent + trailing newline).
|
|
239
|
-
|
|
240
|
-
Options:
|
|
241
|
-
--help, -h Show this help message
|
|
206
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
207
|
+
--help, -h Show this help message
|
|
242
208
|
`);
|
|
243
209
|
}
|
|
244
210
|
function showPublishHelp() {
|
|
@@ -251,6 +217,7 @@ silently filtered out. With --tags, an unpublishable tag is skipped with a warni
|
|
|
251
217
|
any publishable tags still publish.
|
|
252
218
|
|
|
253
219
|
Options:
|
|
220
|
+
--config <path> Path to config file (default: .config/release-kit.config.ts)
|
|
254
221
|
--dry-run Preview without publishing
|
|
255
222
|
--no-git-checks Skip the clean-working-tree check
|
|
256
223
|
--tags=tag1,tag2 Only publish the named tags (comma-separated, full tag names)
|
|
@@ -269,12 +236,17 @@ if (command === undefined || ['--help', '-h'].includes(command)) {
|
|
|
269
236
|
process.exit(0);
|
|
270
237
|
}
|
|
271
238
|
const flags = args.slice(1);
|
|
239
|
+
const styles = resolveStylesOrExit({
|
|
240
|
+
env: process.env,
|
|
241
|
+
stderrIsTty: process.stderr.isTTY,
|
|
242
|
+
stdoutIsTty: process.stdout.isTTY,
|
|
243
|
+
});
|
|
272
244
|
if (command === 'prepare') {
|
|
273
245
|
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
274
246
|
showPrepareHelp();
|
|
275
247
|
process.exit(0);
|
|
276
248
|
}
|
|
277
|
-
await prepareCommand(flags);
|
|
249
|
+
await prepareCommand(flags, styles);
|
|
278
250
|
process.exit(0);
|
|
279
251
|
}
|
|
280
252
|
if (command === 'commit') {
|
|
@@ -296,7 +268,7 @@ if (command === 'tag') {
|
|
|
296
268
|
showTagHelp();
|
|
297
269
|
process.exit(0);
|
|
298
270
|
}
|
|
299
|
-
tagCommand(flags);
|
|
271
|
+
tagCommand(flags, styles);
|
|
300
272
|
process.exit(0);
|
|
301
273
|
}
|
|
302
274
|
if (command === 'push') {
|
|
@@ -304,7 +276,7 @@ if (command === 'push') {
|
|
|
304
276
|
showPushHelp();
|
|
305
277
|
process.exit(0);
|
|
306
278
|
}
|
|
307
|
-
|
|
279
|
+
pushCommand(flags);
|
|
308
280
|
process.exit(0);
|
|
309
281
|
}
|
|
310
282
|
if (command === 'create-github-release') {
|
|
@@ -312,7 +284,7 @@ if (command === 'create-github-release') {
|
|
|
312
284
|
showCreateGithubReleaseHelp();
|
|
313
285
|
process.exit(0);
|
|
314
286
|
}
|
|
315
|
-
await createGithubReleaseCommand(flags);
|
|
287
|
+
await createGithubReleaseCommand(flags, styles);
|
|
316
288
|
process.exit(0);
|
|
317
289
|
}
|
|
318
290
|
if (command === 'publish') {
|
|
@@ -320,7 +292,7 @@ if (command === 'publish') {
|
|
|
320
292
|
showPublishHelp();
|
|
321
293
|
process.exit(0);
|
|
322
294
|
}
|
|
323
|
-
await publishCommand(flags);
|
|
295
|
+
await publishCommand(flags, styles);
|
|
324
296
|
process.exit(0);
|
|
325
297
|
}
|
|
326
298
|
if (command === 'show-tag-prefixes') {
|
|
@@ -328,11 +300,8 @@ if (command === 'show-tag-prefixes') {
|
|
|
328
300
|
showShowTagPrefixesHelp();
|
|
329
301
|
process.exit(0);
|
|
330
302
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
const exitCode = await showTagPrefixesCommand();
|
|
303
|
+
const { config } = parseArgsOrExit(flags, configFlagSchema).flags;
|
|
304
|
+
const exitCode = await showTagPrefixesCommand(styles, config);
|
|
336
305
|
process.exit(exitCode);
|
|
337
306
|
}
|
|
338
307
|
if (command === 'init') {
|
|
@@ -346,7 +315,7 @@ if (command === 'init') {
|
|
|
346
315
|
withConfig: { long: '--with-config', type: 'boolean' },
|
|
347
316
|
};
|
|
348
317
|
const { dryRun, force, withConfig } = parseArgsOrExit(flags, initFlagSchema).flags;
|
|
349
|
-
const exitCode = initCommand({ dryRun, force, withConfig });
|
|
318
|
+
const exitCode = initCommand({ dryRun, force, styles, withConfig });
|
|
350
319
|
process.exit(exitCode);
|
|
351
320
|
}
|
|
352
321
|
if (command === 'sync-labels') {
|
|
@@ -362,11 +331,17 @@ if (command === 'sync-labels') {
|
|
|
362
331
|
process.exit(0);
|
|
363
332
|
}
|
|
364
333
|
const syncLabelsInitFlagSchema = {
|
|
334
|
+
...configFlagSchema,
|
|
365
335
|
dryRun: { long: '--dry-run', type: 'boolean' },
|
|
366
336
|
force: { long: '--force', type: 'boolean' },
|
|
367
337
|
};
|
|
368
|
-
const { dryRun, force } = parseArgsOrExit(subflags, syncLabelsInitFlagSchema).flags;
|
|
369
|
-
const exitCode = await syncLabelsInitCommand({
|
|
338
|
+
const { config, dryRun, force } = parseArgsOrExit(subflags, syncLabelsInitFlagSchema).flags;
|
|
339
|
+
const exitCode = await syncLabelsInitCommand({
|
|
340
|
+
...(config !== undefined && { configPath: config }),
|
|
341
|
+
dryRun,
|
|
342
|
+
force,
|
|
343
|
+
styles,
|
|
344
|
+
});
|
|
370
345
|
process.exit(exitCode);
|
|
371
346
|
}
|
|
372
347
|
if (subcommand === 'generate') {
|
|
@@ -375,10 +350,11 @@ if (command === 'sync-labels') {
|
|
|
375
350
|
process.exit(0);
|
|
376
351
|
}
|
|
377
352
|
const generateFlagSchema = {
|
|
353
|
+
...configFlagSchema,
|
|
378
354
|
check: { long: '--check', type: 'boolean' },
|
|
379
355
|
};
|
|
380
|
-
const { check } = parseArgsOrExit(subflags, generateFlagSchema).flags;
|
|
381
|
-
const exitCode = await generateCommand({ check });
|
|
356
|
+
const { check, config } = parseArgsOrExit(subflags, generateFlagSchema).flags;
|
|
357
|
+
const exitCode = await generateCommand({ check, ...(config !== undefined && { configPath: config }), styles });
|
|
382
358
|
process.exit(exitCode);
|
|
383
359
|
}
|
|
384
360
|
if (subcommand === 'sync') {
|
|
@@ -409,11 +385,8 @@ if (command === 'overrides') {
|
|
|
409
385
|
showOverridesValidateHelp();
|
|
410
386
|
process.exit(0);
|
|
411
387
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
process.exit(1);
|
|
415
|
-
}
|
|
416
|
-
const result = await validateOverridesCommand();
|
|
388
|
+
const { config } = parseArgsOrExit(subflags, configFlagSchema).flags;
|
|
389
|
+
const result = await validateOverridesCommand(styles, config);
|
|
417
390
|
if (result.exitCode === 0) {
|
|
418
391
|
console.info(result.message);
|
|
419
392
|
}
|
|
@@ -426,53 +399,6 @@ if (command === 'overrides') {
|
|
|
426
399
|
showOverridesHelp();
|
|
427
400
|
process.exit(1);
|
|
428
401
|
}
|
|
429
|
-
if (command === 'work-types') {
|
|
430
|
-
const subcommand = flags[0];
|
|
431
|
-
if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
|
|
432
|
-
showWorkTypesHelp();
|
|
433
|
-
process.exit(0);
|
|
434
|
-
}
|
|
435
|
-
const subflags = flags.slice(1);
|
|
436
|
-
if (subcommand === 'check') {
|
|
437
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
438
|
-
showWorkTypesCheckHelp();
|
|
439
|
-
process.exit(0);
|
|
440
|
-
}
|
|
441
|
-
if (subflags.length > 0) {
|
|
442
|
-
reportError(`Unknown option: ${subflags[0]}`);
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
const result = await checkWorkTypesDrift();
|
|
446
|
-
if (result.exitCode === 0) {
|
|
447
|
-
console.info(result.message);
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
process.stderr.write(`${result.message}\n`);
|
|
451
|
-
}
|
|
452
|
-
process.exit(result.exitCode);
|
|
453
|
-
}
|
|
454
|
-
if (subcommand === 'sync') {
|
|
455
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
456
|
-
showWorkTypesSyncHelp();
|
|
457
|
-
process.exit(0);
|
|
458
|
-
}
|
|
459
|
-
if (subflags.length > 0) {
|
|
460
|
-
reportError(`Unknown option: ${subflags[0]}`);
|
|
461
|
-
process.exit(1);
|
|
462
|
-
}
|
|
463
|
-
const result = await syncWorkTypes();
|
|
464
|
-
if (result.exitCode === 0) {
|
|
465
|
-
console.info(result.message);
|
|
466
|
-
}
|
|
467
|
-
else {
|
|
468
|
-
process.stderr.write(`${result.message}\n`);
|
|
469
|
-
}
|
|
470
|
-
process.exit(result.exitCode);
|
|
471
|
-
}
|
|
472
|
-
reportError(`Unknown subcommand: ${subcommand}`);
|
|
473
|
-
showWorkTypesHelp();
|
|
474
|
-
process.exit(1);
|
|
475
|
-
}
|
|
476
402
|
reportError(`Unknown command: ${command}`);
|
|
477
403
|
showUsage();
|
|
478
404
|
process.exit(1);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `--config` flag, shared by every subcommand that reads the config file.
|
|
3
|
+
*
|
|
4
|
+
* Spread into a subcommand's own schema rather than repeated, so that seven subcommands cannot drift to seven
|
|
5
|
+
* spellings of one flag.
|
|
6
|
+
*/
|
|
7
|
+
export declare const configFlagSchema: {
|
|
8
|
+
config: {
|
|
9
|
+
long: string;
|
|
10
|
+
type: "string";
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
1
2
|
/**
|
|
2
3
|
* Orchestrate the CLI `create-github-release` command: Create GitHub Releases from changelog.json
|
|
3
4
|
* for tags on HEAD (or a comma-separated `--tags` subset), without requiring npm publish.
|
|
@@ -5,4 +6,4 @@
|
|
|
5
6
|
* Private/unpublishable workspaces are skipped with a warning and never get a Release, matching
|
|
6
7
|
* `release-kit publish`; an all-private tag set is a clean no-op.
|
|
7
8
|
*/
|
|
8
|
-
export declare function createGithubReleaseCommand(argv: string[]): Promise<void>;
|
|
9
|
+
export declare function createGithubReleaseCommand(argv: string[], styles: StreamStyles): Promise<void>;
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
|
+
import { configFlagSchema } from "./configFlagSchema.js";
|
|
3
4
|
import { createGithubReleases } from "./createGithubRelease.js";
|
|
4
5
|
import { formatPrivateSkip } from "./formatPrivateSkip.js";
|
|
6
|
+
import { assertConfigUsable } from "./loadValidatedConfig.js";
|
|
5
7
|
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
6
8
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
7
9
|
import { resolveReleaseNotesConfig } from "./resolveReleaseNotesConfig.js";
|
|
8
10
|
const createGithubReleaseFlagSchema = {
|
|
11
|
+
...configFlagSchema,
|
|
9
12
|
dryRun: { long: '--dry-run', type: 'boolean' },
|
|
10
13
|
tags: { long: '--tags', type: 'string' },
|
|
11
14
|
};
|
|
12
|
-
export async function createGithubReleaseCommand(argv) {
|
|
15
|
+
export async function createGithubReleaseCommand(argv, styles) {
|
|
13
16
|
const parsed = parseArgsOrExit(argv, createGithubReleaseFlagSchema);
|
|
14
17
|
const { dryRun } = parsed.flags;
|
|
18
|
+
await assertConfigUsable(styles.stderr, parsed.flags.config);
|
|
15
19
|
const requestedTags = parseRequestedTags(parsed.flags.tags);
|
|
16
|
-
const resolvedTags =
|
|
20
|
+
const resolvedTags = resolveCommandTags(requestedTags);
|
|
17
21
|
const publishableTags = resolvedTags.filter((resolvedTag) => resolvedTag.isPublishable);
|
|
18
22
|
for (const resolvedTag of resolvedTags) {
|
|
19
23
|
if (!resolvedTag.isPublishable) {
|
|
@@ -23,7 +27,9 @@ export async function createGithubReleaseCommand(argv) {
|
|
|
23
27
|
if (publishableTags.length === 0) {
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
|
-
const { changelogJsonOutputPath, sectionOrder } = await resolveReleaseNotesConfig({
|
|
30
|
+
const { changelogJsonOutputPath, sectionOrder } = await resolveReleaseNotesConfig(styles.stderr, {
|
|
31
|
+
...(parsed.flags.config !== undefined && { configPath: parsed.flags.config }),
|
|
32
|
+
});
|
|
27
33
|
let outcome;
|
|
28
34
|
try {
|
|
29
35
|
outcome = createGithubReleases(publishableTags, changelogJsonOutputPath, dryRun, sectionOrder);
|
package/dist/esm/createTags.d.ts
CHANGED
package/dist/esm/createTags.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { GIT_OUTPUT_LIMIT, hasErrnoCode } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { formatGlyphLine, GIT_OUTPUT_LIMIT, hasErrnoCode } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { deleteFileIfExists } from "./deleteFileIfExists.js";
|
|
4
|
+
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
4
5
|
import { readReleaseTags, RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
|
|
5
6
|
export function createTags(options) {
|
|
6
|
-
const { dryRun, noGitChecks } = options;
|
|
7
|
+
const { dryRun, noGitChecks, style } = options;
|
|
7
8
|
const tags = readReleaseTags();
|
|
8
9
|
if (tags.length === 0) {
|
|
9
10
|
return [];
|
|
@@ -14,7 +15,7 @@ export function createTags(options) {
|
|
|
14
15
|
if (dryRun) {
|
|
15
16
|
console.info('[dry-run] Would create tags:');
|
|
16
17
|
for (const tag of tags) {
|
|
17
|
-
console.info(
|
|
18
|
+
console.info(formatGlyphLine(RELEASE_GLYPHS, style, 'tag', tag));
|
|
18
19
|
}
|
|
19
20
|
return tags;
|
|
20
21
|
}
|
|
@@ -36,7 +37,7 @@ export function createTags(options) {
|
|
|
36
37
|
}
|
|
37
38
|
console.info('Created tags:');
|
|
38
39
|
for (const tag of tags) {
|
|
39
|
-
console.info(
|
|
40
|
+
console.info(formatGlyphLine(RELEASE_GLYPHS, style, 'tag', tag));
|
|
40
41
|
}
|
|
41
42
|
deleteFileIfExists(RELEASE_TAGS_FILE);
|
|
42
43
|
deleteFileIfExists(RELEASE_SUMMARY_FILE);
|
|
@@ -1,13 +1,58 @@
|
|
|
1
|
+
import { type EmptyWorkspaceCause } from '@williamthorsen/nmr-core/workspace';
|
|
2
|
+
export type { EmptyWorkspaceCause } from '@williamthorsen/nmr-core/workspace';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* What release-kit's workspace discovery produced: the repo releases as a single package, it resolves to a
|
|
5
|
+
* set of package directories, or it declares a workspace that resolves to none and the cause says which
|
|
6
|
+
* condition emptied it.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
8
|
+
* Held apart from nmr-core's `WorkspaceResolution`, whose `packageDirs` are absolute, because these are
|
|
9
|
+
* relative to the monorepo root. The two carry incompatible path contracts, and a distinct `kind` for the
|
|
10
|
+
* single-package case keeps either from standing in for the other.
|
|
11
|
+
*/
|
|
12
|
+
export type WorkspaceDiscovery = {
|
|
13
|
+
kind: 'empty';
|
|
14
|
+
cause: FailingWorkspaceCause;
|
|
15
|
+
patterns: string[];
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'packages';
|
|
18
|
+
packageDirs: string[];
|
|
19
|
+
patterns: string[];
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'single-package';
|
|
22
|
+
};
|
|
23
|
+
/** A workspace that resolved to no package directory, which every release-kit command treats as a failure. */
|
|
24
|
+
export type EmptyWorkspace = Extract<WorkspaceDiscovery, {
|
|
25
|
+
kind: 'empty';
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* The empty-workspace causes that reach a command as a failure.
|
|
29
|
+
*
|
|
30
|
+
* `no-packages-list` is absent because a manifest declaring nothing releases as a single package, so no
|
|
31
|
+
* command ever composes a message for it.
|
|
32
|
+
*/
|
|
33
|
+
export type FailingWorkspaceCause = Exclude<EmptyWorkspaceCause, 'no-packages-list'>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the sentence naming which of the five conditions left a workspace holding no package directory, and
|
|
36
|
+
* the remedy for that one. Each quotes the `packages` list the manifest declares, apart from the two that
|
|
37
|
+
* reach the matcher with no list to quote.
|
|
38
|
+
*
|
|
39
|
+
* The `package.json` requirement is stated under `no-package` because it is a divergence from pnpm, which
|
|
40
|
+
* recognizes two further manifests, and the reader of a workspace that pnpm resolves has no way to infer it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function describeEmptyWorkspace(workspace: EmptyWorkspace): string;
|
|
43
|
+
/**
|
|
44
|
+
* Reads `pnpm-workspace.yaml` at `monorepoRoot` and resolves its `packages` patterns, applying pnpm's
|
|
45
|
+
* semantics — `!`-prefixed exclusions included.
|
|
6
46
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
47
|
+
* A directory holding no manifest, and one whose manifest declares no `packages` list, both release as a
|
|
48
|
+
* single package: pnpm resolves each to the root package alone, and a workspace file kept for `catalog:`,
|
|
49
|
+
* `overrides:`, or `onlyBuiltDependencies:` is the second of them. Every other empty resolution is a failure,
|
|
50
|
+
* whether the reader declared patterns that matched nothing or a `packages` value the resolver could not read
|
|
51
|
+
* as patterns at all.
|
|
10
52
|
*
|
|
11
|
-
*
|
|
53
|
+
* `packageDirs` come back relative to `monorepoRoot` as POSIX paths, because that is what every consumer
|
|
54
|
+
* needs: `WorkspaceConfig.paths` feeds `git log -- <paths>`, and `packageFiles` and `changelogPaths` are read
|
|
55
|
+
* from disk relative to the working directory. The workspace root itself relativizes to `.`, the path
|
|
56
|
+
* `deriveWorkspaceConfig` reads a root package's manifest through.
|
|
12
57
|
*/
|
|
13
|
-
export declare function discoverWorkspaces():
|
|
58
|
+
export declare function discoverWorkspaces(monorepoRoot?: string): WorkspaceDiscovery;
|
|
@@ -1,42 +1,62 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { resolveWorkspace } from '@williamthorsen/nmr-core/workspace';
|
|
3
|
+
export function describeEmptyWorkspace(workspace) {
|
|
4
|
+
const declaredClause = describeDeclaredPatterns(workspace.patterns);
|
|
5
|
+
switch (workspace.cause) {
|
|
6
|
+
case 'all-excluded':
|
|
7
|
+
return (`pnpm-workspace.yaml ${declaredClause}, whose \`!\` entries exclude every directory matched by the ` +
|
|
8
|
+
'positive patterns. Drop or narrow the exclusion.');
|
|
9
|
+
case 'no-package':
|
|
10
|
+
return (`pnpm-workspace.yaml ${declaredClause}, and the matcher found no directory holding a \`package.json\`. ` +
|
|
11
|
+
'Add one to the directory that should be a package, or declare a pattern reaching a directory that ' +
|
|
12
|
+
'holds one.');
|
|
13
|
+
case 'no-pattern':
|
|
14
|
+
return (`pnpm-workspace.yaml ${declaredClause}, so no pattern reaches the matcher. Declare a positive pattern ` +
|
|
15
|
+
'such as `packages/*`, and quote any `!` entry, which YAML reads as a tag rather than a string where ' +
|
|
16
|
+
'it stands bare.');
|
|
17
|
+
case 'unreadable-manifest':
|
|
18
|
+
return ('pnpm-workspace.yaml holds no valid YAML, so nothing it declares reaches the matcher. Repair the ' +
|
|
19
|
+
'syntax error and run the command again. An unterminated quoted string and a mis-indented entry are ' +
|
|
20
|
+
'the usual ones.');
|
|
21
|
+
case 'unreadable-packages':
|
|
22
|
+
return ('pnpm-workspace.yaml declares a `packages` value that is not a list of strings, so nothing it ' +
|
|
23
|
+
'declares reaches the matcher. Make it a list whose every entry is a quoted pattern; a bare value ' +
|
|
24
|
+
'and an entry YAML read as a number or a map are the usual ones.');
|
|
25
|
+
default: {
|
|
26
|
+
const unhandledCause = workspace.cause;
|
|
27
|
+
throw new Error(`Unhandled empty-workspace cause: ${String(unhandledCause)}`);
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
}
|
|
31
|
+
export function discoverWorkspaces(monorepoRoot = process.cwd()) {
|
|
32
|
+
const resolution = resolveWorkspace(monorepoRoot);
|
|
33
|
+
if (resolution.kind === 'not-a-workspace') {
|
|
34
|
+
return { kind: 'single-package' };
|
|
27
35
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
36
|
+
if (resolution.kind === 'empty') {
|
|
37
|
+
const { cause, patterns } = resolution;
|
|
38
|
+
return cause === 'no-packages-list' ? { kind: 'single-package' } : { cause, kind: 'empty', patterns };
|
|
31
39
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
return {
|
|
41
|
+
...resolution,
|
|
42
|
+
packageDirs: resolution.packageDirs.map((packageDir) => relativizeToPosix(monorepoRoot, packageDir)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function describeDeclaredPatterns(patterns) {
|
|
46
|
+
const quotablePatterns = patterns.filter((pattern) => pattern.trim() !== '');
|
|
47
|
+
const emptiedCount = patterns.length - quotablePatterns.length;
|
|
48
|
+
if (emptiedCount === 0) {
|
|
49
|
+
return `declares ${renderQuotedList(patterns)}`;
|
|
40
50
|
}
|
|
41
|
-
|
|
51
|
+
const emptiedClause = `${String(emptiedCount)} ${emptiedCount === 1 ? 'entry' : 'entries'} that YAML left empty`;
|
|
52
|
+
return quotablePatterns.length === 0
|
|
53
|
+
? `declares ${emptiedClause}`
|
|
54
|
+
: `declares ${renderQuotedList(quotablePatterns)}, beside ${emptiedClause}`;
|
|
55
|
+
}
|
|
56
|
+
function relativizeToPosix(monorepoRoot, packageDir) {
|
|
57
|
+
const relativeDir = path.relative(monorepoRoot, packageDir);
|
|
58
|
+
return relativeDir === '' ? '.' : relativeDir.split(path.sep).join('/');
|
|
59
|
+
}
|
|
60
|
+
function renderQuotedList(patterns) {
|
|
61
|
+
return patterns.map((pattern) => `\`${pattern}\``).join(', ');
|
|
42
62
|
}
|
package/dist/esm/format.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export declare function bold(text: string): string;
|
|
3
|
-
/**
|
|
4
|
-
export declare function dim(text: string): string;
|
|
5
|
-
/**
|
|
1
|
+
/** Styles text bold, returning it unstyled for a stream that cannot render color. */
|
|
2
|
+
export declare function bold(text: string, stream?: NodeJS.WritableStream): string;
|
|
3
|
+
/** Styles text dim, returning it unstyled for a stream that cannot render color. */
|
|
4
|
+
export declare function dim(text: string, stream?: NodeJS.WritableStream): string;
|
|
5
|
+
/** Renders a section header for separating workspaces in CLI output. */
|
|
6
6
|
export declare function sectionHeader(name: string): string;
|
package/dist/esm/format.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
export function bold(text, stream = process.stdout) {
|
|
3
|
+
return styleText('bold', text, { stream });
|
|
3
4
|
}
|
|
4
|
-
export function dim(text) {
|
|
5
|
-
return
|
|
5
|
+
export function dim(text, stream = process.stdout) {
|
|
6
|
+
return styleText('dim', text, { stream });
|
|
6
7
|
}
|
|
7
8
|
export function sectionHeader(name) {
|
|
8
9
|
return `━━━ ${name} ━━━`;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Glyphs that name no status; a status marker comes from nmr-core's `STATUS_GLYPHS`. */
|
|
2
|
+
export declare const RELEASE_GLYPHS: Readonly<Record<"plain" | "rich", Readonly<Record<"bump" | "dryRun" | "tag", import("@williamthorsen/toolbelt.terminal/candidate").Glyph>>>>;
|
|
3
|
+
export type ReleaseGlyphName = keyof (typeof RELEASE_GLYPHS)['rich'];
|