@williamthorsen/release-kit 16.1.1 → 17.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 +79 -0
- package/.readyup/kits/default.js +2 -2
- package/.readyup/kits/npm-auto-publish.js +1 -1
- package/.readyup/manifest.json +204 -16
- package/CHANGELOG.md +28 -0
- package/README.md +7 -2
- package/bin/release-kit.js +1 -1
- package/dist/esm/assertCleanWorkingTree.js +14 -7
- package/dist/esm/assertReleasesOnNpm.js +11 -3
- package/dist/esm/assertTaggedBaseline.js +2 -1
- package/dist/esm/assertTarballsComplete.js +16 -5
- package/dist/esm/bin.d.ts +2 -0
- package/dist/esm/bin.js +1 -0
- package/dist/esm/buildChangelogEntries.js +2 -2
- package/dist/esm/buildDependencyGraph.js +5 -4
- package/dist/esm/bumpVersion.js +3 -2
- package/dist/esm/changelogJsonUtils.js +2 -1
- package/dist/esm/changelogOverrides.js +3 -2
- package/dist/esm/cli/commandKit.d.ts +18 -0
- package/dist/esm/cli/commandKit.js +27 -0
- package/dist/esm/cli/maintenanceCommands.d.ts +6 -0
- package/dist/esm/cli/maintenanceCommands.js +129 -0
- package/dist/esm/cli/releaseCommands.d.ts +7 -0
- package/dist/esm/cli/releaseCommands.js +156 -0
- package/dist/esm/cli.js +9 -237
- package/dist/esm/commitCommand.d.ts +4 -1
- package/dist/esm/commitCommand.js +12 -10
- package/dist/esm/compareVersions.js +2 -1
- package/dist/esm/createGithubRelease.js +7 -1
- package/dist/esm/createGithubReleaseCommand.d.ts +10 -4
- package/dist/esm/createGithubReleaseCommand.js +4 -18
- package/dist/esm/createTags.js +6 -4
- package/dist/esm/deleteFileIfExists.js +2 -1
- package/dist/esm/deriveDirectoryConfig.js +7 -7
- package/dist/esm/deriveWorkspaceConfig.js +4 -4
- package/dist/esm/detectPackageManager.js +1 -1
- package/dist/esm/enterRepoRoot.js +2 -1
- package/dist/esm/enumerateReleaseWindows.js +2 -2
- package/dist/esm/findMissingManifestPaths.js +1 -1
- package/dist/esm/hasPrettierConfig.js +1 -1
- package/dist/esm/init/initCommand.js +10 -1
- package/dist/esm/init/parseJsonRecord.js +1 -1
- package/dist/esm/init/updateTemplatesCommand.js +10 -1
- package/dist/esm/isPublishableManifest.js +1 -1
- package/dist/esm/loadChangelogOverrides.js +1 -1
- package/dist/esm/loadConfig.js +6 -6
- package/dist/esm/loadValidatedConfig.d.ts +6 -1
- package/dist/esm/loadValidatedConfig.js +11 -3
- package/dist/esm/mergeConfig.js +5 -4
- package/dist/esm/monorepoConfigAssertions.js +6 -5
- package/dist/esm/npmRegistry.js +1 -1
- package/dist/esm/packFromCleanCheckout.js +12 -4
- package/dist/esm/parseChangeRecordBlock.js +1 -1
- package/dist/esm/planVersionBump.js +6 -5
- package/dist/esm/prepareArgs.d.ts +1 -49
- package/dist/esm/prepareArgs.js +4 -51
- package/dist/esm/prepareCommand.d.ts +15 -3
- package/dist/esm/prepareCommand.js +26 -44
- package/dist/esm/prepareComposite.js +2 -1
- package/dist/esm/prepareRunContext.js +2 -2
- package/dist/esm/previewTagPrefixes.js +2 -2
- package/dist/esm/publish.js +7 -1
- package/dist/esm/publishCommand.d.ts +12 -3
- package/dist/esm/publishCommand.js +4 -33
- package/dist/esm/pushCommand.d.ts +12 -4
- package/dist/esm/pushCommand.js +6 -27
- package/dist/esm/pushRelease.js +18 -6
- package/dist/esm/releaseFiles.js +2 -2
- package/dist/esm/releasePlan.js +2 -2
- package/dist/esm/resolveCommandTags.d.ts +4 -4
- package/dist/esm/resolveCommandTags.js +8 -12
- package/dist/esm/resolveReleaseDeclaration.js +9 -8
- package/dist/esm/resolveReleaseTags.js +11 -4
- package/dist/esm/resolveReleaseTargets.js +2 -1
- package/dist/esm/{resolveStylesOrExit.d.ts → resolveStyles.d.ts} +2 -2
- package/dist/esm/resolveStyles.js +10 -0
- package/dist/esm/route.d.ts +2 -0
- package/dist/esm/route.js +39 -0
- package/dist/esm/showTagPrefixesCommand.js +6 -3
- package/dist/esm/sync-labels/generateCommand.js +11 -7
- package/dist/esm/sync-labels/initCommand.js +7 -1
- package/dist/esm/sync-labels/presets.js +7 -8
- package/dist/esm/sync-labels/resolveLabels.js +4 -3
- package/dist/esm/sync-labels/retiredConfig.js +2 -1
- package/dist/esm/sync-labels/syncCommand.js +4 -3
- package/dist/esm/sync-labels/templates.js +2 -2
- package/dist/esm/tagCommand.d.ts +8 -3
- package/dist/esm/tagCommand.js +2 -15
- package/dist/esm/typeGuards.d.ts +0 -2
- package/dist/esm/typeGuards.js +0 -3
- package/dist/esm/user-error-chaining.d.ts +13 -0
- package/dist/esm/user-error-chaining.js +11 -0
- package/dist/esm/validateConfig.js +1 -1
- package/dist/esm/validateOverridesCommand.js +12 -5
- package/docs/configuration.md +1 -1
- package/docs/editorial-overrides.md +2 -0
- package/docs/labels.md +2 -0
- package/docs/releasing.md +1 -1
- package/package.json +3 -3
- package/dist/esm/configFlagSchema.d.ts +0 -12
- package/dist/esm/configFlagSchema.js +0 -3
- package/dist/esm/help/initHelp.d.ts +0 -4
- package/dist/esm/help/initHelp.js +0 -31
- package/dist/esm/help/overridesHelp.d.ts +0 -4
- package/dist/esm/help/overridesHelp.js +0 -32
- package/dist/esm/help/prepareHelp.d.ts +0 -7
- package/dist/esm/help/prepareHelp.js +0 -30
- package/dist/esm/help/releaseStepsHelp.d.ts +0 -12
- package/dist/esm/help/releaseStepsHelp.js +0 -91
- package/dist/esm/help/syncLabelsHelp.d.ts +0 -8
- package/dist/esm/help/syncLabelsHelp.js +0 -55
- package/dist/esm/help/usage.d.ts +0 -2
- package/dist/esm/help/usage.js +0 -29
- package/dist/esm/resolveConfigFlag.d.ts +0 -7
- package/dist/esm/resolveConfigFlag.js +0 -4
- package/dist/esm/resolveStylesOrExit.js +0 -10
|
@@ -2,8 +2,9 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
+
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
5
6
|
import { resolveExecutable } from "./detectPackageManager.js";
|
|
6
|
-
import {
|
|
7
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
7
8
|
export function packFromCleanCheckout(workspacePaths, packageManager) {
|
|
8
9
|
const repoRoot = runStep('find the repository root', 'git', ['rev-parse', '--show-toplevel'], process.cwd()).trim();
|
|
9
10
|
const headSha = runStep('resolve HEAD', 'git', ['rev-parse', 'HEAD'], repoRoot).trim();
|
|
@@ -51,7 +52,14 @@ const PACKAGE_MANAGER_COMMANDS = {
|
|
|
51
52
|
},
|
|
52
53
|
};
|
|
53
54
|
function declaresPrepublishOnly(packageDir) {
|
|
54
|
-
const
|
|
55
|
+
const manifestPath = join(packageDir, 'package.json');
|
|
56
|
+
let manifest;
|
|
57
|
+
try {
|
|
58
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw chainUserError(`Failed to parse ${manifestPath}`, error);
|
|
62
|
+
}
|
|
55
63
|
const scripts = isRecord(manifest) ? manifest['scripts'] : undefined;
|
|
56
64
|
return isRecord(scripts) && typeof scripts['prepublishOnly'] === 'string';
|
|
57
65
|
}
|
|
@@ -59,7 +67,7 @@ function findTarball(packDir, workspacePath) {
|
|
|
59
67
|
const tarballs = readdirSync(packDir).filter((name) => name.endsWith('.tgz'));
|
|
60
68
|
const [tarball] = tarballs;
|
|
61
69
|
if (tarballs.length !== 1 || tarball === undefined) {
|
|
62
|
-
throw new
|
|
70
|
+
throw new UserError(`Could not pack ${workspacePath}: expected one tarball, found ${tarballs.length}.`);
|
|
63
71
|
}
|
|
64
72
|
return join(packDir, tarball);
|
|
65
73
|
}
|
|
@@ -77,5 +85,5 @@ function runStep(step, command, args, cwd) {
|
|
|
77
85
|
}
|
|
78
86
|
const outcome = result.error === undefined ? `exited with status ${result.status}` : result.error.message;
|
|
79
87
|
const output = [result.stdout, result.stderr].filter((text) => text !== '').join('\n');
|
|
80
|
-
throw new
|
|
88
|
+
throw new UserError(`Could not ${step}: \`${[command, ...args].join(' ')}\` ${outcome}.${output === '' ? '' : `\n${output}`}`);
|
|
81
89
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
1
2
|
import { parseDocument } from 'yaml';
|
|
2
|
-
import { isRecord } from "./typeGuards.js";
|
|
3
3
|
export function parseChangeRecordBlock(message) {
|
|
4
4
|
const lines = message.split(/\r?\n/);
|
|
5
5
|
const fence = findFences(lines).at(-1);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { bumpVersion } from "./bumpVersion.js";
|
|
4
4
|
import { isForwardVersion } from "./compareVersions.js";
|
|
5
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
5
6
|
function isPackageJson(value) {
|
|
6
7
|
return typeof value === 'object' && value !== null && 'version' in value && typeof value.version === 'string';
|
|
7
8
|
}
|
|
@@ -19,7 +20,7 @@ export function planVersionSet(packageFiles, newVersion) {
|
|
|
19
20
|
const { firstFile, firstPackageJson } = readPrimaryPackage(packageFiles);
|
|
20
21
|
const currentVersion = firstPackageJson.version;
|
|
21
22
|
if (!isForwardVersion(currentVersion, newVersion)) {
|
|
22
|
-
throw new
|
|
23
|
+
throw new UserError(`--set-version ${newVersion} is not greater than current version ${currentVersion}`);
|
|
23
24
|
}
|
|
24
25
|
return {
|
|
25
26
|
currentVersion,
|
|
@@ -50,17 +51,17 @@ function readPackageJson(filePath) {
|
|
|
50
51
|
content = readFileSync(filePath, 'utf8');
|
|
51
52
|
}
|
|
52
53
|
catch (error) {
|
|
53
|
-
throw
|
|
54
|
+
throw chainUserError(`Failed to read ${filePath}`, error);
|
|
54
55
|
}
|
|
55
56
|
let parsedJson;
|
|
56
57
|
try {
|
|
57
58
|
parsedJson = JSON.parse(content);
|
|
58
59
|
}
|
|
59
60
|
catch (error) {
|
|
60
|
-
throw
|
|
61
|
+
throw chainUserError(`Failed to parse JSON in ${filePath}`, error);
|
|
61
62
|
}
|
|
62
63
|
if (!isPackageJson(parsedJson)) {
|
|
63
|
-
throw new
|
|
64
|
+
throw new UserError(`No valid 'version' field found in ${filePath}`);
|
|
64
65
|
}
|
|
65
66
|
return parsedJson;
|
|
66
67
|
}
|
|
@@ -1,55 +1,7 @@
|
|
|
1
|
-
import type { ReleaseType } from './types.js';
|
|
2
1
|
/** Canonical `N.N.N` semver pattern for validating `--set-version` input. */
|
|
3
2
|
export declare const CANONICAL_SEMVER_PATTERN: RegExp;
|
|
4
|
-
export declare const prepareFlagSchema: {
|
|
5
|
-
isDryRun: {
|
|
6
|
-
long: string;
|
|
7
|
-
type: "boolean";
|
|
8
|
-
};
|
|
9
|
-
shouldForce: {
|
|
10
|
-
long: string;
|
|
11
|
-
type: "boolean";
|
|
12
|
-
};
|
|
13
|
-
shouldSkipGitChecks: {
|
|
14
|
-
long: string;
|
|
15
|
-
type: "boolean";
|
|
16
|
-
short: string;
|
|
17
|
-
};
|
|
18
|
-
bump: {
|
|
19
|
-
long: string;
|
|
20
|
-
type: "string";
|
|
21
|
-
};
|
|
22
|
-
setVersion: {
|
|
23
|
-
long: string;
|
|
24
|
-
type: "string";
|
|
25
|
-
};
|
|
26
|
-
only: {
|
|
27
|
-
long: string;
|
|
28
|
-
type: "string";
|
|
29
|
-
};
|
|
30
|
-
shouldIncludeReleaseNotes: {
|
|
31
|
-
long: string;
|
|
32
|
-
type: "boolean";
|
|
33
|
-
};
|
|
34
|
-
config: {
|
|
35
|
-
long: string;
|
|
36
|
-
type: "string";
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
/** Parses CLI arguments into structured options. Prints a usage error and exits on invalid input. */
|
|
40
|
-
export declare function parseArgs(argv: string[]): {
|
|
41
|
-
configPath: string | undefined;
|
|
42
|
-
isDryRun: boolean;
|
|
43
|
-
shouldForce: boolean;
|
|
44
|
-
shouldSkipGitChecks: boolean;
|
|
45
|
-
bumpOverride: ReleaseType | undefined;
|
|
46
|
-
onlyNames: string[] | undefined;
|
|
47
|
-
/** The raw `--set-version` value, whose accepted form depends on the mode. */
|
|
48
|
-
setVersion: string | undefined;
|
|
49
|
-
shouldIncludeReleaseNotes: boolean;
|
|
50
|
-
};
|
|
51
3
|
/**
|
|
52
4
|
* Parses a monorepo `--set-version` value, a comma-separated list of `<target>@N.N.N` entries, into versions keyed by
|
|
53
|
-
* target name.
|
|
5
|
+
* target name. Throws a `UsageError` for a malformed entry, an unknown target, or a repeated one.
|
|
54
6
|
*/
|
|
55
7
|
export declare function parseSetVersions(value: string, knownNames: readonly string[]): Map<string, string>;
|
package/dist/esm/prepareArgs.js
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
3
|
-
const VALID_BUMP_TYPES = ['major', 'minor', 'patch'];
|
|
1
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
4
2
|
export const CANONICAL_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
5
|
-
export const prepareFlagSchema = {
|
|
6
|
-
...configFlagSchema,
|
|
7
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
8
|
-
shouldForce: { long: '--force', type: 'boolean' },
|
|
9
|
-
shouldSkipGitChecks: {
|
|
10
|
-
long: '--no-git-checks',
|
|
11
|
-
type: 'boolean',
|
|
12
|
-
short: '-n',
|
|
13
|
-
},
|
|
14
|
-
bump: { long: '--bump', type: 'string' },
|
|
15
|
-
setVersion: { long: '--set-version', type: 'string' },
|
|
16
|
-
only: { long: '--only', type: 'string' },
|
|
17
|
-
shouldIncludeReleaseNotes: { long: '--with-release-notes', type: 'boolean' },
|
|
18
|
-
};
|
|
19
|
-
export function parseArgs(argv) {
|
|
20
|
-
const { flags } = parseArgsOrExit(argv, prepareFlagSchema);
|
|
21
|
-
let bumpOverride;
|
|
22
|
-
if (flags.bump !== undefined) {
|
|
23
|
-
if (!isReleaseType(flags.bump)) {
|
|
24
|
-
reportError(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(', ')}`);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
bumpOverride = flags.bump;
|
|
28
|
-
}
|
|
29
|
-
let onlyNames;
|
|
30
|
-
if (flags.only !== undefined) {
|
|
31
|
-
onlyNames = flags.only.split(',');
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
configPath: flags.config,
|
|
35
|
-
isDryRun: flags.isDryRun,
|
|
36
|
-
shouldForce: flags.shouldForce,
|
|
37
|
-
shouldSkipGitChecks: flags.shouldSkipGitChecks,
|
|
38
|
-
bumpOverride,
|
|
39
|
-
onlyNames,
|
|
40
|
-
setVersion: flags.setVersion,
|
|
41
|
-
shouldIncludeReleaseNotes: flags.shouldIncludeReleaseNotes,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
3
|
export function parseSetVersions(value, knownNames) {
|
|
45
4
|
const setVersions = new Map();
|
|
46
5
|
for (const entry of value.split(',')) {
|
|
@@ -48,22 +7,16 @@ export function parseSetVersions(value, knownNames) {
|
|
|
48
7
|
const name = entry.slice(0, Math.max(0, separatorIndex));
|
|
49
8
|
const version = entry.slice(separatorIndex + 1);
|
|
50
9
|
if (separatorIndex <= 0 || !CANONICAL_SEMVER_PATTERN.test(version)) {
|
|
51
|
-
|
|
10
|
+
throw new UsageError(`Invalid --set-version entry "${entry}". In monorepo mode, each entry must be <target>@N.N.N ` +
|
|
52
11
|
'(canonical semver, no pre-release suffix), e.g. --set-version=arrays@1.0.0.');
|
|
53
|
-
process.exit(1);
|
|
54
12
|
}
|
|
55
13
|
if (!knownNames.includes(name)) {
|
|
56
|
-
|
|
57
|
-
process.exit(1);
|
|
14
|
+
throw new UsageError(`Unknown release target "${name}" in --set-version. Known targets: ${knownNames.join(', ')}`);
|
|
58
15
|
}
|
|
59
16
|
if (setVersions.has(name)) {
|
|
60
|
-
|
|
61
|
-
process.exit(1);
|
|
17
|
+
throw new UsageError(`--set-version names target "${name}" more than once`);
|
|
62
18
|
}
|
|
63
19
|
setVersions.set(name, version);
|
|
64
20
|
}
|
|
65
21
|
return setVersions;
|
|
66
22
|
}
|
|
67
|
-
function isReleaseType(value) {
|
|
68
|
-
return VALID_BUMP_TYPES.includes(value);
|
|
69
|
-
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
import type { ReleaseType } from './types.js';
|
|
3
|
+
export interface PrepareCommandOptions {
|
|
4
|
+
bumpOverride: ReleaseType | undefined;
|
|
5
|
+
configPath: string | undefined;
|
|
6
|
+
isDryRun: boolean;
|
|
7
|
+
/** The release targets that `--only` names, or `undefined` to release every target that has changes. */
|
|
8
|
+
onlyNames: string[] | undefined;
|
|
9
|
+
/** The raw `--set-version` value, whose accepted form depends on the mode. */
|
|
10
|
+
setVersion: string | undefined;
|
|
11
|
+
shouldForce: boolean;
|
|
12
|
+
shouldIncludeReleaseNotes: boolean;
|
|
13
|
+
shouldSkipGitChecks: boolean;
|
|
14
|
+
styles: StreamStyles;
|
|
15
|
+
}
|
|
2
16
|
/**
|
|
3
17
|
* Orchestrates the CLI `prepare` command: checks the working tree, loads and validates the config, discovers the
|
|
4
18
|
* workspaces, and prepares the release over the targets of the repo shape.
|
|
5
|
-
*
|
|
6
|
-
* A relative `--config` resolves against `invocationDir`.
|
|
7
19
|
*/
|
|
8
|
-
export declare function prepareCommand(
|
|
20
|
+
export declare function prepareCommand({ bumpOverride, configPath, isDryRun, onlyNames, setVersion, shouldForce, shouldIncludeReleaseNotes, shouldSkipGitChecks, styles, }: PrepareCommandOptions): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { formatGlyphLine,
|
|
2
|
+
import { formatGlyphLine, isAnticipatedFailure, UserError, } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
3
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
5
6
|
import { assertReleasesOnNpm } from "./assertReleasesOnNpm.js";
|
|
@@ -7,16 +8,16 @@ import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
|
7
8
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
8
9
|
import { dim } from "./format.js";
|
|
9
10
|
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
10
|
-
import {
|
|
11
|
-
import { CANONICAL_SEMVER_PATTERN,
|
|
11
|
+
import { ConfigProblemError, loadValidatedConfig, reportConfigWarnings } from "./loadValidatedConfig.js";
|
|
12
|
+
import { CANONICAL_SEMVER_PATTERN, parseSetVersions } from "./prepareArgs.js";
|
|
12
13
|
import { prepareReleaseTargets } from "./prepareReleaseTargets.js";
|
|
13
14
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
|
|
14
15
|
import { applyReleasePlan } from "./releasePlan.js";
|
|
15
16
|
import { reportPrepare } from "./reportPrepare.js";
|
|
16
|
-
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
17
17
|
import { isSinglePackageTargets, PROJECT_TARGET_ID, resolveReleaseTargets, SINGLE_PACKAGE_TARGET_ID, } from "./resolveReleaseTargets.js";
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import { prefixUserError } from "./user-error-chaining.js";
|
|
19
|
+
const UNCHANGED_TREE_NOTE = 'The command did not write any files; the working tree is unchanged.';
|
|
20
|
+
export async function prepareCommand({ bumpOverride, configPath, isDryRun, onlyNames, setVersion, shouldForce, shouldIncludeReleaseNotes, shouldSkipGitChecks, styles, }) {
|
|
20
21
|
const options = {
|
|
21
22
|
shouldForce,
|
|
22
23
|
...(bumpOverride !== undefined && { bumpOverride }),
|
|
@@ -26,18 +27,11 @@ export async function prepareCommand(argv, styles, invocationDir) {
|
|
|
26
27
|
console.info(`\n${formatGlyphLine(RELEASE_GLYPHS, styles.stdout, 'dryRun', 'DRY RUN: release-kit will not modify any files')}\n`);
|
|
27
28
|
}
|
|
28
29
|
if (!isDryRun && !shouldSkipGitChecks) {
|
|
29
|
-
|
|
30
|
-
assertCleanWorkingTree();
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
reportError(describeError(error));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
30
|
+
assertCleanWorkingTree();
|
|
36
31
|
}
|
|
37
|
-
const configResult = await loadValidatedConfig(
|
|
32
|
+
const configResult = await loadValidatedConfig(configPath);
|
|
38
33
|
if (configResult.status === 'invalid') {
|
|
39
|
-
|
|
40
|
-
process.exit(1);
|
|
34
|
+
throw new ConfigProblemError(configResult.problem);
|
|
41
35
|
}
|
|
42
36
|
let userConfig;
|
|
43
37
|
if (configResult.status === 'ok') {
|
|
@@ -49,14 +43,12 @@ export async function prepareCommand(argv, styles, invocationDir) {
|
|
|
49
43
|
workspaceDiscovery = discoverWorkspaces();
|
|
50
44
|
}
|
|
51
45
|
catch (error) {
|
|
52
|
-
|
|
53
|
-
process.exit(1);
|
|
46
|
+
throw prefixUserError('Failed to discover workspaces', error);
|
|
54
47
|
}
|
|
55
48
|
if (workspaceDiscovery.kind === 'empty') {
|
|
56
|
-
|
|
57
|
-
process.exit(1);
|
|
49
|
+
throw new UserError(`No workspace package to release. ${describeEmptyWorkspace(workspaceDiscovery)}`);
|
|
58
50
|
}
|
|
59
|
-
const config =
|
|
51
|
+
const config = resolveSelectedTargets(userConfig, workspaceDiscovery, onlyNames, setVersion);
|
|
60
52
|
const targetOptions = {
|
|
61
53
|
...options,
|
|
62
54
|
...(onlyNames !== undefined && { onlyTargetIds: onlyNames }),
|
|
@@ -74,15 +66,13 @@ function findReleasedPackagePaths(plan, targets) {
|
|
|
74
66
|
const releasedIds = new Set(plan.targets.flatMap((result) => result.status === 'released' && result.kind === 'package' ? result.targetId : []));
|
|
75
67
|
return targets.flatMap((target) => target.kind === 'package' && releasedIds.has(target.id) ? target.packagePath : []);
|
|
76
68
|
}
|
|
77
|
-
function
|
|
69
|
+
function resolveSelectedTargets(userConfig, discovery, onlyNames, setVersion) {
|
|
78
70
|
if (discovery.kind === 'single-package') {
|
|
79
71
|
if (onlyNames !== undefined) {
|
|
80
|
-
|
|
81
|
-
process.exit(1);
|
|
72
|
+
throw new UsageError('--only is only supported for monorepo configurations');
|
|
82
73
|
}
|
|
83
74
|
if (setVersion !== undefined && !CANONICAL_SEMVER_PATTERN.test(setVersion)) {
|
|
84
|
-
|
|
85
|
-
process.exit(1);
|
|
75
|
+
throw new UsageError(`Invalid --set-version value "${setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`);
|
|
86
76
|
}
|
|
87
77
|
return resolveReleaseTargets(userConfig, discovery);
|
|
88
78
|
}
|
|
@@ -91,14 +81,12 @@ function resolveTargetsOrExit(userConfig, discovery, onlyNames, setVersion) {
|
|
|
91
81
|
config = resolveReleaseTargets(userConfig, discovery);
|
|
92
82
|
}
|
|
93
83
|
catch (error) {
|
|
94
|
-
|
|
95
|
-
process.exit(1);
|
|
84
|
+
throw prefixUserError('Failed to resolve workspaces', error);
|
|
96
85
|
}
|
|
97
86
|
if (setVersion !== undefined && config.targets.some((target) => target.kind === 'composite')) {
|
|
98
|
-
|
|
87
|
+
throw new UsageError('--set-version cannot be combined with a project release. ' +
|
|
99
88
|
"--set-version sets individual workspaces' versions; a project release rolls up every " +
|
|
100
89
|
'contributing workspace. To use --set-version, run on a config without a `project` block or a `releases` composite.');
|
|
101
|
-
process.exit(1);
|
|
102
90
|
}
|
|
103
91
|
const knownNames = listSelectableIds(config.targets);
|
|
104
92
|
const requestedNames = onlyNames ?? [];
|
|
@@ -106,8 +94,7 @@ function resolveTargetsOrExit(userConfig, discovery, onlyNames, setVersion) {
|
|
|
106
94
|
if (knownNames.includes(name)) {
|
|
107
95
|
continue;
|
|
108
96
|
}
|
|
109
|
-
|
|
110
|
-
process.exit(1);
|
|
97
|
+
throw new UsageError(`Unknown release target "${name}". Known targets: ${knownNames.join(', ')}`);
|
|
111
98
|
}
|
|
112
99
|
return config;
|
|
113
100
|
}
|
|
@@ -129,31 +116,26 @@ function runAndReport(computePlan, isDryRun, style) {
|
|
|
129
116
|
plan = computePlan();
|
|
130
117
|
}
|
|
131
118
|
catch (error) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
119
|
+
if (isAnticipatedFailure(error)) {
|
|
120
|
+
throw new UserError(`${error.message}\n${UNCHANGED_TREE_NOTE}`, { cause: error });
|
|
121
|
+
}
|
|
122
|
+
process.stderr.write(`${UNCHANGED_TREE_NOTE}\n`);
|
|
123
|
+
throw error;
|
|
135
124
|
}
|
|
136
125
|
if (isDryRun) {
|
|
137
126
|
process.stdout.write(reportPrepare(plan, { applied: false, style }) + '\n');
|
|
138
127
|
reportPlanFiles(plan, true);
|
|
139
128
|
return;
|
|
140
129
|
}
|
|
141
|
-
|
|
142
|
-
applyReleasePlan(plan);
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
reportError(describeError(error));
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
130
|
+
applyReleasePlan(plan);
|
|
148
131
|
const formatError = runFormatCommand(plan.formatCommand);
|
|
149
132
|
process.stdout.write(reportPrepare(plan, { applied: true, style, ...(formatError !== undefined && { formatError }) }) + '\n');
|
|
150
133
|
reportPlanFiles(plan, false);
|
|
151
134
|
if (formatError === undefined) {
|
|
152
135
|
return;
|
|
153
136
|
}
|
|
154
|
-
|
|
137
|
+
throw new UserError(`The format command failed, but the release is prepared and ${RELEASE_TAGS_FILE} is written. ` +
|
|
155
138
|
`Format the release files and run 'release-kit commit'.`);
|
|
156
|
-
process.exit(1);
|
|
157
139
|
}
|
|
158
140
|
function runFormatCommand(formatCommand) {
|
|
159
141
|
if (formatCommand === undefined) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
1
2
|
import { assertTaggedBaseline } from "./assertTaggedBaseline.js";
|
|
2
3
|
import { collect } from "./prepareRunContext.js";
|
|
3
4
|
import { buildSkippedTargetResult, decideTargetRelease, planTargetRelease, readTargetHistory, } from "./prepareTarget.js";
|
|
@@ -24,7 +25,7 @@ export function prepareComposite(compositeTarget, context, options, collectedRel
|
|
|
24
25
|
return buildSkippedTargetResult(compositeTarget, history, decision.skipReason);
|
|
25
26
|
}
|
|
26
27
|
if (decision.setVersion !== undefined) {
|
|
27
|
-
throw new
|
|
28
|
+
throw new UserError('A composite target cannot take --set-version');
|
|
28
29
|
}
|
|
29
30
|
const plannedRelease = planTargetRelease({
|
|
30
31
|
...context,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
2
1
|
import { isRepoRoot } from "./prepareTarget.js";
|
|
2
|
+
import { prefixUserError } from "./user-error-chaining.js";
|
|
3
3
|
export function collect(collectedReleases, plannedRelease) {
|
|
4
4
|
collectedReleases.tags.push(plannedRelease.tag);
|
|
5
5
|
collectedReleases.writes.push(...plannedRelease.writes);
|
|
@@ -34,6 +34,6 @@ export function runStage(stage, fn) {
|
|
|
34
34
|
return fn();
|
|
35
35
|
}
|
|
36
36
|
catch (error) {
|
|
37
|
-
throw
|
|
37
|
+
throw prefixUserError(stage, error);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
|
-
import { GIT_OUTPUT_LIMIT_BYTES } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { GIT_OUTPUT_LIMIT_BYTES, UserError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
5
|
import { DEFAULT_PROJECT_TAG_PREFIX } from "./defaults.js";
|
|
6
6
|
import { deriveDirectoryConfig } from "./deriveDirectoryConfig.js";
|
|
@@ -12,7 +12,7 @@ import { normalizeWorkspacePath } from "./resolveReleaseDeclaration.js";
|
|
|
12
12
|
export function previewTagPrefixes(config) {
|
|
13
13
|
const workspace = discoverWorkspaces();
|
|
14
14
|
if (workspace.kind === 'empty') {
|
|
15
|
-
throw new
|
|
15
|
+
throw new UserError(`No workspace package to preview. ${describeEmptyWorkspace(workspace)}`);
|
|
16
16
|
}
|
|
17
17
|
const workspacePaths = workspace.kind === 'packages' ? workspace.packageDirs : [];
|
|
18
18
|
const overridesByPath = buildOverrideMap(config, workspacePaths);
|
package/dist/esm/publish.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { resolveExecutable } from "./detectPackageManager.js";
|
|
3
|
+
import { asUserError } from "./user-error-chaining.js";
|
|
3
4
|
export function publishPackage(resolvedTag, packageManager, options) {
|
|
4
5
|
const { isDryRun, shouldGenerateProvenance } = options;
|
|
5
6
|
const executable = resolveExecutable(packageManager);
|
|
6
7
|
const args = buildPublishArgs(packageManager, { isDryRun, shouldGenerateProvenance });
|
|
7
8
|
console.info(`\n${isDryRun ? '[dry-run] ' : ''}Running: ${executable} ${args.join(' ')} (cwd: ${resolvedTag.workspacePath})`);
|
|
8
|
-
|
|
9
|
+
try {
|
|
10
|
+
execFileSync(executable, args, { cwd: resolvedTag.workspacePath, stdio: 'inherit' });
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw asUserError(error);
|
|
14
|
+
}
|
|
9
15
|
}
|
|
10
16
|
function buildPublishArgs(packageManager, options) {
|
|
11
17
|
const args = packageManager === 'yarn-berry' ? ['npm', 'publish'] : ['publish'];
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface PublishCommandOptions {
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
isDryRun: boolean;
|
|
5
|
+
shouldGenerateProvenance: boolean;
|
|
6
|
+
shouldSkipGitChecks: boolean;
|
|
7
|
+
/** The tag names that `--tags` selects, or `undefined` to publish every publishable release tag on HEAD. */
|
|
8
|
+
tags: string[] | undefined;
|
|
9
|
+
styles: StreamStyles;
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
12
|
* Orchestrates the CLI `publish` command: checks for a clean working tree, resolves the tags on HEAD, checks the
|
|
4
13
|
* tarballs of the publishable ones from a clean checkout, and publishes each one, injecting release notes into its
|
|
5
|
-
* README for the duration of the publish.
|
|
14
|
+
* README for the duration of the publish.
|
|
6
15
|
*/
|
|
7
|
-
export declare function publishCommand(
|
|
16
|
+
export declare function publishCommand({ configPath, isDryRun, shouldGenerateProvenance, shouldSkipGitChecks, tags: requestedTags, styles, }: PublishCommandOptions): Promise<void>;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
4
|
-
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
3
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
6
4
|
import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
7
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
8
5
|
import { deriveReleaseNotesConfig } from "./deriveReleaseNotesConfig.js";
|
|
9
6
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
10
7
|
import { formatDirectoryPublishSkip } from "./formatDirectoryPublishSkip.js";
|
|
@@ -12,32 +9,13 @@ import { formatPrivateSkip } from "./formatPrivateSkip.js";
|
|
|
12
9
|
import { formatProjectPublishSkip } from "./formatProjectPublishSkip.js";
|
|
13
10
|
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
14
11
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
15
|
-
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
16
12
|
import { publishPackage } from "./publish.js";
|
|
17
13
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
18
|
-
|
|
19
|
-
const publishFlagSchema = {
|
|
20
|
-
...configFlagSchema,
|
|
21
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
22
|
-
shouldSkipGitChecks: { long: '--no-git-checks', type: 'boolean' },
|
|
23
|
-
shouldGenerateProvenance: { long: '--provenance', type: 'boolean' },
|
|
24
|
-
tags: { long: '--tags', type: 'string' },
|
|
25
|
-
};
|
|
26
|
-
export async function publishCommand(argv, styles, invocationDir) {
|
|
27
|
-
const parsedArgs = parseArgsOrExit(argv, publishFlagSchema);
|
|
28
|
-
const configPath = resolveConfigFlag(parsedArgs.flags.config, invocationDir);
|
|
29
|
-
const { isDryRun, shouldSkipGitChecks, shouldGenerateProvenance } = parsedArgs.flags;
|
|
14
|
+
export async function publishCommand({ configPath, isDryRun, shouldGenerateProvenance, shouldSkipGitChecks, tags: requestedTags, styles, }) {
|
|
30
15
|
const userConfig = await loadUsableConfig(styles.stderr, configPath);
|
|
31
16
|
if (!isDryRun && !shouldSkipGitChecks) {
|
|
32
|
-
|
|
33
|
-
assertCleanWorkingTree();
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
reportError(describeError(error));
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
17
|
+
assertCleanWorkingTree();
|
|
39
18
|
}
|
|
40
|
-
const requestedTags = parseRequestedTags(parsedArgs.flags.tags);
|
|
41
19
|
const resolvedTags = resolveCommandTags(requestedTags, userConfig);
|
|
42
20
|
if (resolvedTags.length === 0) {
|
|
43
21
|
return;
|
|
@@ -47,13 +25,7 @@ export async function publishCommand(argv, styles, invocationDir) {
|
|
|
47
25
|
console.info('Nothing to publish.');
|
|
48
26
|
return;
|
|
49
27
|
}
|
|
50
|
-
|
|
51
|
-
assertTarballsComplete(publishableTags.map(({ workspacePath }) => workspacePath), userConfig?.devExportConditions ?? []);
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
reportError(describeError(error));
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
28
|
+
assertTarballsComplete(publishableTags.map(({ workspacePath }) => workspacePath), userConfig?.devExportConditions ?? []);
|
|
57
29
|
const packageManager = detectPackageManager();
|
|
58
30
|
const { releaseNotes, changelogJsonOutputPath, sectionOrder } = deriveReleaseNotesConfig(userConfig);
|
|
59
31
|
const shouldInject = releaseNotes.shouldInjectIntoReadme;
|
|
@@ -90,8 +62,7 @@ export async function publishCommand(argv, styles, invocationDir) {
|
|
|
90
62
|
console.warn(` ${tag}`);
|
|
91
63
|
}
|
|
92
64
|
}
|
|
93
|
-
|
|
94
|
-
process.exit(1);
|
|
65
|
+
throw error;
|
|
95
66
|
}
|
|
96
67
|
}
|
|
97
68
|
function filterPublishableTags(resolvedTags, isExplicit) {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface PushCommandOptions {
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
isDryRun: boolean;
|
|
5
|
+
/** The tag names that `--tags` selects, or `undefined` to push every release tag on HEAD. */
|
|
6
|
+
tags: string[] | undefined;
|
|
7
|
+
tagsOnly: boolean;
|
|
8
|
+
styles: StreamStyles;
|
|
9
|
+
}
|
|
2
10
|
/**
|
|
3
|
-
* Orchestrates the CLI `push` command:
|
|
4
|
-
*
|
|
11
|
+
* Orchestrates the CLI `push` command: loads the config, resolves tags from HEAD, and pushes the release commit and
|
|
12
|
+
* each tag individually.
|
|
5
13
|
*/
|
|
6
|
-
export declare function pushCommand(
|
|
14
|
+
export declare function pushCommand({ configPath, isDryRun, tags, tagsOnly, styles }: PushCommandOptions): Promise<void>;
|
package/dist/esm/pushCommand.js
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import { parseArgsOrExit, reportError } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
4
1
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
5
|
-
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
6
2
|
import { pushRelease } from "./pushRelease.js";
|
|
7
3
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
8
|
-
|
|
9
|
-
const pushFlagSchema = {
|
|
10
|
-
...configFlagSchema,
|
|
11
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
12
|
-
tags: { long: '--tags', type: 'string' },
|
|
13
|
-
tagsOnly: { long: '--tags-only', type: 'boolean' },
|
|
14
|
-
};
|
|
15
|
-
export async function pushCommand(argv, styles, invocationDir) {
|
|
16
|
-
const parsedArgs = parseArgsOrExit(argv, pushFlagSchema);
|
|
17
|
-
const configPath = resolveConfigFlag(parsedArgs.flags.config, invocationDir);
|
|
18
|
-
const { isDryRun, tagsOnly } = parsedArgs.flags;
|
|
4
|
+
export async function pushCommand({ configPath, isDryRun, tags, tagsOnly, styles }) {
|
|
19
5
|
const userConfig = await loadUsableConfig(styles.stderr, configPath);
|
|
20
|
-
const
|
|
21
|
-
const resolvedTags = resolveCommandTags(requestedTags, userConfig);
|
|
6
|
+
const resolvedTags = resolveCommandTags(tags, userConfig);
|
|
22
7
|
if (resolvedTags.length === 0) {
|
|
23
8
|
return;
|
|
24
9
|
}
|
|
@@ -32,16 +17,10 @@ export async function pushCommand(argv, styles, invocationDir) {
|
|
|
32
17
|
for (const { tag } of resolvedTags) {
|
|
33
18
|
console.info(` ${tag}`);
|
|
34
19
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.info(`[dry-run] ${step.command.join(' ')}`);
|
|
40
|
-
}
|
|
20
|
+
const steps = pushRelease(resolvedTags, { isDryRun, tagsOnly });
|
|
21
|
+
if (isDryRun) {
|
|
22
|
+
for (const step of steps) {
|
|
23
|
+
console.info(`[dry-run] ${step.command.join(' ')}`);
|
|
41
24
|
}
|
|
42
25
|
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
reportError(describeError(error));
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
26
|
}
|