@williamthorsen/release-kit 4.6.0 → 4.8.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/CHANGELOG.md +26 -8
- package/cliff.toml.template +21 -19
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.js +24 -0
- package/dist/esm/createGithubRelease.js +6 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/init/detectRepoType.js +1 -2
- package/dist/esm/init/templates.js +7 -7
- package/dist/esm/parseCommitMessage.js +1 -1
- package/dist/esm/prepareCommand.js +1 -1
- package/dist/esm/pushCommand.d.ts +1 -0
- package/dist/esm/pushCommand.js +46 -0
- package/dist/esm/pushRelease.d.ts +11 -0
- package/dist/esm/pushRelease.js +26 -0
- package/dist/esm/sync-labels/templates.js +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
- package/dist/esm/init/prompt.d.ts +0 -4
- package/dist/esm/init/prompt.js +0 -19
- package/dist/esm/runReleasePrepare.d.ts +0 -10
- package/dist/esm/runReleasePrepare.js +0 -125
- package/dist/esm/sync-labels/scaffold.d.ts +0 -6
- package/dist/esm/sync-labels/scaffold.js +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [release-kit-v4.8.0] - 2026-04-17
|
|
6
|
+
|
|
7
|
+
### Bug fixes
|
|
8
|
+
|
|
9
|
+
- Replace broad catch with `existsSync` guard in `detectRepoType` (#229)
|
|
10
|
+
|
|
11
|
+
Fixes silent swallowing of unexpected filesystem errors in `detectRepoType`. Previously, errors like `EACCES` (permission denied) or `EMFILE` (too many open files) when reading `package.json` were caught and discarded, causing the function to silently return `'single-package'` instead of surfacing the problem.
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- Add `push` command for safe tag pushing (#243)
|
|
16
|
+
|
|
17
|
+
Adds a `release-kit push` command that safely pushes the release commit and each tag individually, ensuring GitHub Actions fires a separate workflow run per tag. The command performs a `1 + N` push sequence: one branch push followed by one `git push --no-follow-tags origin <tag>` per resolved tag. Supports `--dry-run` (preview without pushing), `--only` (filter tags by package name), and `--tags-only` (skip the branch push).
|
|
18
|
+
|
|
19
|
+
## [release-kit-v4.7.0] - 2026-04-16
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
- Support ## as synthetic ticket prefix in changelogs
|
|
24
|
+
|
|
25
|
+
Commits prefixed with `##` are now included in changelogs without requiring a ticket ID. This supports ad-hoc changes made during interactive sessions where creating a ticket and PR adds undesired overhead.
|
|
26
|
+
|
|
5
27
|
## [release-kit-v4.6.0] - 2026-04-15
|
|
6
28
|
|
|
7
29
|
### Features
|
|
@@ -219,6 +241,10 @@ All notable changes to this project will be documented in this file.
|
|
|
219
241
|
|
|
220
242
|
### Features
|
|
221
243
|
|
|
244
|
+
- Migrate release-kit from toolbelt (#18)
|
|
245
|
+
|
|
246
|
+
Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.
|
|
247
|
+
|
|
222
248
|
- Slim down release workflow by removing unnecessary pnpm install (#21)
|
|
223
249
|
|
|
224
250
|
Make release-kit self-contained by invoking git-cliff via `npx --yes` instead of requiring it on PATH, and by appending modified file paths to the format command so lightweight formatters like `npx prettier --write` work without a full `pnpm install`. Update init templates, README, and consuming repo config/workflow to reference workflow v3.
|
|
@@ -241,12 +267,4 @@ All notable changes to this project will be documented in this file.
|
|
|
241
267
|
|
|
242
268
|
Addresses five code quality issues and a test coverage gap identified during the release-kit migration (#5). Extracts a duplicated `isRecord` type guard into a shared module, eliminates a double-read in `bumpAllVersions`, improves error handling in `usesPnpm` by replacing a silent catch with a structured error boundary, removes an unreachable `'feature'` pattern from version defaults, and adds an integration test for scaffold template path resolution.
|
|
243
269
|
|
|
244
|
-
## [release-kit-v1.0.1] - 2026-03-14
|
|
245
|
-
|
|
246
|
-
### Features
|
|
247
|
-
|
|
248
|
-
- Migrate release-kit from toolbelt (#18)
|
|
249
|
-
|
|
250
|
-
Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.
|
|
251
|
-
|
|
252
270
|
<!-- generated by git-cliff -->
|
package/cliff.toml.template
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
# Examples:
|
|
6
6
|
# #19 rdy|feat: Add utility functions for common check patterns (#26)
|
|
7
7
|
# AFG-42 panel|fix: Fix button layout (#73)
|
|
8
|
+
# ## feat: Quick utility added during interactive session
|
|
8
9
|
#
|
|
9
|
-
# Only ticketed commits (leading #NN
|
|
10
|
+
# Only ticketed commits (leading #NN, PROJ-NN, or ##) are included.
|
|
10
11
|
# Unticketed maintenance commits (deps upgrades, tooling tweaks) are skipped.
|
|
12
|
+
# Use ## as a synthetic ticket prefix for ad-hoc commits that belong in the changelog.
|
|
11
13
|
|
|
12
14
|
[changelog]
|
|
13
15
|
header = """
|
|
@@ -55,27 +57,27 @@ filter_unconventional = false
|
|
|
55
57
|
split_commits = false
|
|
56
58
|
commit_preprocessors = []
|
|
57
59
|
# Require a ticket-ID prefix for inclusion.
|
|
58
|
-
# Supported prefixes: "#123 ", "#123-1 ", "#123.1 ", "TOOL-123 "
|
|
60
|
+
# Supported prefixes: "#123 ", "#123-1 ", "#123.1 ", "TOOL-123 ", "## "
|
|
59
61
|
commit_parsers = [
|
|
60
62
|
{ message = "^release:", skip = true },
|
|
61
63
|
{ message = "^Merge", skip = true },
|
|
62
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
63
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
64
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
65
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
66
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
67
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
68
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
69
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
70
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
71
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
72
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
73
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
74
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
75
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
76
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
77
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
78
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)
|
|
64
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?bugfix(!)?:", group = "Bug fixes" },
|
|
65
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?ci(!)?:", group = "CI" },
|
|
66
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?deprecate(!)?:", group = "Deprecated" },
|
|
67
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?deps?(!)?:", group = "Dependencies" },
|
|
68
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?docs?(!)?:", group = "Documentation" },
|
|
69
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?feat(!)?:", group = "Features" },
|
|
70
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?feature(!)?:", group = "Features" },
|
|
71
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?fix(!)?:", group = "Bug fixes" },
|
|
72
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?fmt(!)?:", group = "Formatting" },
|
|
73
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?internal(!)?:", group = "Internal" },
|
|
74
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?perf(!)?:", group = "Performance" },
|
|
75
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?performance(!)?:", group = "Performance" },
|
|
76
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?refactor(!)?:", group = "Refactoring" },
|
|
77
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?sec(!)?:", group = "Security" },
|
|
78
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?security(!)?:", group = "Security" },
|
|
79
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tests?(!)?:", group = "Tests" },
|
|
80
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tooling(!)?:", group = "Tooling" },
|
|
79
81
|
# Skip unticketed commits (maintenance, deps, initial scaffolding, etc.)
|
|
80
82
|
{ message = ".*", skip = true },
|
|
81
83
|
]
|
package/dist/esm/.cache
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d6a6d71f34f00ba31c3dca3b32657832693ff9c12de118fafa96fc3554835ecc
|
|
@@ -5,6 +5,7 @@ import { githubReleaseCommand } from "../githubReleaseCommand.js";
|
|
|
5
5
|
import { initCommand } from "../init/initCommand.js";
|
|
6
6
|
import { prepareCommand } from "../prepareCommand.js";
|
|
7
7
|
import { publishCommand } from "../publishCommand.js";
|
|
8
|
+
import { pushCommand } from "../pushCommand.js";
|
|
8
9
|
import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
9
10
|
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
10
11
|
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
@@ -18,6 +19,7 @@ Commands:
|
|
|
18
19
|
prepare Run release preparation (auto-discovers workspaces)
|
|
19
20
|
commit Stage changes and create the release commit
|
|
20
21
|
tag Create annotated git tags from the tags file
|
|
22
|
+
push Push release commit and tags (one push per tag)
|
|
21
23
|
publish Publish packages with release tags on HEAD
|
|
22
24
|
github-release Create GitHub Releases from changelog.json for tags on HEAD
|
|
23
25
|
init Initialize release-kit in the current repository
|
|
@@ -128,6 +130,20 @@ Options:
|
|
|
128
130
|
--help, -h Show this help message
|
|
129
131
|
`);
|
|
130
132
|
}
|
|
133
|
+
function showPushHelp() {
|
|
134
|
+
console.info(`
|
|
135
|
+
Usage: release-kit push [options]
|
|
136
|
+
|
|
137
|
+
Push the release commit and each tag individually, ensuring GitHub Actions
|
|
138
|
+
fires a separate workflow run per tag.
|
|
139
|
+
|
|
140
|
+
Options:
|
|
141
|
+
--dry-run Preview without pushing
|
|
142
|
+
--only=name1,name2 Only push tags for the named packages (comma-separated, monorepo only)
|
|
143
|
+
--tags-only Skip the branch push (push tags only)
|
|
144
|
+
--help, -h Show this help message
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
131
147
|
function showGithubReleaseHelp() {
|
|
132
148
|
console.info(`
|
|
133
149
|
Usage: release-kit github-release [options]
|
|
@@ -194,6 +210,14 @@ if (command === "tag") {
|
|
|
194
210
|
tagCommand(flags);
|
|
195
211
|
process.exit(0);
|
|
196
212
|
}
|
|
213
|
+
if (command === "push") {
|
|
214
|
+
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
215
|
+
showPushHelp();
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
await pushCommand(flags);
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
197
221
|
if (command === "github-release") {
|
|
198
222
|
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
199
223
|
showGithubReleaseHelp();
|
|
@@ -20,10 +20,16 @@ function createGithubRelease(options) {
|
|
|
20
20
|
console.warn(`Warning: no changelog entry for version ${version}; skipping GitHub Release creation`);
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
|
+
if (!entry.sections.some(matchesAudience("all"))) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
23
26
|
const body = renderReleaseNotesSingle(entry, {
|
|
24
27
|
filter: matchesAudience("all"),
|
|
25
28
|
includeHeading: false
|
|
26
29
|
});
|
|
30
|
+
if (body.trim() === "") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
27
33
|
const args = ["release", "create", tag, "--title", tag, "--notes", body];
|
|
28
34
|
if (dryRun) {
|
|
29
35
|
console.info(`[dry-run] Would run: gh ${args.join(" ")}`);
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export { injectSection } from './injectSection.ts';
|
|
|
27
27
|
export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
|
|
28
28
|
export { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
|
|
29
29
|
export { publishPackage } from './publish.ts';
|
|
30
|
+
export type { PushReleaseOptions } from './pushRelease.ts';
|
|
31
|
+
export { pushRelease } from './pushRelease.ts';
|
|
30
32
|
export { releasePrepare } from './releasePrepare.ts';
|
|
31
33
|
export { releasePrepareMono } from './releasePrepareMono.ts';
|
|
32
34
|
export type { RenderOptions } from './renderReleaseNotes.ts';
|
package/dist/esm/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { injectSection } from "./injectSection.js";
|
|
|
23
23
|
import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
|
|
24
24
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
|
|
25
25
|
import { publishPackage } from "./publish.js";
|
|
26
|
+
import { pushRelease } from "./pushRelease.js";
|
|
26
27
|
import { releasePrepare } from "./releasePrepare.js";
|
|
27
28
|
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
28
29
|
import { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
@@ -60,6 +61,7 @@ export {
|
|
|
60
61
|
matchesAudience,
|
|
61
62
|
parseCommitMessage,
|
|
62
63
|
publishPackage,
|
|
64
|
+
pushRelease,
|
|
63
65
|
releasePrepare,
|
|
64
66
|
releasePrepareMono,
|
|
65
67
|
renderReleaseNotesMulti,
|
|
@@ -4,13 +4,12 @@ function detectRepoType() {
|
|
|
4
4
|
if (existsSync("pnpm-workspace.yaml")) {
|
|
5
5
|
return "monorepo";
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
if (existsSync("package.json")) {
|
|
8
8
|
const raw = readFileSync("package.json", "utf8");
|
|
9
9
|
const pkg = parseJsonRecord(raw);
|
|
10
10
|
if (pkg !== void 0 && Array.isArray(pkg.workspaces)) {
|
|
11
11
|
return "monorepo";
|
|
12
12
|
}
|
|
13
|
-
} catch {
|
|
14
13
|
}
|
|
15
14
|
return "single-package";
|
|
16
15
|
}
|
|
@@ -36,7 +36,7 @@ export default config;
|
|
|
36
36
|
`;
|
|
37
37
|
}
|
|
38
38
|
function publishWorkflow(repoType) {
|
|
39
|
-
const tagPattern = repoType === "monorepo" ? "'*-v[0-9]*'" : "'v[0-9]*'";
|
|
39
|
+
const tagPattern = repoType === "monorepo" ? "'*-v[0-9]*.[0-9]*.[0-9]*'" : "'v[0-9]*.[0-9]*.[0-9]*'";
|
|
40
40
|
return `# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
|
41
41
|
name: Publish
|
|
42
42
|
|
|
@@ -51,9 +51,9 @@ permissions:
|
|
|
51
51
|
|
|
52
52
|
jobs:
|
|
53
53
|
publish:
|
|
54
|
-
uses: williamthorsen/node-monorepo-tools/.github/workflows/publish.reusable.yaml@publish-
|
|
54
|
+
uses: williamthorsen/node-monorepo-tools/.github/workflows/publish.reusable.yaml@workflow/publish-v1
|
|
55
55
|
with:
|
|
56
|
-
provenance:
|
|
56
|
+
provenance: true
|
|
57
57
|
`;
|
|
58
58
|
}
|
|
59
59
|
function releaseWorkflow(repoType) {
|
|
@@ -78,7 +78,7 @@ on:
|
|
|
78
78
|
- minor
|
|
79
79
|
- major
|
|
80
80
|
force:
|
|
81
|
-
description: 'Force a
|
|
81
|
+
description: 'Force a release even when there are no commits since the last tag (requires --bump)'
|
|
82
82
|
required: false
|
|
83
83
|
type: boolean
|
|
84
84
|
default: false
|
|
@@ -89,7 +89,7 @@ permissions:
|
|
|
89
89
|
|
|
90
90
|
jobs:
|
|
91
91
|
release:
|
|
92
|
-
uses: williamthorsen/node-monorepo-tools/.github/workflows/release.reusable.yaml@release-
|
|
92
|
+
uses: williamthorsen/node-monorepo-tools/.github/workflows/release.reusable.yaml@workflow/release-v1
|
|
93
93
|
with:
|
|
94
94
|
only: \${{ inputs.only }}
|
|
95
95
|
bump: \${{ inputs.bump }}
|
|
@@ -112,7 +112,7 @@ on:
|
|
|
112
112
|
- minor
|
|
113
113
|
- major
|
|
114
114
|
force:
|
|
115
|
-
description: 'Force a
|
|
115
|
+
description: 'Force a release even when there are no commits since the last tag (requires --bump)'
|
|
116
116
|
required: false
|
|
117
117
|
type: boolean
|
|
118
118
|
default: false
|
|
@@ -123,7 +123,7 @@ permissions:
|
|
|
123
123
|
|
|
124
124
|
jobs:
|
|
125
125
|
release:
|
|
126
|
-
uses: williamthorsen/node-monorepo-tools/.github/workflows/release.reusable.yaml@release-
|
|
126
|
+
uses: williamthorsen/node-monorepo-tools/.github/workflows/release.reusable.yaml@workflow/release-v1
|
|
127
127
|
with:
|
|
128
128
|
bump: \${{ inputs.bump }}
|
|
129
129
|
force: \${{ inputs.force }}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const COMMIT_PREPROCESSOR_PATTERNS = [/^#\d+([.-]\d+)?\s+/, /^[A-Z]+-\d+\s+/];
|
|
1
|
+
const COMMIT_PREPROCESSOR_PATTERNS = [/^##\s+/, /^#\d+([.-]\d+)?\s+/, /^[A-Z]+-\d+\s+/];
|
|
2
2
|
function parseCommitMessage(message, hash, workTypes, scopeAliases) {
|
|
3
3
|
const stripped = stripTicketPrefix(message);
|
|
4
4
|
const match = stripped.match(/^(?:([^|]+)\|)?(\w+)(?:\(([^)]+)\))?(!)?:\s*(.*)$/);
|
|
@@ -25,7 +25,7 @@ Usage: npx @williamthorsen/release-kit prepare [options]
|
|
|
25
25
|
Options:
|
|
26
26
|
--dry-run Run without modifying any files
|
|
27
27
|
--bump=major|minor|patch Override the bump type for all components
|
|
28
|
-
--force
|
|
28
|
+
--force Force a release even when there are no commits since the last tag (requires --bump)
|
|
29
29
|
--no-git-checks, -n Skip the clean-working-tree check
|
|
30
30
|
--only=name1,name2 Only process the named components (comma-separated, monorepo only)
|
|
31
31
|
--help Show this help message
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function pushCommand(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
|
+
import { pushRelease } from "./pushRelease.js";
|
|
3
|
+
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
4
|
+
const pushFlagSchema = {
|
|
5
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
6
|
+
only: { long: "--only", type: "string" },
|
|
7
|
+
tagsOnly: { long: "--tags-only", type: "boolean" }
|
|
8
|
+
};
|
|
9
|
+
async function pushCommand(argv) {
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = parseArgs(argv, pushFlagSchema);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const { dryRun, tagsOnly } = parsed.flags;
|
|
18
|
+
const only = parsed.flags.only?.split(",");
|
|
19
|
+
const resolvedTags = await resolveCommandTags(only);
|
|
20
|
+
if (resolvedTags.length === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const prefix = dryRun ? "[dry-run] Would push" : "Pushing";
|
|
24
|
+
if (!tagsOnly) {
|
|
25
|
+
console.info(`${prefix} branch and ${resolvedTags.length} tag(s):`);
|
|
26
|
+
} else {
|
|
27
|
+
console.info(`${prefix} ${resolvedTags.length} tag(s):`);
|
|
28
|
+
}
|
|
29
|
+
for (const { tag } of resolvedTags) {
|
|
30
|
+
console.info(` ${tag}`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const steps = pushRelease(resolvedTags, { dryRun, tagsOnly });
|
|
34
|
+
if (dryRun) {
|
|
35
|
+
for (const step of steps) {
|
|
36
|
+
console.info(`[dry-run] ${step.command.join(" ")}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
pushCommand
|
|
46
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
2
|
+
export interface PushReleaseOptions {
|
|
3
|
+
dryRun?: boolean;
|
|
4
|
+
tagsOnly?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface PushStep {
|
|
7
|
+
type: 'branch' | 'tag';
|
|
8
|
+
ref: string;
|
|
9
|
+
command: readonly [string, ...string[]];
|
|
10
|
+
}
|
|
11
|
+
export declare function pushRelease(resolvedTags: ResolvedTag[], options?: PushReleaseOptions): PushStep[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
function pushRelease(resolvedTags, options = {}) {
|
|
3
|
+
const { dryRun = false, tagsOnly = false } = options;
|
|
4
|
+
const steps = [];
|
|
5
|
+
if (!tagsOnly) {
|
|
6
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
7
|
+
encoding: "utf8",
|
|
8
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9
|
+
}).trim();
|
|
10
|
+
const command = ["git", "push", "origin", branch];
|
|
11
|
+
steps.push({ type: "branch", ref: branch, command });
|
|
12
|
+
}
|
|
13
|
+
for (const { tag } of resolvedTags) {
|
|
14
|
+
const command = ["git", "push", "--no-follow-tags", "origin", tag];
|
|
15
|
+
steps.push({ type: "tag", ref: tag, command });
|
|
16
|
+
}
|
|
17
|
+
if (!dryRun) {
|
|
18
|
+
for (const step of steps) {
|
|
19
|
+
execFileSync(step.command[0], step.command.slice(1), { stdio: "inherit" });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return steps;
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
pushRelease
|
|
26
|
+
};
|
|
@@ -12,7 +12,7 @@ permissions:
|
|
|
12
12
|
|
|
13
13
|
jobs:
|
|
14
14
|
sync:
|
|
15
|
-
uses: williamthorsen/node-monorepo-tools/.github/workflows/sync-labels.reusable.yaml@sync-labels-
|
|
15
|
+
uses: williamthorsen/node-monorepo-tools/.github/workflows/sync-labels.reusable.yaml@workflow/sync-labels-v1
|
|
16
16
|
`;
|
|
17
17
|
}
|
|
18
18
|
function buildScopeLabels(workspacePaths) {
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "4.
|
|
1
|
+
export declare const VERSION = "4.8.0";
|
package/dist/esm/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/release-kit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "Version-bumping and changelog-generation toolkit for release workflows",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/release-kit#readme",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"jiti": "2.6.1",
|
|
36
36
|
"js-yaml": "4.1.1",
|
|
37
37
|
"json-stringify-pretty-compact": "4.0.0",
|
|
38
|
-
"@williamthorsen/node-monorepo-core": "0.2.
|
|
38
|
+
"@williamthorsen/node-monorepo-core": "0.2.7"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/js-yaml": "4.0.9",
|
package/dist/esm/init/prompt.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function printStep(message) {
|
|
2
|
-
console.info(`
|
|
3
|
-
> ${message}`);
|
|
4
|
-
}
|
|
5
|
-
function printSuccess(message) {
|
|
6
|
-
console.info(` \u2705 ${message}`);
|
|
7
|
-
}
|
|
8
|
-
function printSkip(message) {
|
|
9
|
-
console.info(` \u26A0\uFE0F ${message}`);
|
|
10
|
-
}
|
|
11
|
-
function printError(message) {
|
|
12
|
-
console.error(` \u274C ${message}`);
|
|
13
|
-
}
|
|
14
|
-
export {
|
|
15
|
-
printError,
|
|
16
|
-
printSkip,
|
|
17
|
-
printStep,
|
|
18
|
-
printSuccess
|
|
19
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseType } from './types.ts';
|
|
2
|
-
export declare const RELEASE_TAGS_FILE = "tmp/.release-tags";
|
|
3
|
-
export declare function parseArgs(argv: string[]): {
|
|
4
|
-
dryRun: boolean;
|
|
5
|
-
force: boolean;
|
|
6
|
-
bumpOverride: ReleaseType | undefined;
|
|
7
|
-
only: string[] | undefined;
|
|
8
|
-
};
|
|
9
|
-
export declare function runReleasePrepare(config: MonorepoReleaseConfig | ReleaseConfig): void;
|
|
10
|
-
export declare function writeReleaseTags(tags: string[], dryRun: boolean): void;
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
import { dim } from "./format.js";
|
|
4
|
-
import { releasePrepare } from "./releasePrepare.js";
|
|
5
|
-
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
6
|
-
const RELEASE_TAGS_FILE = "tmp/.release-tags";
|
|
7
|
-
const VALID_BUMP_TYPES = ["major", "minor", "patch"];
|
|
8
|
-
function isReleaseType(value) {
|
|
9
|
-
return VALID_BUMP_TYPES.includes(value);
|
|
10
|
-
}
|
|
11
|
-
function isMonorepoConfig(config) {
|
|
12
|
-
return "components" in config;
|
|
13
|
-
}
|
|
14
|
-
function showHelp() {
|
|
15
|
-
console.info(`
|
|
16
|
-
Usage: runReleasePrepare [options]
|
|
17
|
-
|
|
18
|
-
Legacy entry point for release preparation. Prefer the CLI:
|
|
19
|
-
npx @williamthorsen/release-kit prepare
|
|
20
|
-
|
|
21
|
-
Options:
|
|
22
|
-
--dry-run Run without modifying any files
|
|
23
|
-
--bump=major|minor|patch Override the bump type for all components
|
|
24
|
-
--force Bypass the "no commits since last tag" check (monorepo only, requires --bump)
|
|
25
|
-
--only=name1,name2 Only process the named components (comma-separated, monorepo only)
|
|
26
|
-
--help Show this help message
|
|
27
|
-
`);
|
|
28
|
-
}
|
|
29
|
-
function parseArgs(argv) {
|
|
30
|
-
let dryRun = false;
|
|
31
|
-
let force = false;
|
|
32
|
-
let bumpOverride;
|
|
33
|
-
let only;
|
|
34
|
-
for (const arg of argv) {
|
|
35
|
-
if (arg === "--dry-run") {
|
|
36
|
-
dryRun = true;
|
|
37
|
-
} else if (arg === "--force") {
|
|
38
|
-
force = true;
|
|
39
|
-
} else if (arg.startsWith("--bump=")) {
|
|
40
|
-
const value = arg.slice("--bump=".length);
|
|
41
|
-
if (!isReleaseType(value)) {
|
|
42
|
-
console.error(`Error: Invalid bump type "${value}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
bumpOverride = value;
|
|
46
|
-
} else if (arg.startsWith("--only=")) {
|
|
47
|
-
const value = arg.slice("--only=".length);
|
|
48
|
-
if (!value) {
|
|
49
|
-
console.error("Error: --only requires a comma-separated list of component names");
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
only = value.split(",");
|
|
53
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
54
|
-
showHelp();
|
|
55
|
-
process.exit(0);
|
|
56
|
-
} else {
|
|
57
|
-
console.error(`Error: Unknown argument: ${arg}`);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (force && bumpOverride === void 0) {
|
|
62
|
-
console.error("Error: --force requires --bump to specify the version bump type");
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
return { dryRun, force, bumpOverride, only };
|
|
66
|
-
}
|
|
67
|
-
function runReleasePrepare(config) {
|
|
68
|
-
const { dryRun, force, bumpOverride, only } = parseArgs(process.argv.slice(2));
|
|
69
|
-
const options = {
|
|
70
|
-
dryRun,
|
|
71
|
-
force,
|
|
72
|
-
...bumpOverride === void 0 ? {} : { bumpOverride }
|
|
73
|
-
};
|
|
74
|
-
if (!isMonorepoConfig(config)) {
|
|
75
|
-
if (only !== void 0) {
|
|
76
|
-
console.error("Error: --only is only supported for monorepo configurations");
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
const tags = releasePrepare(config, options);
|
|
81
|
-
writeReleaseTags(tags, dryRun);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
let effectiveConfig = config;
|
|
89
|
-
if (only !== void 0) {
|
|
90
|
-
const knownNames = config.components.map((c) => c.dir);
|
|
91
|
-
for (const name of only) {
|
|
92
|
-
if (!knownNames.includes(name)) {
|
|
93
|
-
console.error(`Error: Unknown component "${name}". Known components: ${knownNames.join(", ")}`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const filtered = config.components.filter((c) => only.includes(c.dir));
|
|
98
|
-
effectiveConfig = { ...config, components: filtered };
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const tags = releasePrepareMono(effectiveConfig, options);
|
|
102
|
-
writeReleaseTags(tags, dryRun);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function writeReleaseTags(tags, dryRun) {
|
|
109
|
-
if (tags.length === 0) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (dryRun) {
|
|
113
|
-
console.info(dim(` [dry-run] Would write ${RELEASE_TAGS_FILE}: ${tags.join(" ")}`));
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
mkdirSync(dirname(RELEASE_TAGS_FILE), { recursive: true });
|
|
117
|
-
writeFileSync(RELEASE_TAGS_FILE, tags.join("\n"), "utf8");
|
|
118
|
-
console.info(dim(` Wrote ${RELEASE_TAGS_FILE}: ${tags.join(" ")}`));
|
|
119
|
-
}
|
|
120
|
-
export {
|
|
121
|
-
RELEASE_TAGS_FILE,
|
|
122
|
-
parseArgs,
|
|
123
|
-
runReleasePrepare,
|
|
124
|
-
writeReleaseTags
|
|
125
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
function writeIfAbsent(filePath, content, dryRun, overwrite) {
|
|
4
|
-
if (existsSync(filePath) && !overwrite) {
|
|
5
|
-
console.info(` Skipped ${filePath} (already exists)`);
|
|
6
|
-
return { action: "skipped", filePath };
|
|
7
|
-
}
|
|
8
|
-
if (dryRun) {
|
|
9
|
-
console.info(` [dry-run] Would create ${filePath}`);
|
|
10
|
-
return { action: "dry-run", filePath };
|
|
11
|
-
}
|
|
12
|
-
try {
|
|
13
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
14
|
-
writeFileSync(filePath, content, "utf8");
|
|
15
|
-
} catch (error) {
|
|
16
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
-
console.error(` Failed to write ${filePath}: ${message}`);
|
|
18
|
-
return { action: "failed", filePath };
|
|
19
|
-
}
|
|
20
|
-
console.info(` Created ${filePath}`);
|
|
21
|
-
return { action: "created", filePath };
|
|
22
|
-
}
|
|
23
|
-
export {
|
|
24
|
-
writeIfAbsent
|
|
25
|
-
};
|