@williamthorsen/release-kit 16.1.1 → 16.1.2
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 +17 -0
- package/.readyup/kits/default.js +1 -1
- package/.readyup/manifest.json +3 -3
- package/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/dist/esm/assertCleanWorkingTree.js +14 -7
- package/dist/esm/assertReleasesOnNpm.js +11 -2
- package/dist/esm/assertTaggedBaseline.js +2 -1
- package/dist/esm/assertTarballsComplete.js +15 -3
- package/dist/esm/buildChangelogEntries.js +2 -2
- package/dist/esm/buildDependencyGraph.js +5 -4
- package/dist/esm/bumpVersion.js +3 -2
- package/dist/esm/changelogOverrides.js +3 -2
- package/dist/esm/cli.js +8 -237
- package/dist/esm/commitCommand.js +11 -5
- package/dist/esm/compareVersions.js +2 -1
- package/dist/esm/createGithubRelease.js +7 -1
- package/dist/esm/createGithubReleaseCommand.js +3 -4
- package/dist/esm/createTags.js +6 -4
- package/dist/esm/deleteFileIfExists.js +2 -1
- package/dist/esm/deriveDirectoryConfig.js +7 -6
- package/dist/esm/deriveWorkspaceConfig.js +4 -3
- package/dist/esm/enterRepoRoot.js +2 -1
- package/dist/esm/enumerateReleaseWindows.js +2 -2
- package/dist/esm/init/initCommand.js +10 -1
- package/dist/esm/init/updateTemplatesCommand.js +10 -1
- package/dist/esm/loadConfig.js +6 -5
- package/dist/esm/mergeConfig.js +5 -4
- package/dist/esm/monorepoConfigAssertions.js +6 -5
- package/dist/esm/packFromCleanCheckout.js +12 -3
- package/dist/esm/planVersionBump.js +6 -5
- package/dist/esm/prepareCommand.js +12 -22
- 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.js +4 -18
- package/dist/esm/pushCommand.js +5 -12
- 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 +3 -5
- package/dist/esm/resolveReleaseDeclaration.js +9 -8
- package/dist/esm/resolveReleaseTags.js +11 -4
- package/dist/esm/resolveReleaseTargets.js +2 -1
- package/dist/esm/runCli.d.ts +5 -0
- package/dist/esm/runCli.js +224 -0
- package/dist/esm/showTagPrefixesCommand.js +5 -3
- package/dist/esm/sync-labels/generateCommand.js +4 -1
- package/dist/esm/sync-labels/initCommand.js +7 -1
- package/dist/esm/sync-labels/presets.js +7 -7
- package/dist/esm/sync-labels/resolveLabels.js +4 -3
- package/dist/esm/sync-labels/templates.js +2 -2
- package/dist/esm/tagCommand.js +2 -9
- package/dist/esm/user-error-chaining.d.ts +13 -0
- package/dist/esm/user-error-chaining.js +11 -0
- package/dist/esm/validateOverridesCommand.js +7 -1
- package/package.json +2 -2
package/.meta/changelog.json
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "16.1.2",
|
|
4
|
+
"date": "2026-11-06",
|
|
5
|
+
"sections": [
|
|
6
|
+
{
|
|
7
|
+
"title": "🐛 Bug fixes",
|
|
8
|
+
"audience": "all",
|
|
9
|
+
"items": [
|
|
10
|
+
{
|
|
11
|
+
"description": "Fixes the issue that `release-kit` printed a bug in its own code as an ordinary `Error: <message>`, which it now prints as `release-kit: internal error: <message>` followed by the stack, while a failure in the user's input, config, or environment, including a failed `git`, `gh`, or `npm` call, keeps the `Error: <message>` form. (#1158)",
|
|
12
|
+
"hash": "3a62cc768b23f37156c846f32fd4acaa70f7f931",
|
|
13
|
+
"entry": 1
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
2
19
|
{
|
|
3
20
|
"version": "16.1.1",
|
|
4
21
|
"date": "2026-11-05",
|
package/.readyup/kits/default.js
CHANGED
|
@@ -234,7 +234,7 @@ var CREATE_GITHUB_RELEASE_WORKFLOW_HASH_SINGLE = "d6de961de913c67b08dfa7e07f7349
|
|
|
234
234
|
|
|
235
235
|
// .readyup/kits/default.ts
|
|
236
236
|
function getMinVersion() {
|
|
237
|
-
const picked = { "version": "16.1.
|
|
237
|
+
const picked = { "version": "16.1.2" };
|
|
238
238
|
if (typeof picked["version"] !== "string") {
|
|
239
239
|
throw new TypeError("release-kit/package.json: 'version' must be a string");
|
|
240
240
|
}
|
package/.readyup/manifest.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"checklist": "release-kit",
|
|
8
8
|
"checks": [
|
|
9
9
|
"@williamthorsen/release-kit in devDependencies",
|
|
10
|
-
"@williamthorsen/release-kit >= 16.1.
|
|
10
|
+
"@williamthorsen/release-kit >= 16.1.2",
|
|
11
11
|
"release.yaml workflow exists",
|
|
12
12
|
"release.yaml matches template",
|
|
13
13
|
"release.yaml does not reference deprecated tag ref",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"path": "lib/workflow-hashes.ts"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
|
-
"hash": "
|
|
66
|
+
"hash": "ff1fde3d",
|
|
67
67
|
"kind": "inline",
|
|
68
68
|
"path": "../package.json",
|
|
69
69
|
"paths": [
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"readyupVersion": "0.45.0",
|
|
102
102
|
"source": "kits/default.ts",
|
|
103
103
|
"sourceHash": "56566d0b",
|
|
104
|
-
"targetHash": "
|
|
104
|
+
"targetHash": "6a43de10"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"checklistChecks": [
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 16.1.2 — 2026-11-06
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug fixes
|
|
8
|
+
|
|
9
|
+
- Fixes the issue that `release-kit` printed a bug in its own code as an ordinary `Error: <message>`, which it now prints as `release-kit: internal error: <message>` followed by the stack, while a failure in the user's input, config, or environment, including a failed `git`, `gh`, or `npm` call, keeps the `Error: <message>` form. (#1158)
|
|
10
|
+
|
|
5
11
|
## 16.1.1 — 2026-11-05
|
|
6
12
|
|
|
7
13
|
### 🐛 Bug fixes
|
package/README.md
CHANGED
|
@@ -7,11 +7,11 @@ Version-bumping and changelog-generation toolkit for release workflows.
|
|
|
7
7
|
Provides a self-contained CLI that auto-discovers workspaces from `pnpm-workspace.yaml`, parses conventional commits, determines version bumps, updates `package.json` files, and generates changelogs from the same commit history that decides each bump (with optional [editorial overrides](docs/editorial-overrides.md)).
|
|
8
8
|
|
|
9
9
|
<!-- section:release-notes -->
|
|
10
|
-
## Release notes — v16.1.
|
|
10
|
+
## Release notes — v16.1.2 (2026-11-06)
|
|
11
11
|
|
|
12
12
|
### 🐛 Bug fixes
|
|
13
13
|
|
|
14
|
-
- Fixes the issue that
|
|
14
|
+
- Fixes the issue that `release-kit` printed a bug in its own code as an ordinary `Error: <message>`, which it now prints as `release-kit: internal error: <message>` followed by the stack, while a failure in the user's input, config, or environment, including a failed `git`, `gh`, or `npm` call, keeps the `Error: <message>` form. (#1158)
|
|
15
15
|
<!-- /section:release-notes -->
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { GIT_OUTPUT_LIMIT_BYTES } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { GIT_OUTPUT_LIMIT_BYTES, UserError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { asUserError } from "./user-error-chaining.js";
|
|
3
4
|
export function assertCleanWorkingTree() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
let status;
|
|
6
|
+
try {
|
|
7
|
+
status = execFileSync('git', ['status', '--porcelain'], {
|
|
8
|
+
encoding: 'utf8',
|
|
9
|
+
maxBuffer: GIT_OUTPUT_LIMIT_BYTES,
|
|
10
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
11
|
+
}).trim();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
throw asUserError(error);
|
|
15
|
+
}
|
|
9
16
|
if (status.length > 0) {
|
|
10
|
-
throw new
|
|
17
|
+
throw new UserError('Working tree has uncommitted changes. Commit or stash them, or use --no-git-checks to bypass this check.');
|
|
11
18
|
}
|
|
12
19
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
3
4
|
import { isPublishableManifest } from "./isPublishableManifest.js";
|
|
4
5
|
import { lookUpNpmPackage } from "./npmRegistry.js";
|
|
5
6
|
import { isRecord } from "./typeGuards.js";
|
|
7
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
6
8
|
export function assertReleasesOnNpm(workspacePaths) {
|
|
7
9
|
const unpublishedCandidates = [];
|
|
8
10
|
const unverifiableCandidates = [];
|
|
@@ -22,10 +24,17 @@ export function assertReleasesOnNpm(workspacePaths) {
|
|
|
22
24
|
if (unpublishedCandidates.length === 0 && unverifiableCandidates.length === 0) {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
|
-
throw new
|
|
27
|
+
throw new UserError(describeRefusal(unpublishedCandidates, unverifiableCandidates));
|
|
26
28
|
}
|
|
27
29
|
function buildNpmCandidate(workspacePath) {
|
|
28
|
-
const
|
|
30
|
+
const manifestPath = join(workspacePath, 'package.json');
|
|
31
|
+
let manifest;
|
|
32
|
+
try {
|
|
33
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw chainUserError(`Failed to parse ${manifestPath}`, error);
|
|
37
|
+
}
|
|
29
38
|
if (!isRecord(manifest) || !isPublishableManifest(manifest) || typeof manifest['name'] !== 'string') {
|
|
30
39
|
return undefined;
|
|
31
40
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
2
3
|
import { resolveChangelogJsonPath } from "./changelogJsonFile.js";
|
|
3
4
|
import { readChangelogEntries } from "./changelogJsonUtils.js";
|
|
4
5
|
import { readCurrentVersion } from "./planVersionBump.js";
|
|
@@ -8,7 +9,7 @@ export function assertTaggedBaseline(findings) {
|
|
|
8
9
|
if (untaggedFindings.length === 0)
|
|
9
10
|
return;
|
|
10
11
|
const lines = untaggedFindings.map(({ label, version, tag }) => ` - ${label}: ${version} (create tag ${tag})`);
|
|
11
|
-
throw new
|
|
12
|
+
throw new UserError([
|
|
12
13
|
'The current version is recorded in the changelog, but its tag is not the previous tag reachable from HEAD:',
|
|
13
14
|
...lines,
|
|
14
15
|
'Tag the commit that released each version, then run prepare again.',
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { isAnticipatedFailure, UserError } from '@williamthorsen/nmr-core';
|
|
3
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
5
6
|
import { findMissingManifestPaths } from "./findMissingManifestPaths.js";
|
|
6
7
|
import { isPublishableManifest } from "./isPublishableManifest.js";
|
|
7
8
|
import { packFromCleanCheckout } from "./packFromCleanCheckout.js";
|
|
8
9
|
import { isRecord } from "./typeGuards.js";
|
|
10
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
9
11
|
export function assertTarballsComplete(workspacePaths, devExportConditions, pack = packFromCleanCheckout) {
|
|
10
12
|
const candidates = workspacePaths
|
|
11
13
|
.map((workspacePath) => ({ workspacePath, manifest: readManifest(workspacePath) }))
|
|
@@ -19,7 +21,10 @@ export function assertTarballsComplete(workspacePaths, devExportConditions, pack
|
|
|
19
21
|
filesByWorkspace = pack(candidates.map(({ workspacePath }) => workspacePath), detectPackageManager());
|
|
20
22
|
}
|
|
21
23
|
catch (error) {
|
|
22
|
-
|
|
24
|
+
if (!isAnticipatedFailure(error)) {
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
throw new UserError(`Cannot release: The packages could not be packed from a clean checkout of HEAD.\n${describeError(error)}`, { cause: error });
|
|
23
28
|
}
|
|
24
29
|
const incompletePackages = candidates
|
|
25
30
|
.map(({ workspacePath, manifest }) => ({
|
|
@@ -31,7 +36,7 @@ export function assertTarballsComplete(workspacePaths, devExportConditions, pack
|
|
|
31
36
|
if (incompletePackages.length === 0) {
|
|
32
37
|
return;
|
|
33
38
|
}
|
|
34
|
-
throw new
|
|
39
|
+
throw new UserError(describeRefusal(incompletePackages));
|
|
35
40
|
}
|
|
36
41
|
function describeRefusal(incompletePackages) {
|
|
37
42
|
const sections = ['Cannot release: The tarball of every package must contain each file that its manifest names.'];
|
|
@@ -47,6 +52,13 @@ function describeRefusal(incompletePackages) {
|
|
|
47
52
|
return sections.join('\n\n');
|
|
48
53
|
}
|
|
49
54
|
function readManifest(workspacePath) {
|
|
50
|
-
const
|
|
55
|
+
const manifestPath = join(workspacePath, '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
|
+
}
|
|
51
63
|
return isRecord(manifest) ? manifest : {};
|
|
52
64
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
2
1
|
import { buildEntryItem, buildTitleItem, hasRoutedEntry, } from "./buildChangelogItem.js";
|
|
3
2
|
import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
|
|
4
3
|
import { extractVersion } from "./changelogJsonUtils.js";
|
|
@@ -8,6 +7,7 @@ import { determineBumpType } from "./determineBumpType.js";
|
|
|
8
7
|
import { enumerateReleaseWindows } from "./enumerateReleaseWindows.js";
|
|
9
8
|
import { parseChangeRecordBlock } from "./parseChangeRecordBlock.js";
|
|
10
9
|
import { stripEmojiPrefix } from "./stripEmojiPrefix.js";
|
|
10
|
+
import { prefixUserError } from "./user-error-chaining.js";
|
|
11
11
|
const UNRELEASED_TAG = 'unreleased';
|
|
12
12
|
const CANONICAL_SECTION_ORDER = new Map(Object.values(DEFAULT_WORK_TYPES).map((config, index) => [stripGroupDecorations(config.header), index]));
|
|
13
13
|
export function buildChangelogEntries(config, tag, options) {
|
|
@@ -49,7 +49,7 @@ export function readReleaseHistory(config, options) {
|
|
|
49
49
|
return transformReleases(windows, context);
|
|
50
50
|
}
|
|
51
51
|
catch (error) {
|
|
52
|
-
throw
|
|
52
|
+
throw prefixUserError(`Failed to read the release history for ${options.tagPrefixes.join(', ')}`, error);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
export function stripGroupDecorations(group) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
3
4
|
function isPackageJsonSubset(value) {
|
|
4
5
|
return typeof value === 'object' && value !== null;
|
|
5
6
|
}
|
|
@@ -56,17 +57,17 @@ function readPackageJsonSubset(filePath) {
|
|
|
56
57
|
content = readFileSync(filePath, 'utf8');
|
|
57
58
|
}
|
|
58
59
|
catch (error) {
|
|
59
|
-
throw
|
|
60
|
+
throw chainUserError(`Failed to read ${filePath}`, error);
|
|
60
61
|
}
|
|
61
62
|
let parsedJson;
|
|
62
63
|
try {
|
|
63
64
|
parsedJson = JSON.parse(content);
|
|
64
65
|
}
|
|
65
66
|
catch (error) {
|
|
66
|
-
throw
|
|
67
|
+
throw chainUserError(`Failed to parse JSON in ${filePath}`, error);
|
|
67
68
|
}
|
|
68
69
|
if (!isPackageJsonSubset(parsedJson)) {
|
|
69
|
-
throw new
|
|
70
|
+
throw new UserError(`Invalid package.json at ${filePath}`);
|
|
70
71
|
}
|
|
71
72
|
return parsedJson;
|
|
72
73
|
}
|
package/dist/esm/bumpVersion.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
1
2
|
export function bumpVersion(version, releaseType) {
|
|
2
3
|
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
3
4
|
if (!match) {
|
|
4
|
-
throw new
|
|
5
|
+
throw new UserError(`Invalid semver version: '${version}'`);
|
|
5
6
|
}
|
|
6
7
|
const major = match[1];
|
|
7
8
|
const minor = match[2];
|
|
8
9
|
const patch = match[3];
|
|
9
10
|
if (major === undefined || minor === undefined || patch === undefined) {
|
|
10
|
-
throw new
|
|
11
|
+
throw new UserError(`Invalid semver version: '${version}'`);
|
|
11
12
|
}
|
|
12
13
|
const majorNumber = Number.parseInt(major, 10);
|
|
13
14
|
const minorNumber = Number.parseInt(minor, 10);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
2
3
|
import { applyChangelogOverrides, formatStaleOverrideKeyWarning } from "./applyChangelogOverrides.js";
|
|
3
4
|
import { loadChangelogOverrides } from "./loadChangelogOverrides.js";
|
|
4
5
|
const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
|
|
@@ -8,7 +9,7 @@ export function applyWorkspaceOverrides(newEntries, workspacePath, overrideConte
|
|
|
8
9
|
const composedOverrides = composeOverrides(projectOverrides, workspaceOverrides);
|
|
9
10
|
const appliedResult = applyChangelogOverrides(newEntries, composedOverrides);
|
|
10
11
|
if (appliedResult.errors.length > 0) {
|
|
11
|
-
throw new
|
|
12
|
+
throw new UserError(`Changelog override application failed:\n - ${appliedResult.errors.join('\n - ')}`);
|
|
12
13
|
}
|
|
13
14
|
overrideWarnings.push(...appliedResult.warnings);
|
|
14
15
|
const matchedSet = new Set(appliedResult.matchedKeys);
|
|
@@ -38,7 +39,7 @@ export function composeOverrides(rootEntries, workspaceEntries) {
|
|
|
38
39
|
export function createOverrideContext(workspacePaths) {
|
|
39
40
|
const result = loadOverridesForScopes({ projectPath: '.', workspaces: [...workspacePaths] });
|
|
40
41
|
if (result.errors.length > 0) {
|
|
41
|
-
throw new
|
|
42
|
+
throw new UserError(`Failed to load changelog overrides:\n - ${result.errors.join('\n - ')}`);
|
|
42
43
|
}
|
|
43
44
|
return {
|
|
44
45
|
projectOverrides: result.projectOverrides,
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,240 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { enterRepoRoot } from "./enterRepoRoot.js";
|
|
8
|
-
import { showInitHelp, showUpdateTemplatesHelp } from "./help/initHelp.js";
|
|
9
|
-
import { showOverridesHelp, showOverridesValidateHelp } from "./help/overridesHelp.js";
|
|
10
|
-
import { showPrepareHelp } from "./help/prepareHelp.js";
|
|
11
|
-
import { showCommitHelp, showCreateGithubReleaseHelp, showPublishHelp, showPushHelp, showShowTagPrefixesHelp, showTagHelp, } from "./help/releaseStepsHelp.js";
|
|
12
|
-
import { showSyncLabelsGenerateHelp, showSyncLabelsHelp, showSyncLabelsInitHelp, showSyncLabelsSyncHelp, } from "./help/syncLabelsHelp.js";
|
|
13
|
-
import { showUsage } from "./help/usage.js";
|
|
14
|
-
import { initCommand } from "./init/initCommand.js";
|
|
15
|
-
import { updateTemplatesCommand } from "./init/updateTemplatesCommand.js";
|
|
16
|
-
import { prepareCommand } from "./prepareCommand.js";
|
|
17
|
-
import { publishCommand } from "./publishCommand.js";
|
|
18
|
-
import { pushCommand } from "./pushCommand.js";
|
|
19
|
-
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
20
|
-
import { resolveStylesOrExit } from "./resolveStylesOrExit.js";
|
|
21
|
-
import { showTagPrefixesCommand } from "./showTagPrefixesCommand.js";
|
|
22
|
-
import { generateCommand } from "./sync-labels/generateCommand.js";
|
|
23
|
-
import { syncLabelsInitCommand } from "./sync-labels/initCommand.js";
|
|
24
|
-
import { syncLabelsCommand } from "./sync-labels/syncCommand.js";
|
|
25
|
-
import { tagCommand } from "./tagCommand.js";
|
|
26
|
-
import { validateOverridesCommand } from "./validateOverridesCommand.js";
|
|
27
|
-
const VERSION = resolveSelfVersion(import.meta.url);
|
|
28
|
-
function enterRepoRootOrExit() {
|
|
29
|
-
try {
|
|
30
|
-
return enterRepoRoot();
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
reportError(describeError(error));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { reportFailure } from '@williamthorsen/nmr-core';
|
|
4
|
+
import { runCli } from "./runCli.js";
|
|
5
|
+
try {
|
|
6
|
+
await runCli(process.argv.slice(2));
|
|
36
7
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.info(VERSION);
|
|
41
|
-
process.exit(0);
|
|
8
|
+
catch (error) {
|
|
9
|
+
reportFailure('release-kit', error);
|
|
10
|
+
process.exitCode = 1;
|
|
42
11
|
}
|
|
43
|
-
if (command === undefined || ['--help', '-h'].includes(command)) {
|
|
44
|
-
showUsage();
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
const flags = args.slice(1);
|
|
48
|
-
const styles = resolveStylesOrExit({
|
|
49
|
-
env: process.env,
|
|
50
|
-
stderrIsTty: process.stderr.isTTY,
|
|
51
|
-
stdoutIsTty: process.stdout.isTTY,
|
|
52
|
-
});
|
|
53
|
-
if (command === 'prepare') {
|
|
54
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
55
|
-
showPrepareHelp();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
}
|
|
58
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
59
|
-
await prepareCommand(flags, styles, invocationDir);
|
|
60
|
-
process.exit(0);
|
|
61
|
-
}
|
|
62
|
-
if (command === 'commit') {
|
|
63
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
64
|
-
showCommitHelp();
|
|
65
|
-
process.exit(0);
|
|
66
|
-
}
|
|
67
|
-
enterRepoRootOrExit();
|
|
68
|
-
try {
|
|
69
|
-
commitCommand(flags);
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
reportError(describeError(error));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
if (command === 'tag') {
|
|
78
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
79
|
-
showTagHelp();
|
|
80
|
-
process.exit(0);
|
|
81
|
-
}
|
|
82
|
-
enterRepoRootOrExit();
|
|
83
|
-
tagCommand(flags, styles);
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
if (command === 'push') {
|
|
87
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
88
|
-
showPushHelp();
|
|
89
|
-
process.exit(0);
|
|
90
|
-
}
|
|
91
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
92
|
-
await pushCommand(flags, styles, invocationDir);
|
|
93
|
-
process.exit(0);
|
|
94
|
-
}
|
|
95
|
-
if (command === 'create-github-release') {
|
|
96
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
97
|
-
showCreateGithubReleaseHelp();
|
|
98
|
-
process.exit(0);
|
|
99
|
-
}
|
|
100
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
101
|
-
await createGithubReleaseCommand(flags, styles, invocationDir);
|
|
102
|
-
process.exit(0);
|
|
103
|
-
}
|
|
104
|
-
if (command === 'publish') {
|
|
105
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
106
|
-
showPublishHelp();
|
|
107
|
-
process.exit(0);
|
|
108
|
-
}
|
|
109
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
110
|
-
await publishCommand(flags, styles, invocationDir);
|
|
111
|
-
process.exit(0);
|
|
112
|
-
}
|
|
113
|
-
if (command === 'show-tag-prefixes') {
|
|
114
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
115
|
-
showShowTagPrefixesHelp();
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
119
|
-
const { config } = parseArgsOrExit(flags, configFlagSchema).flags;
|
|
120
|
-
const exitCode = await showTagPrefixesCommand(styles, resolveConfigFlag(config, invocationDir));
|
|
121
|
-
process.exit(exitCode);
|
|
122
|
-
}
|
|
123
|
-
if (command === 'init') {
|
|
124
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
125
|
-
showInitHelp();
|
|
126
|
-
process.exit(0);
|
|
127
|
-
}
|
|
128
|
-
enterRepoRootOrExit();
|
|
129
|
-
const initFlagSchema = {
|
|
130
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
131
|
-
shouldForce: { long: '--force', type: 'boolean' },
|
|
132
|
-
shouldScaffoldConfig: { long: '--with-config', type: 'boolean' },
|
|
133
|
-
};
|
|
134
|
-
const { isDryRun, shouldForce, shouldScaffoldConfig } = parseArgsOrExit(flags, initFlagSchema).flags;
|
|
135
|
-
const exitCode = initCommand({ isDryRun, shouldForce, styles, shouldScaffoldConfig });
|
|
136
|
-
process.exit(exitCode);
|
|
137
|
-
}
|
|
138
|
-
if (command === 'update-templates') {
|
|
139
|
-
if (flags.some((f) => f === '--help' || f === '-h')) {
|
|
140
|
-
showUpdateTemplatesHelp();
|
|
141
|
-
process.exit(0);
|
|
142
|
-
}
|
|
143
|
-
enterRepoRootOrExit();
|
|
144
|
-
const updateTemplatesFlagSchema = {
|
|
145
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
146
|
-
};
|
|
147
|
-
const { isDryRun } = parseArgsOrExit(flags, updateTemplatesFlagSchema).flags;
|
|
148
|
-
const exitCode = updateTemplatesCommand({ isDryRun, styles });
|
|
149
|
-
process.exit(exitCode);
|
|
150
|
-
}
|
|
151
|
-
if (command === 'sync-labels') {
|
|
152
|
-
const subcommand = flags[0];
|
|
153
|
-
if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
|
|
154
|
-
showSyncLabelsHelp();
|
|
155
|
-
process.exit(0);
|
|
156
|
-
}
|
|
157
|
-
const subflags = flags.slice(1);
|
|
158
|
-
if (subcommand === 'init') {
|
|
159
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
160
|
-
showSyncLabelsInitHelp();
|
|
161
|
-
process.exit(0);
|
|
162
|
-
}
|
|
163
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
164
|
-
const syncLabelsInitFlagSchema = {
|
|
165
|
-
...configFlagSchema,
|
|
166
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
167
|
-
shouldForce: { long: '--force', type: 'boolean' },
|
|
168
|
-
};
|
|
169
|
-
const { config, isDryRun, shouldForce } = parseArgsOrExit(subflags, syncLabelsInitFlagSchema).flags;
|
|
170
|
-
const configPath = resolveConfigFlag(config, invocationDir);
|
|
171
|
-
const exitCode = await syncLabelsInitCommand({
|
|
172
|
-
...(configPath !== undefined && { configPath }),
|
|
173
|
-
isDryRun,
|
|
174
|
-
shouldForce,
|
|
175
|
-
styles,
|
|
176
|
-
});
|
|
177
|
-
process.exit(exitCode);
|
|
178
|
-
}
|
|
179
|
-
if (subcommand === 'generate') {
|
|
180
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
181
|
-
showSyncLabelsGenerateHelp();
|
|
182
|
-
process.exit(0);
|
|
183
|
-
}
|
|
184
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
185
|
-
const generateFlagSchema = {
|
|
186
|
-
...configFlagSchema,
|
|
187
|
-
shouldCheck: { long: '--check', type: 'boolean' },
|
|
188
|
-
};
|
|
189
|
-
const { shouldCheck, config } = parseArgsOrExit(subflags, generateFlagSchema).flags;
|
|
190
|
-
const configPath = resolveConfigFlag(config, invocationDir);
|
|
191
|
-
const exitCode = await generateCommand({ shouldCheck, ...(configPath !== undefined && { configPath }), styles });
|
|
192
|
-
process.exit(exitCode);
|
|
193
|
-
}
|
|
194
|
-
if (subcommand === 'sync') {
|
|
195
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
196
|
-
showSyncLabelsSyncHelp();
|
|
197
|
-
process.exit(0);
|
|
198
|
-
}
|
|
199
|
-
if (subflags.length > 0) {
|
|
200
|
-
reportError(`Unknown option: ${subflags[0]}`);
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
enterRepoRootOrExit();
|
|
204
|
-
const exitCode = syncLabelsCommand();
|
|
205
|
-
process.exit(exitCode);
|
|
206
|
-
}
|
|
207
|
-
reportError(`Unknown subcommand: ${subcommand}`);
|
|
208
|
-
showSyncLabelsHelp();
|
|
209
|
-
process.exit(1);
|
|
210
|
-
}
|
|
211
|
-
if (command === 'overrides') {
|
|
212
|
-
const subcommand = flags[0];
|
|
213
|
-
if (subcommand === undefined || ['--help', '-h'].includes(subcommand)) {
|
|
214
|
-
showOverridesHelp();
|
|
215
|
-
process.exit(0);
|
|
216
|
-
}
|
|
217
|
-
const subflags = flags.slice(1);
|
|
218
|
-
if (subcommand === 'validate') {
|
|
219
|
-
if (subflags.some((f) => f === '--help' || f === '-h')) {
|
|
220
|
-
showOverridesValidateHelp();
|
|
221
|
-
process.exit(0);
|
|
222
|
-
}
|
|
223
|
-
const { invocationDir } = enterRepoRootOrExit();
|
|
224
|
-
const { config } = parseArgsOrExit(subflags, configFlagSchema).flags;
|
|
225
|
-
const result = await validateOverridesCommand(styles, resolveConfigFlag(config, invocationDir));
|
|
226
|
-
if (result.exitCode === 0) {
|
|
227
|
-
console.info(result.message);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
process.stderr.write(`${result.message}\n`);
|
|
231
|
-
}
|
|
232
|
-
process.exit(result.exitCode);
|
|
233
|
-
}
|
|
234
|
-
reportError(`Unknown subcommand: ${subcommand}`);
|
|
235
|
-
showOverridesHelp();
|
|
236
|
-
process.exit(1);
|
|
237
|
-
}
|
|
238
|
-
reportError(`Unknown command: ${command}`);
|
|
239
|
-
showUsage();
|
|
240
|
-
process.exit(1);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { GIT_OUTPUT_LIMIT_BYTES, hasErrnoCode, parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { GIT_OUTPUT_LIMIT_BYTES, hasErrnoCode, parseArgsOrExit, UserError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { readReleaseTags, RELEASE_SUMMARY_FILE, resolveReleaseTagsPath } from "./releaseFiles.js";
|
|
5
|
+
import { asUserError } from "./user-error-chaining.js";
|
|
5
6
|
const commitFlagSchema = {
|
|
6
7
|
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
7
8
|
};
|
|
@@ -9,7 +10,7 @@ export function commitCommand(argv) {
|
|
|
9
10
|
const isDryRun = parseArgsOrExit(argv, commitFlagSchema).flags.isDryRun;
|
|
10
11
|
const tags = readReleaseTags();
|
|
11
12
|
if (tags.length === 0) {
|
|
12
|
-
throw new
|
|
13
|
+
throw new UserError(`Tags file at ${resolveReleaseTagsPath()} is empty. Run \`release-kit prepare\` first.`);
|
|
13
14
|
}
|
|
14
15
|
let summary = '';
|
|
15
16
|
try {
|
|
@@ -19,7 +20,7 @@ export function commitCommand(argv) {
|
|
|
19
20
|
if (hasErrnoCode(error, 'ENOENT')) {
|
|
20
21
|
}
|
|
21
22
|
else {
|
|
22
|
-
throw error;
|
|
23
|
+
throw asUserError(error);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
const title = `release: ${tags.join(' ')}`;
|
|
@@ -42,7 +43,12 @@ export function commitCommand(argv) {
|
|
|
42
43
|
}
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
try {
|
|
47
|
+
execFileSync('git', ['add', '-A'], { maxBuffer: GIT_OUTPUT_LIMIT_BYTES });
|
|
48
|
+
execFileSync('git', ['commit', '-m', message], { maxBuffer: GIT_OUTPUT_LIMIT_BYTES });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw asUserError(error);
|
|
52
|
+
}
|
|
47
53
|
console.info(`Created release commit: ${title}`);
|
|
48
54
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
1
2
|
import semver from 'semver';
|
|
2
3
|
const CANONICAL_SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
3
4
|
function parseCanonicalSemver(version) {
|
|
4
5
|
const match = version.match(CANONICAL_SEMVER_PATTERN);
|
|
5
6
|
if (!match) {
|
|
6
|
-
throw new
|
|
7
|
+
throw new UserError(`Invalid semver version: '${version}'`);
|
|
7
8
|
}
|
|
8
9
|
const [, major = '', minor = '', patch = ''] = match;
|
|
9
10
|
return {
|
|
@@ -3,6 +3,7 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { extractVersion, readChangelogEntries } from "./changelogJsonUtils.js";
|
|
5
5
|
import { matchesAudience, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
6
|
+
import { asUserError } from "./user-error-chaining.js";
|
|
6
7
|
export function createGithubRelease(options) {
|
|
7
8
|
const { tag, changelogJsonPath, isDryRun, sectionOrder } = options;
|
|
8
9
|
if (!existsSync(changelogJsonPath)) {
|
|
@@ -36,7 +37,12 @@ export function createGithubRelease(options) {
|
|
|
36
37
|
console.info(`[dry-run] Would run: gh ${args.join(' ')}`);
|
|
37
38
|
return { status: 'created' };
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
try {
|
|
41
|
+
execFileSync('gh', args, { stdio: 'inherit' });
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw asUserError(error);
|
|
45
|
+
}
|
|
40
46
|
return { status: 'created' };
|
|
41
47
|
}
|
|
42
48
|
export function createGithubReleases(tags, changelogJsonOutputPath, isDryRun, sectionOrder) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { parseArgsOrExit
|
|
2
|
-
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
1
|
+
import { parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
3
2
|
import { configFlagSchema } from "./configFlagSchema.js";
|
|
4
3
|
import { createGithubReleases } from "./createGithubRelease.js";
|
|
5
4
|
import { deriveReleaseNotesConfig } from "./deriveReleaseNotesConfig.js";
|
|
@@ -8,6 +7,7 @@ import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
|
8
7
|
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
9
8
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
10
9
|
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
10
|
+
import { prefixUserError } from "./user-error-chaining.js";
|
|
11
11
|
const createGithubReleaseFlagSchema = {
|
|
12
12
|
...configFlagSchema,
|
|
13
13
|
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
@@ -35,8 +35,7 @@ export async function createGithubReleaseCommand(argv, styles, invocationDir) {
|
|
|
35
35
|
outcome = createGithubReleases(releasableTags, changelogJsonOutputPath, isDryRun, sectionOrder);
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
|
-
|
|
39
|
-
process.exit(1);
|
|
38
|
+
throw prefixUserError('Failed to create GitHub Releases', error);
|
|
40
39
|
}
|
|
41
40
|
if (outcome.skippedTags.length === 0) {
|
|
42
41
|
return;
|