@williamthorsen/release-kit 10.8.0 → 10.9.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 +43 -3
- package/.readyup/kits/default.js +3 -3
- package/.readyup/kits/npm-auto-publish.js +1 -1
- package/.readyup/manifest.json +9 -9
- package/CHANGELOG.md +34 -3
- package/README.md +25 -9
- package/dist/esm/bin/release-kit.js +20 -102
- package/dist/esm/createGithubReleaseCommand.d.ts +2 -1
- package/dist/esm/createGithubReleaseCommand.js +4 -2
- package/dist/esm/createTags.d.ts +2 -0
- package/dist/esm/createTags.js +5 -4
- 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/init/initCommand.d.ts +3 -1
- package/dist/esm/init/initCommand.js +15 -15
- package/dist/esm/loadValidatedConfig.d.ts +2 -1
- package/dist/esm/loadValidatedConfig.js +3 -3
- package/dist/esm/prepareCommand.d.ts +2 -1
- package/dist/esm/prepareCommand.js +17 -16
- package/dist/esm/publishCommand.d.ts +2 -1
- package/dist/esm/publishCommand.js +2 -2
- package/dist/esm/reportPrepare.d.ts +2 -0
- package/dist/esm/reportPrepare.js +44 -38
- package/dist/esm/resolveReleaseNotesConfig.d.ts +2 -1
- package/dist/esm/resolveReleaseNotesConfig.js +4 -4
- package/dist/esm/resolveStylesOrExit.d.ts +10 -0
- package/dist/esm/resolveStylesOrExit.js +10 -0
- package/dist/esm/showTagPrefixesCommand.d.ts +2 -1
- package/dist/esm/showTagPrefixesCommand.js +16 -10
- package/dist/esm/sync-labels/generateCommand.d.ts +4 -2
- package/dist/esm/sync-labels/generateCommand.js +4 -4
- package/dist/esm/sync-labels/initCommand.d.ts +3 -1
- package/dist/esm/sync-labels/initCommand.js +7 -7
- package/dist/esm/tagCommand.d.ts +2 -1
- package/dist/esm/tagCommand.js +2 -2
- package/dist/esm/validateOverridesCommand.d.ts +4 -4
- package/dist/esm/validateOverridesCommand.js +6 -6
- package/docs/configuration.md +23 -1
- package/docs/work-types.md +11 -8
- package/package.json +11 -11
- 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,36 +1,36 @@
|
|
|
1
|
-
import { printError, printStep, printSuccess, reportWriteResult } from '@williamthorsen/nmr-core';
|
|
1
|
+
import { printError, printStep, printSuccess, reportWriteResult, } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
3
|
import { hasPackageJson, isGitRepo, usesPnpm } from "./checks.js";
|
|
4
4
|
import { detectRepoType } from "./detectRepoType.js";
|
|
5
5
|
import { scaffoldFiles } from "./scaffold.js";
|
|
6
|
-
function runRequiredCheck(label, result) {
|
|
6
|
+
function runRequiredCheck(label, result, styles) {
|
|
7
7
|
if (result.ok) {
|
|
8
|
-
printSuccess(label);
|
|
8
|
+
printSuccess(label, styles.stdout);
|
|
9
9
|
return true;
|
|
10
10
|
}
|
|
11
|
-
printError(result.message ?? `${label} failed
|
|
11
|
+
printError(result.message ?? `${label} failed`, styles.stderr);
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
|
-
function checkEligibility() {
|
|
14
|
+
function checkEligibility(styles) {
|
|
15
15
|
printStep('Checking eligibility');
|
|
16
|
-
if (!runRequiredCheck('Git repository detected', isGitRepo()))
|
|
16
|
+
if (!runRequiredCheck('Git repository detected', isGitRepo(), styles))
|
|
17
17
|
return false;
|
|
18
|
-
if (!runRequiredCheck('package.json found', hasPackageJson()))
|
|
18
|
+
if (!runRequiredCheck('package.json found', hasPackageJson(), styles))
|
|
19
19
|
return false;
|
|
20
|
-
if (!runRequiredCheck('pnpm detected', usesPnpm()))
|
|
20
|
+
if (!runRequiredCheck('pnpm detected', usesPnpm(), styles))
|
|
21
21
|
return false;
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
|
-
export function initCommand({ dryRun, force, withConfig }) {
|
|
24
|
+
export function initCommand({ dryRun, force, styles, withConfig }) {
|
|
25
25
|
if (dryRun) {
|
|
26
26
|
console.info('[dry-run mode]');
|
|
27
27
|
}
|
|
28
28
|
let eligible;
|
|
29
29
|
try {
|
|
30
|
-
eligible = checkEligibility();
|
|
30
|
+
eligible = checkEligibility(styles);
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
33
|
-
printError(`Eligibility check failed: ${describeError(error)}
|
|
33
|
+
printError(`Eligibility check failed: ${describeError(error)}`, styles.stderr);
|
|
34
34
|
return 1;
|
|
35
35
|
}
|
|
36
36
|
if (!eligible)
|
|
@@ -41,21 +41,21 @@ export function initCommand({ dryRun, force, withConfig }) {
|
|
|
41
41
|
repoType = detectRepoType();
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
|
-
printError(`Failed to detect repo type: ${describeError(error)}
|
|
44
|
+
printError(`Failed to detect repo type: ${describeError(error)}`, styles.stderr);
|
|
45
45
|
return 1;
|
|
46
46
|
}
|
|
47
|
-
printSuccess(`Detected: ${repoType}
|
|
47
|
+
printSuccess(`Detected: ${repoType}`, styles.stdout);
|
|
48
48
|
printStep('Scaffolding files');
|
|
49
49
|
let results;
|
|
50
50
|
try {
|
|
51
51
|
results = scaffoldFiles({ repoType, dryRun, overwrite: force, withConfig });
|
|
52
52
|
}
|
|
53
53
|
catch (error) {
|
|
54
|
-
printError(`Failed to scaffold files: ${describeError(error)}
|
|
54
|
+
printError(`Failed to scaffold files: ${describeError(error)}`, styles.stderr);
|
|
55
55
|
return 1;
|
|
56
56
|
}
|
|
57
57
|
for (const result of results) {
|
|
58
|
-
reportWriteResult(result, dryRun);
|
|
58
|
+
reportWriteResult(result, dryRun, styles);
|
|
59
59
|
}
|
|
60
60
|
if (results.some((r) => r.outcome === 'failed')) {
|
|
61
61
|
return 1;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
1
2
|
import type { ReleaseKitConfig } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Outcome of loading and validating the consumer config file. `missing` is not reported
|
|
@@ -15,4 +16,4 @@ export type LoadValidatedConfigResult = {
|
|
|
15
16
|
* Load `.config/release-kit.config.ts` and validate it against the shared schema,
|
|
16
17
|
* reporting load failures and validation errors to stderr.
|
|
17
18
|
*/
|
|
18
|
-
export declare function loadValidatedConfig(): Promise<LoadValidatedConfigResult>;
|
|
19
|
+
export declare function loadValidatedConfig(stderrStyle: OutputStyle): Promise<LoadValidatedConfigResult>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { reportError } from '@williamthorsen/nmr-core';
|
|
1
|
+
import { printError, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
3
|
import { loadConfig } from "./loadConfig.js";
|
|
4
4
|
import { validateConfig } from "./validateConfig.js";
|
|
5
|
-
export async function loadValidatedConfig() {
|
|
5
|
+
export async function loadValidatedConfig(stderrStyle) {
|
|
6
6
|
let raw;
|
|
7
7
|
try {
|
|
8
8
|
raw = await loadConfig();
|
|
@@ -19,7 +19,7 @@ export async function loadValidatedConfig() {
|
|
|
19
19
|
if (errors.length > 0) {
|
|
20
20
|
process.stderr.write('Invalid config:\n');
|
|
21
21
|
for (const err of errors) {
|
|
22
|
-
|
|
22
|
+
printError(err, stderrStyle);
|
|
23
23
|
}
|
|
24
24
|
return { status: 'invalid' };
|
|
25
25
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
1
2
|
import type { ReleaseType } from './types.js';
|
|
2
3
|
export declare const prepareFlagSchema: {
|
|
3
4
|
dryRun: {
|
|
@@ -49,4 +50,4 @@ export declare function parseArgs(argv: string[]): {
|
|
|
49
50
|
* 4. Delegates to `releasePrepare` or `releasePrepareMono` to compute the release plan.
|
|
50
51
|
* 5. Applies the plan, runs the format command, and prints the result via `reportPrepare`.
|
|
51
52
|
*/
|
|
52
|
-
export declare function prepareCommand(argv: string[]): Promise<void>;
|
|
53
|
+
export declare function prepareCommand(argv: string[], styles: StreamStyles): Promise<void>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { formatGlyphLine, formatStatusLine, parseArgsOrExit, printError, reportError, } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
4
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
5
5
|
import { buildDependencyGraph } from "./buildDependencyGraph.js";
|
|
6
6
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
7
7
|
import { dim } from "./format.js";
|
|
8
8
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
9
|
+
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
9
10
|
import { loadConfig, mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
10
11
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
|
|
11
12
|
import { applyReleasePlan } from "./releasePlan.js";
|
|
@@ -72,7 +73,7 @@ export function parseArgs(argv) {
|
|
|
72
73
|
withReleaseNotes: flags.withReleaseNotes,
|
|
73
74
|
};
|
|
74
75
|
}
|
|
75
|
-
export async function prepareCommand(argv) {
|
|
76
|
+
export async function prepareCommand(argv, styles) {
|
|
76
77
|
const { dryRun, force, noGitChecks, bumpOverride, only, setVersion, withReleaseNotes } = parseArgs(argv);
|
|
77
78
|
const options = {
|
|
78
79
|
force,
|
|
@@ -81,7 +82,7 @@ export async function prepareCommand(argv) {
|
|
|
81
82
|
...(withReleaseNotes && { withReleaseNotes: true }),
|
|
82
83
|
};
|
|
83
84
|
if (dryRun) {
|
|
84
|
-
console.info(
|
|
85
|
+
console.info(`\n${formatGlyphLine(RELEASE_GLYPHS, styles.stdout, 'dryRun', 'DRY RUN — no files will be modified')}\n`);
|
|
85
86
|
}
|
|
86
87
|
if (!dryRun && !noGitChecks) {
|
|
87
88
|
try {
|
|
@@ -92,7 +93,7 @@ export async function prepareCommand(argv) {
|
|
|
92
93
|
process.exit(1);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
|
-
const userConfig = await loadAndValidateConfig();
|
|
96
|
+
const userConfig = await loadAndValidateConfig(styles.stderr);
|
|
96
97
|
let discoveredPaths;
|
|
97
98
|
try {
|
|
98
99
|
discoveredPaths = await discoverWorkspaces();
|
|
@@ -102,13 +103,13 @@ export async function prepareCommand(argv) {
|
|
|
102
103
|
process.exit(1);
|
|
103
104
|
}
|
|
104
105
|
if (discoveredPaths === undefined) {
|
|
105
|
-
runSinglePackageMode(userConfig, options, only, dryRun);
|
|
106
|
+
runSinglePackageMode(userConfig, options, only, dryRun, styles.stdout);
|
|
106
107
|
}
|
|
107
108
|
else {
|
|
108
|
-
runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun);
|
|
109
|
+
runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun, styles.stdout);
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
|
-
function runSinglePackageMode(userConfig, options, only, dryRun) {
|
|
112
|
+
function runSinglePackageMode(userConfig, options, only, dryRun, style) {
|
|
112
113
|
if (only !== undefined) {
|
|
113
114
|
reportError('--only is only supported for monorepo configurations');
|
|
114
115
|
process.exit(1);
|
|
@@ -119,9 +120,9 @@ function runSinglePackageMode(userConfig, options, only, dryRun) {
|
|
|
119
120
|
process.exit(1);
|
|
120
121
|
}
|
|
121
122
|
const config = mergeSinglePackageConfig(userConfig);
|
|
122
|
-
runAndReport(() => releasePrepare(config, options), dryRun);
|
|
123
|
+
runAndReport(() => releasePrepare(config, options), dryRun, style);
|
|
123
124
|
}
|
|
124
|
-
function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun) {
|
|
125
|
+
function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun, style) {
|
|
125
126
|
let config;
|
|
126
127
|
try {
|
|
127
128
|
const rootPackage = readRootPackageVersion();
|
|
@@ -177,9 +178,9 @@ function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion,
|
|
|
177
178
|
process.exit(1);
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
|
-
runAndReport(() => releasePrepareMono(config, { ...options, ...(only !== undefined && { only }) }), dryRun);
|
|
181
|
+
runAndReport(() => releasePrepareMono(config, { ...options, ...(only !== undefined && { only }) }), dryRun, style);
|
|
181
182
|
}
|
|
182
|
-
async function loadAndValidateConfig() {
|
|
183
|
+
async function loadAndValidateConfig(stderrStyle) {
|
|
183
184
|
let rawConfig;
|
|
184
185
|
try {
|
|
185
186
|
rawConfig = await loadConfig();
|
|
@@ -195,16 +196,16 @@ async function loadAndValidateConfig() {
|
|
|
195
196
|
if (errors.length > 0) {
|
|
196
197
|
process.stderr.write('Invalid config:\n');
|
|
197
198
|
for (const err of errors) {
|
|
198
|
-
|
|
199
|
+
printError(err, stderrStyle);
|
|
199
200
|
}
|
|
200
201
|
process.exit(1);
|
|
201
202
|
}
|
|
202
203
|
for (const warning of warnings) {
|
|
203
|
-
console.warn(`
|
|
204
|
+
console.warn(` ${formatStatusLine(stderrStyle, 'warning', warning)}`);
|
|
204
205
|
}
|
|
205
206
|
return config;
|
|
206
207
|
}
|
|
207
|
-
function runAndReport(computePlan, dryRun) {
|
|
208
|
+
function runAndReport(computePlan, dryRun, style) {
|
|
208
209
|
let plan;
|
|
209
210
|
try {
|
|
210
211
|
plan = computePlan();
|
|
@@ -215,7 +216,7 @@ function runAndReport(computePlan, dryRun) {
|
|
|
215
216
|
process.exit(1);
|
|
216
217
|
}
|
|
217
218
|
if (dryRun) {
|
|
218
|
-
process.stdout.write(reportPrepare(plan, { applied: false }) + '\n');
|
|
219
|
+
process.stdout.write(reportPrepare(plan, { applied: false, style }) + '\n');
|
|
219
220
|
reportPlanFiles(plan, true);
|
|
220
221
|
return;
|
|
221
222
|
}
|
|
@@ -227,7 +228,7 @@ function runAndReport(computePlan, dryRun) {
|
|
|
227
228
|
process.exit(1);
|
|
228
229
|
}
|
|
229
230
|
const formatError = runFormatCommand(plan.formatCommand);
|
|
230
|
-
process.stdout.write(reportPrepare(plan, { applied: true, ...(formatError !== undefined && { formatError }) }) + '\n');
|
|
231
|
+
process.stdout.write(reportPrepare(plan, { applied: true, style, ...(formatError !== undefined && { formatError }) }) + '\n');
|
|
231
232
|
reportPlanFiles(plan, false);
|
|
232
233
|
if (formatError !== undefined) {
|
|
233
234
|
reportError(`The format command failed, but the release is prepared and ${RELEASE_TAGS_FILE} is written. ` +
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
1
2
|
/**
|
|
2
3
|
* Orchestrate the CLI `publish` command: parse flags, discover workspaces, resolve tags from HEAD,
|
|
3
4
|
* detect the package manager, validate `--tags`, and publish each tag with inject/restore lifecycle.
|
|
4
5
|
*/
|
|
5
|
-
export declare function publishCommand(argv: string[]): Promise<void>;
|
|
6
|
+
export declare function publishCommand(argv: string[], styles: StreamStyles): Promise<void>;
|
|
@@ -16,7 +16,7 @@ const publishFlagSchema = {
|
|
|
16
16
|
provenance: { long: '--provenance', type: 'boolean' },
|
|
17
17
|
tags: { long: '--tags', type: 'string' },
|
|
18
18
|
};
|
|
19
|
-
export async function publishCommand(argv) {
|
|
19
|
+
export async function publishCommand(argv, styles) {
|
|
20
20
|
const parsed = parseArgsOrExit(argv, publishFlagSchema);
|
|
21
21
|
const { dryRun, noGitChecks, provenance } = parsed.flags;
|
|
22
22
|
if (!dryRun && !noGitChecks) {
|
|
@@ -39,7 +39,7 @@ export async function publishCommand(argv) {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
const packageManager = detectPackageManager();
|
|
42
|
-
const { releaseNotes, changelogJsonOutputPath, sectionOrder } = await resolveReleaseNotesConfig();
|
|
42
|
+
const { releaseNotes, changelogJsonOutputPath, sectionOrder } = await resolveReleaseNotesConfig(styles.stderr);
|
|
43
43
|
const shouldInject = releaseNotes.shouldInjectIntoReadme;
|
|
44
44
|
console.info(dryRun ? '[dry-run] Would publish:' : 'Publishing:');
|
|
45
45
|
for (const { tag, workspacePath } of publishableTags) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
1
2
|
import type { PrepareResult } from './types.js';
|
|
2
3
|
/** How the plan was carried out, which decides the tense the report is rendered in. */
|
|
3
4
|
export interface ReportPrepareOptions {
|
|
@@ -5,6 +6,7 @@ export interface ReportPrepareOptions {
|
|
|
5
6
|
applied: boolean;
|
|
6
7
|
/** The format command's failure message, when it ran and failed. */
|
|
7
8
|
formatError?: string;
|
|
9
|
+
style: OutputStyle;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Format a release plan into styled terminal output.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { formatGlyphLine, formatStatusLine, measureGlyphColumn, STATUS_GLYPHS, } from '@williamthorsen/nmr-core';
|
|
1
2
|
import { bold, dim, sectionHeader } from "./format.js";
|
|
3
|
+
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
2
4
|
import { GIT_CLIFF_NPX_ARGS } from "./runGitCliff.js";
|
|
3
5
|
const CLIFF_DRY_RUN_COMMAND = `npx ${GIT_CLIFF_NPX_ARGS.join(' ')} ...`;
|
|
4
6
|
export function reportPrepare(plan, options) {
|
|
@@ -10,6 +12,7 @@ export function reportPrepare(plan, options) {
|
|
|
10
12
|
}
|
|
11
13
|
function formatSingleWorkspace(result, options) {
|
|
12
14
|
const dryRun = !options.applied;
|
|
15
|
+
const { style } = options;
|
|
13
16
|
const lines = [];
|
|
14
17
|
const workspace = result.workspaces[0];
|
|
15
18
|
if (workspace === undefined) {
|
|
@@ -20,11 +23,11 @@ function formatSingleWorkspace(result, options) {
|
|
|
20
23
|
if (workspace.parsedCommitCount !== undefined) {
|
|
21
24
|
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
22
25
|
}
|
|
23
|
-
formatUnparseableWarning(lines, workspace);
|
|
24
|
-
formatPolicyViolations(lines, workspace.policyViolations);
|
|
26
|
+
formatUnparseableWarning(lines, workspace, style);
|
|
27
|
+
formatPolicyViolations(lines, workspace.policyViolations, style);
|
|
25
28
|
if (workspace.status === 'skipped') {
|
|
26
|
-
lines.push(
|
|
27
|
-
formatWarnings(lines, result);
|
|
29
|
+
lines.push(formatStatusLine(style, 'skipped', workspace.skipReason));
|
|
30
|
+
formatWarnings(lines, result, style);
|
|
28
31
|
return lines.join('\n');
|
|
29
32
|
}
|
|
30
33
|
if (workspace.setVersion !== undefined) {
|
|
@@ -40,49 +43,50 @@ function formatSingleWorkspace(result, options) {
|
|
|
40
43
|
lines.push(dim(`Bumping versions (version override)...`));
|
|
41
44
|
}
|
|
42
45
|
if (workspace.setVersion !== undefined) {
|
|
43
|
-
lines.push(
|
|
46
|
+
lines.push(formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${workspace.currentVersion} → ${bold(workspace.newVersion)} (version override)`));
|
|
44
47
|
}
|
|
45
48
|
else if (workspace.releaseType !== undefined) {
|
|
46
|
-
lines.push(
|
|
49
|
+
lines.push(formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${workspace.currentVersion} → ${bold(workspace.newVersion)} (${workspace.releaseType})`));
|
|
47
50
|
}
|
|
48
51
|
formatBumpFiles(lines, workspace, dryRun);
|
|
49
52
|
lines.push(dim('Generating changelogs...'));
|
|
50
53
|
formatChangelogFiles(lines, workspace, dryRun);
|
|
51
54
|
formatPreviewFiles(lines, workspace, dryRun);
|
|
52
55
|
formatFormatCommand(lines, result, options);
|
|
53
|
-
formatWarnings(lines, result);
|
|
54
|
-
lines.push(
|
|
56
|
+
formatWarnings(lines, result, style);
|
|
57
|
+
lines.push(formatStatusLine(style, 'passed', 'Release preparation complete.'), ` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(workspace.tag))}`);
|
|
55
58
|
return lines.join('\n');
|
|
56
59
|
}
|
|
57
60
|
function formatMultiWorkspace(result, options) {
|
|
58
61
|
const dryRun = !options.applied;
|
|
62
|
+
const { style } = options;
|
|
59
63
|
const lines = [];
|
|
60
64
|
for (const workspace of result.workspaces) {
|
|
61
|
-
formatWorkspaceSection(lines, workspace, dryRun);
|
|
65
|
+
formatWorkspaceSection(lines, workspace, dryRun, style);
|
|
62
66
|
}
|
|
63
67
|
if (result.project !== undefined) {
|
|
64
|
-
formatProjectSection(lines, result.project, dryRun);
|
|
68
|
+
formatProjectSection(lines, result.project, dryRun, style);
|
|
65
69
|
}
|
|
66
70
|
formatFormatCommand(lines, result, options);
|
|
67
|
-
formatWarnings(lines, result);
|
|
71
|
+
formatWarnings(lines, result, style);
|
|
68
72
|
if (result.tags.length > 0) {
|
|
69
|
-
lines.push(`\n
|
|
73
|
+
lines.push(`\n${formatStatusLine(style, 'passed', 'Release preparation complete.')}`);
|
|
70
74
|
for (const tag of result.tags) {
|
|
71
|
-
lines.push(`
|
|
75
|
+
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(tag))}`);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
else {
|
|
75
|
-
lines.push(`\n
|
|
79
|
+
lines.push(`\n${formatStatusLine(style, 'skipped', 'No workspaces had release-worthy changes.')}`);
|
|
76
80
|
}
|
|
77
81
|
return lines.join('\n');
|
|
78
82
|
}
|
|
79
|
-
function formatProjectSection(lines, project, dryRun) {
|
|
83
|
+
function formatProjectSection(lines, project, dryRun, style) {
|
|
80
84
|
lines.push(`\n${sectionHeader('project')}`);
|
|
81
85
|
const since = project.previousTag === undefined ? '(no previous release found)' : `since ${project.previousTag}`;
|
|
82
86
|
lines.push(dim(` Found ${project.commitCount} commits ${since}`));
|
|
83
|
-
formatPolicyViolations(lines, project.policyViolations, ' ');
|
|
87
|
+
formatPolicyViolations(lines, project.policyViolations, style, ' ');
|
|
84
88
|
if (project.status === 'skipped') {
|
|
85
|
-
lines.push(`
|
|
89
|
+
lines.push(` ${formatStatusLine(style, 'skipped', project.skipReason)}`);
|
|
86
90
|
return;
|
|
87
91
|
}
|
|
88
92
|
const { releaseType, currentVersion, newVersion, tag } = project;
|
|
@@ -92,8 +96,8 @@ function formatProjectSection(lines, project, dryRun) {
|
|
|
92
96
|
if (project.bumpOverride !== undefined) {
|
|
93
97
|
lines.push(` Using bump override: ${project.bumpOverride}`);
|
|
94
98
|
}
|
|
95
|
-
formatProjectUnparseable(lines, project);
|
|
96
|
-
lines.push(dim(` Bumping versions (${releaseType})...`), `
|
|
99
|
+
formatProjectUnparseable(lines, project, style);
|
|
100
|
+
lines.push(dim(` Bumping versions (${releaseType})...`), ` ${formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${currentVersion} → ${bold(newVersion)} (${releaseType})`)}`);
|
|
97
101
|
for (const file of project.bumpedFiles) {
|
|
98
102
|
if (dryRun) {
|
|
99
103
|
lines.push(dim(` [dry-run] Would bump ${file}`));
|
|
@@ -112,9 +116,9 @@ function formatProjectSection(lines, project, dryRun) {
|
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
formatPreviewFiles(lines, project, dryRun, ' ');
|
|
115
|
-
lines.push(`
|
|
119
|
+
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(tag))}`);
|
|
116
120
|
}
|
|
117
|
-
function formatProjectUnparseable(lines, project) {
|
|
121
|
+
function formatProjectUnparseable(lines, project, style) {
|
|
118
122
|
const unparseable = project.unparseableCommits;
|
|
119
123
|
if (unparseable === undefined || unparseable.length === 0) {
|
|
120
124
|
return;
|
|
@@ -122,35 +126,35 @@ function formatProjectUnparseable(lines, project) {
|
|
|
122
126
|
const count = unparseable.length;
|
|
123
127
|
const isPatchFloor = project.parsedCommitCount === 0;
|
|
124
128
|
const suffix = isPatchFloor ? ' (defaulting to patch bump)' : '';
|
|
125
|
-
lines.push(`
|
|
129
|
+
lines.push(` ${formatStatusLine(style, 'warning', `${count} commit${count === 1 ? '' : 's'} could not be parsed${suffix}`)}`);
|
|
126
130
|
for (const commit of unparseable) {
|
|
127
131
|
const shortHash = commit.hash.slice(0, 7);
|
|
128
132
|
const truncatedMessage = commit.message.length > 72 ? `${commit.message.slice(0, 69)}...` : commit.message;
|
|
129
133
|
lines.push(` · ${shortHash} ${truncatedMessage}`);
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
|
-
function formatWorkspaceSection(lines, workspace, dryRun) {
|
|
136
|
+
function formatWorkspaceSection(lines, workspace, dryRun, style) {
|
|
133
137
|
if (workspace.name !== undefined) {
|
|
134
138
|
lines.push(`\n${sectionHeader(workspace.name)}`);
|
|
135
139
|
}
|
|
136
140
|
const since = workspace.previousTag === undefined ? '(no previous release found)' : `since ${workspace.previousTag}`;
|
|
137
141
|
lines.push(dim(` Found ${workspace.commitCount} commits ${since}`));
|
|
138
142
|
if (workspace.status === 'skipped') {
|
|
139
|
-
lines.push(`
|
|
143
|
+
lines.push(` ${formatStatusLine(style, 'skipped', workspace.skipReason)}`);
|
|
140
144
|
return;
|
|
141
145
|
}
|
|
142
146
|
const { propagatedFrom } = workspace;
|
|
143
147
|
const isPropagatedOnly = propagatedFrom !== undefined && workspace.commitCount === 0;
|
|
144
148
|
formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
145
|
-
formatUnparseableWarning(lines, workspace, ' ');
|
|
146
|
-
formatPolicyViolations(lines, workspace.policyViolations, ' ');
|
|
149
|
+
formatUnparseableWarning(lines, workspace, style, ' ');
|
|
150
|
+
formatPolicyViolations(lines, workspace.policyViolations, style, ' ');
|
|
147
151
|
formatBumpLabels(lines, workspace, isPropagatedOnly);
|
|
148
|
-
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
152
|
+
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly, style);
|
|
149
153
|
formatBumpFiles(lines, workspace, dryRun, ' ');
|
|
150
154
|
lines.push(dim(' Generating changelogs...'));
|
|
151
155
|
formatChangelogFiles(lines, workspace, dryRun, ' ');
|
|
152
156
|
formatPreviewFiles(lines, workspace, dryRun, ' ');
|
|
153
|
-
lines.push(`
|
|
157
|
+
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(workspace.tag))}`);
|
|
154
158
|
}
|
|
155
159
|
function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
156
160
|
if (isPropagatedOnly && propagatedFrom !== undefined) {
|
|
@@ -175,13 +179,14 @@ function formatBumpLabels(lines, workspace, isPropagatedOnly) {
|
|
|
175
179
|
lines.push(dim(` Bumping versions (version override)...`));
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
|
-
function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
182
|
+
function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly, style) {
|
|
183
|
+
const versions = `${workspace.currentVersion} → ${bold(workspace.newVersion)}`;
|
|
179
184
|
if (workspace.setVersion !== undefined) {
|
|
180
|
-
lines.push(`
|
|
185
|
+
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${versions} (version override)`)}`);
|
|
181
186
|
}
|
|
182
187
|
else if (workspace.releaseType !== undefined) {
|
|
183
188
|
const suffix = isPropagatedOnly ? formatPropagationSuffix(propagatedFrom) : '';
|
|
184
|
-
lines.push(`
|
|
189
|
+
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${versions} (${workspace.releaseType}${suffix})`)}`);
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
192
|
function formatBumpFiles(lines, workspace, dryRun, indent = '') {
|
|
@@ -215,7 +220,7 @@ function formatPreviewFiles(lines, release, dryRun, indent = '') {
|
|
|
215
220
|
}
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
|
-
function formatUnparseableWarning(lines, workspace, indent = '') {
|
|
223
|
+
function formatUnparseableWarning(lines, workspace, style, indent = '') {
|
|
219
224
|
const unparseable = workspace.unparseableCommits;
|
|
220
225
|
if (unparseable === undefined || unparseable.length === 0) {
|
|
221
226
|
return;
|
|
@@ -223,19 +228,19 @@ function formatUnparseableWarning(lines, workspace, indent = '') {
|
|
|
223
228
|
const count = unparseable.length;
|
|
224
229
|
const isPatchFloor = workspace.parsedCommitCount === 0;
|
|
225
230
|
const suffix = isPatchFloor ? ' (defaulting to patch bump)' : '';
|
|
226
|
-
lines.push(`${indent}
|
|
231
|
+
lines.push(`${indent} ${formatStatusLine(style, 'warning', `${count} commit${count === 1 ? '' : 's'} could not be parsed${suffix}`)}`);
|
|
227
232
|
for (const commit of unparseable) {
|
|
228
233
|
const shortHash = commit.hash.slice(0, 7);
|
|
229
234
|
const truncatedMessage = commit.message.length > 72 ? `${commit.message.slice(0, 69)}...` : commit.message;
|
|
230
235
|
lines.push(`${indent} · ${shortHash} ${truncatedMessage}`);
|
|
231
236
|
}
|
|
232
237
|
}
|
|
233
|
-
function formatPolicyViolations(lines, violations, indent = '') {
|
|
238
|
+
function formatPolicyViolations(lines, violations, style, indent = '') {
|
|
234
239
|
if (violations === undefined || violations.length === 0) {
|
|
235
240
|
return;
|
|
236
241
|
}
|
|
237
242
|
const count = violations.length;
|
|
238
|
-
lines.push(`${indent}
|
|
243
|
+
lines.push(`${indent} ${formatStatusLine(style, 'warning', `${count} policy violation${count === 1 ? '' : 's'}:`)}`);
|
|
239
244
|
for (const violation of violations) {
|
|
240
245
|
const shortHash = violation.commitHash.slice(0, 7);
|
|
241
246
|
const subject = violation.commitSubject;
|
|
@@ -250,14 +255,14 @@ function formatPropagationSuffix(propagatedFrom) {
|
|
|
250
255
|
const names = propagatedFrom.map((p) => p.packageName).join(', ');
|
|
251
256
|
return `, dependency: ${names}`;
|
|
252
257
|
}
|
|
253
|
-
function formatWarnings(lines, result) {
|
|
258
|
+
function formatWarnings(lines, result, style) {
|
|
254
259
|
const { warnings } = result;
|
|
255
260
|
if (warnings === undefined || warnings.length === 0) {
|
|
256
261
|
return;
|
|
257
262
|
}
|
|
258
263
|
lines.push('');
|
|
259
264
|
for (const warning of warnings) {
|
|
260
|
-
lines.push(
|
|
265
|
+
lines.push(formatStatusLine(style, 'warning', warning));
|
|
261
266
|
}
|
|
262
267
|
}
|
|
263
268
|
function formatFormatCommand(lines, result, options) {
|
|
@@ -272,5 +277,6 @@ function formatFormatCommand(lines, result, options) {
|
|
|
272
277
|
lines.push(dim(`\n Running format command: ${result.formatCommand.command}`));
|
|
273
278
|
return;
|
|
274
279
|
}
|
|
275
|
-
|
|
280
|
+
const messageIndent = ' '.repeat(2 + measureGlyphColumn(STATUS_GLYPHS[options.style]) + 1);
|
|
281
|
+
lines.push(`\n ${formatStatusLine(options.style, 'warning', `Format command failed: ${result.formatCommand.command}`)}`, `${messageIndent}${options.formatError}`);
|
|
276
282
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
1
2
|
import type { ReleaseNotesConfig } from './types.js';
|
|
2
3
|
export interface ResolvedReleaseNotesConfig {
|
|
3
4
|
releaseNotes: ReleaseNotesConfig;
|
|
@@ -20,7 +21,7 @@ export interface ResolveReleaseNotesConfigOptions {
|
|
|
20
21
|
* By default, falls back to defaults when `loadConfig()` rejects (legacy publish behavior).
|
|
21
22
|
* When `strictLoad` is `true`, a load failure prints an error and calls `process.exit(1)`.
|
|
22
23
|
*/
|
|
23
|
-
export declare function resolveReleaseNotesConfig(options?: ResolveReleaseNotesConfigOptions): Promise<ResolvedReleaseNotesConfig>;
|
|
24
|
+
export declare function resolveReleaseNotesConfig(stderrStyle: OutputStyle, options?: ResolveReleaseNotesConfigOptions): Promise<ResolvedReleaseNotesConfig>;
|
|
24
25
|
/** Extract section headers in declaration order from a merged workTypes record. */
|
|
25
26
|
export declare function deriveSectionOrder(workTypes: Record<string, {
|
|
26
27
|
header: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { reportError } from '@williamthorsen/nmr-core';
|
|
1
|
+
import { formatStatusLine, printError, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
3
|
import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG } from "./defaults.js";
|
|
4
4
|
import { loadConfig, resolveWorkTypes } from "./loadConfig.js";
|
|
5
5
|
import { validateConfig } from "./validateConfig.js";
|
|
6
|
-
export async function resolveReleaseNotesConfig(options = {}) {
|
|
6
|
+
export async function resolveReleaseNotesConfig(stderrStyle, options = {}) {
|
|
7
7
|
const { strictLoad = false } = options;
|
|
8
8
|
let rawConfig;
|
|
9
9
|
try {
|
|
@@ -28,12 +28,12 @@ export async function resolveReleaseNotesConfig(options = {}) {
|
|
|
28
28
|
if (errors.length > 0) {
|
|
29
29
|
process.stderr.write('Invalid config:\n');
|
|
30
30
|
for (const err of errors) {
|
|
31
|
-
|
|
31
|
+
printError(err, stderrStyle);
|
|
32
32
|
}
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
for (const warning of warnings) {
|
|
36
|
-
console.warn(`
|
|
36
|
+
console.warn(` ${formatStatusLine(stderrStyle, 'warning', warning)}`);
|
|
37
37
|
}
|
|
38
38
|
return {
|
|
39
39
|
releaseNotes: {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
/** The environment variable holding the caller's standing style preference: `auto`, `plain`, or `rich`. */
|
|
3
|
+
export declare const OUTPUT_STYLE_ENV_VAR = "RELEASE_KIT_OUTPUT_STYLE";
|
|
4
|
+
export interface ResolveStylesOptions {
|
|
5
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
|
+
readonly stderrIsTty: boolean;
|
|
7
|
+
readonly stdoutIsTty: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Resolves the style of each output stream, exiting with a usage error when the variable names no setting. */
|
|
10
|
+
export declare function resolveStylesOrExit(options: ResolveStylesOptions): StreamStyles;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describeInvalidOutputStyle, reportError, resolveStreamStyles, } from '@williamthorsen/nmr-core';
|
|
2
|
+
export const OUTPUT_STYLE_ENV_VAR = 'RELEASE_KIT_OUTPUT_STYLE';
|
|
3
|
+
export function resolveStylesOrExit(options) {
|
|
4
|
+
const { invalid, styles } = resolveStreamStyles({ ...options, envVar: OUTPUT_STYLE_ENV_VAR });
|
|
5
|
+
if (invalid !== undefined) {
|
|
6
|
+
reportError(describeInvalidOutputStyle(invalid));
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
return styles;
|
|
10
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
1
2
|
/**
|
|
2
3
|
* Orchestrate the CLI `show-tag-prefixes` command.
|
|
3
4
|
*
|
|
@@ -9,4 +10,4 @@
|
|
|
9
10
|
*
|
|
10
11
|
* @returns The exit code the caller should use.
|
|
11
12
|
*/
|
|
12
|
-
export declare function showTagPrefixesCommand(): Promise<number>;
|
|
13
|
+
export declare function showTagPrefixesCommand(styles: StreamStyles): Promise<number>;
|