@releasekit/release 0.3.1 → 0.4.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/README.md +22 -1
- package/dist/{chunk-J5NT6OX2.js → chunk-I5CGC253.js} +11 -11
- package/dist/{chunk-EDF3BZFF.js → chunk-WNKYXS62.js} +4 -3
- package/dist/{chunk-I5ABZWVU.js → chunk-YZHGXRG6.js} +28 -25
- package/dist/cli.js +3 -3
- package/dist/dispatcher.js +60 -4
- package/dist/index.js +2 -2
- package/docs/ci-setup.md +266 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @releasekit/release
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@releasekit/release)
|
|
4
|
+
[](https://www.npmjs.com/package/@releasekit/release)
|
|
5
|
+
[](https://www.npmjs.com/package/@releasekit/release)
|
|
6
|
+
|
|
7
|
+
**Unified release pipeline: version, changelog, and publish in a single command.**
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
@@ -71,6 +75,18 @@ If no releasable changes are found after step 1, the command exits with code 0 a
|
|
|
71
75
|
| `-q, --quiet` | Suppress non-error output | `false` |
|
|
72
76
|
| `--project-dir <path>` | Project directory | cwd |
|
|
73
77
|
|
|
78
|
+
### `releasekit init`
|
|
79
|
+
|
|
80
|
+
Create a default `releasekit.config.json` in the current directory.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
releasekit init [--force]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Detects monorepo layout and sets `changelog.mode` accordingly. Adds `access: "public"` only for scoped packages (`@scope/name`), which npm defaults to restricted.
|
|
87
|
+
|
|
88
|
+
Use `--force` to overwrite an existing config file.
|
|
89
|
+
|
|
74
90
|
### `releasekit preview`
|
|
75
91
|
|
|
76
92
|
Posts a release preview comment on a pull request showing what would be released if merged.
|
|
@@ -275,6 +291,11 @@ jobs:
|
|
|
275
291
|
|
|
276
292
|
A template is also available at [`templates/workflows/release-preview.yml`](../../templates/workflows/release-preview.yml).
|
|
277
293
|
|
|
294
|
+
## Documentation
|
|
295
|
+
|
|
296
|
+
**Getting Started**
|
|
297
|
+
- [CI Setup](./docs/ci-setup.md) — GitHub Actions workflows (push, label, OIDC, PR preview, prerelease)
|
|
298
|
+
|
|
278
299
|
## License
|
|
279
300
|
|
|
280
301
|
MIT
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
runRelease,
|
|
6
6
|
success,
|
|
7
7
|
warn
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YZHGXRG6.js";
|
|
9
9
|
|
|
10
10
|
// src/preview-context.ts
|
|
11
11
|
import * as fs from "fs";
|
|
@@ -98,15 +98,15 @@ var TYPE_LABELS = {
|
|
|
98
98
|
function getNoChangesMessage(strategy) {
|
|
99
99
|
switch (strategy) {
|
|
100
100
|
case "manual":
|
|
101
|
-
return "
|
|
101
|
+
return "Run the release workflow manually if a release is needed.";
|
|
102
102
|
case "direct":
|
|
103
|
-
return "
|
|
103
|
+
return "Merging this PR will not trigger a release.";
|
|
104
104
|
case "standing-pr":
|
|
105
|
-
return "
|
|
105
|
+
return "Merging this PR will not affect the release PR.";
|
|
106
106
|
case "scheduled":
|
|
107
|
-
return "
|
|
107
|
+
return "These changes will not be included in the next scheduled release.";
|
|
108
108
|
default:
|
|
109
|
-
return "
|
|
109
|
+
return "";
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
function getIntroMessage(strategy, standingPrNumber) {
|
|
@@ -125,20 +125,20 @@ function getLabelBanner(labelContext) {
|
|
|
125
125
|
if (!labelContext) return [];
|
|
126
126
|
if (labelContext.trigger === "commit") {
|
|
127
127
|
if (labelContext.skip) {
|
|
128
|
-
return [">
|
|
128
|
+
return ["> **Warning:** This PR is marked to skip release.", ""];
|
|
129
129
|
}
|
|
130
130
|
if (labelContext.bumpLabel === "major") {
|
|
131
|
-
return [">
|
|
131
|
+
return ["> **Important:** This PR is labeled for a **major** release.", ""];
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
if (labelContext.trigger === "label") {
|
|
135
135
|
if (labelContext.noBumpLabel) {
|
|
136
136
|
const labels = labelContext.labels;
|
|
137
137
|
const labelExamples = labels ? `\`${labels.patch}\`, \`${labels.minor}\`, or \`${labels.major}\`` : "a release label (e.g., `release:patch`, `release:minor`, `release:major`)";
|
|
138
|
-
return [">
|
|
138
|
+
return ["> No release label detected.", `> **Note:** Add ${labelExamples} to trigger a release.`, ""];
|
|
139
139
|
}
|
|
140
140
|
if (labelContext.bumpLabel) {
|
|
141
|
-
return [
|
|
141
|
+
return [`> This PR is labeled for a **${labelContext.bumpLabel}** release.`, ""];
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
return [];
|
|
@@ -152,7 +152,7 @@ function formatPreviewComment(result, options) {
|
|
|
152
152
|
lines.push("<details>", "<summary><b>Release Preview</b> \u2014 no release</summary>", "");
|
|
153
153
|
lines.push(...banner);
|
|
154
154
|
if (!labelContext?.noBumpLabel) {
|
|
155
|
-
lines.push(
|
|
155
|
+
lines.push(`> **Note:** No releasable changes detected. ${getNoChangesMessage(strategy)}`);
|
|
156
156
|
}
|
|
157
157
|
lines.push("", "---", FOOTER, "</details>");
|
|
158
158
|
return lines.join("\n");
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EXIT_CODES,
|
|
3
3
|
runRelease
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YZHGXRG6.js";
|
|
5
5
|
|
|
6
6
|
// src/release-command.ts
|
|
7
|
-
import { Command } from "commander";
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
8
|
function createReleaseCommand() {
|
|
9
|
-
return new Command("release").description("Run the full release pipeline").option("-c, --config <path>", "Path to config file").option("-d, --dry-run", "Preview all steps without side effects", false).option("-b, --bump <type>", "Force bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --sync", "Use synchronized versioning across all packages", false).option("-t, --target <packages>", "Target specific packages (comma-separated)").option("--branch <name>", "Override the git branch used for push").option("--skip-notes", "Skip changelog generation", false).option("--skip-publish", "Skip registry publishing and git operations", false).option("--skip-git", "Skip git commit/tag/push", false).option("--skip-github-release", "Skip GitHub release creation", false).option("--skip-verification", "Skip post-publish verification", false).option("-j, --json", "Output results as JSON", false).option("-v, --verbose", "Verbose logging", false).option("-q, --quiet", "Suppress non-error output", false).option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
9
|
+
return new Command("release").description("Run the full release pipeline").option("-c, --config <path>", "Path to config file").option("-d, --dry-run", "Preview all steps without side effects", false).option("-b, --bump <type>", "Force bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --sync", "Use synchronized versioning across all packages", false).option("-t, --target <packages>", "Target specific packages (comma-separated)").option("--branch <name>", "Override the git branch used for push").addOption(new Option("--npm-auth <method>", "NPM auth method").choices(["auto", "oidc", "token"]).default("auto")).option("--skip-notes", "Skip changelog generation", false).option("--skip-publish", "Skip registry publishing and git operations", false).option("--skip-git", "Skip git commit/tag/push", false).option("--skip-github-release", "Skip GitHub release creation", false).option("--skip-verification", "Skip post-publish verification", false).option("-j, --json", "Output results as JSON", false).option("-v, --verbose", "Verbose logging", false).option("-q, --quiet", "Suppress non-error output", false).option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
10
10
|
const options = {
|
|
11
11
|
config: opts.config,
|
|
12
12
|
dryRun: opts.dryRun,
|
|
@@ -15,6 +15,7 @@ function createReleaseCommand() {
|
|
|
15
15
|
sync: opts.sync,
|
|
16
16
|
target: opts.target,
|
|
17
17
|
branch: opts.branch,
|
|
18
|
+
npmAuth: opts.npmAuth,
|
|
18
19
|
skipNotes: opts.skipNotes,
|
|
19
20
|
skipPublish: opts.skipPublish,
|
|
20
21
|
skipGit: opts.skipGit,
|
|
@@ -208,14 +208,14 @@ var GitHubReleaseConfigSchema = z.object({
|
|
|
208
208
|
perPackage: z.boolean().default(true),
|
|
209
209
|
prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
|
|
210
210
|
/**
|
|
211
|
-
* Controls
|
|
212
|
-
* - 'auto': Use
|
|
213
|
-
*
|
|
214
|
-
* - '
|
|
215
|
-
* - '
|
|
216
|
-
* -
|
|
211
|
+
* Controls the source for the GitHub release body.
|
|
212
|
+
* - 'auto': Use release notes if enabled, else changelog, else GitHub auto-generated.
|
|
213
|
+
* - 'releaseNotes': Use LLM-generated release notes (requires notes.releaseNotes.enabled: true).
|
|
214
|
+
* - 'changelog': Use formatted changelog entries.
|
|
215
|
+
* - 'generated': Use GitHub's auto-generated notes.
|
|
216
|
+
* - 'none': No body.
|
|
217
217
|
*/
|
|
218
|
-
|
|
218
|
+
body: z.enum(["auto", "releaseNotes", "changelog", "generated", "none"]).default("auto")
|
|
219
219
|
});
|
|
220
220
|
var VerifyRegistryConfigSchema = z.object({
|
|
221
221
|
enabled: z.boolean().default(true),
|
|
@@ -259,7 +259,7 @@ var PublishConfigSchema = z.object({
|
|
|
259
259
|
draft: true,
|
|
260
260
|
perPackage: true,
|
|
261
261
|
prerelease: "auto",
|
|
262
|
-
|
|
262
|
+
body: "auto"
|
|
263
263
|
}),
|
|
264
264
|
verify: VerifyConfigSchema.default({
|
|
265
265
|
npm: {
|
|
@@ -280,10 +280,10 @@ var TemplateConfigSchema = z.object({
|
|
|
280
280
|
path: z.string().optional(),
|
|
281
281
|
engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
|
|
282
282
|
});
|
|
283
|
-
var
|
|
284
|
-
|
|
283
|
+
var LocationModeSchema = z.enum(["root", "packages", "both"]);
|
|
284
|
+
var ChangelogConfigSchema = z.object({
|
|
285
|
+
mode: LocationModeSchema.optional(),
|
|
285
286
|
file: z.string().optional(),
|
|
286
|
-
options: z.record(z.string(), z.unknown()).optional(),
|
|
287
287
|
templates: TemplateConfigSchema.optional()
|
|
288
288
|
});
|
|
289
289
|
var LLMOptionsSchema = z.object({
|
|
@@ -343,17 +343,20 @@ var LLMConfigSchema = z.object({
|
|
|
343
343
|
scopes: ScopeConfigSchema.optional(),
|
|
344
344
|
prompts: LLMPromptsConfigSchema.optional()
|
|
345
345
|
});
|
|
346
|
+
var ReleaseNotesConfigSchema = z.object({
|
|
347
|
+
mode: LocationModeSchema.optional(),
|
|
348
|
+
file: z.string().optional(),
|
|
349
|
+
templates: TemplateConfigSchema.optional(),
|
|
350
|
+
llm: LLMConfigSchema.optional()
|
|
351
|
+
});
|
|
346
352
|
var NotesInputConfigSchema = z.object({
|
|
347
353
|
source: z.string().optional(),
|
|
348
354
|
file: z.string().optional()
|
|
349
355
|
});
|
|
350
356
|
var NotesConfigSchema = z.object({
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
templates: TemplateConfigSchema.optional(),
|
|
355
|
-
llm: LLMConfigSchema.optional(),
|
|
356
|
-
updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
|
|
357
|
+
changelog: z.union([z.literal(false), ChangelogConfigSchema]).optional(),
|
|
358
|
+
releaseNotes: z.union([z.literal(false), ReleaseNotesConfigSchema]).optional(),
|
|
359
|
+
updateStrategy: z.enum(["prepend", "regenerate"]).optional()
|
|
357
360
|
});
|
|
358
361
|
var CILabelsConfigSchema = z.object({
|
|
359
362
|
stable: z.string().default("release:stable"),
|
|
@@ -554,11 +557,13 @@ async function runRelease(inputOptions) {
|
|
|
554
557
|
}
|
|
555
558
|
let notesGenerated = false;
|
|
556
559
|
let packageNotes;
|
|
560
|
+
let releaseNotes;
|
|
557
561
|
let notesFiles = [];
|
|
558
562
|
if (!options.skipNotes) {
|
|
559
563
|
info("Generating release notes...");
|
|
560
564
|
const notesResult = await runNotesStep(versionOutput, options);
|
|
561
565
|
packageNotes = notesResult.packageNotes;
|
|
566
|
+
releaseNotes = notesResult.releaseNotes;
|
|
562
567
|
notesFiles = notesResult.files;
|
|
563
568
|
notesGenerated = true;
|
|
564
569
|
success("Release notes generated");
|
|
@@ -566,10 +571,10 @@ async function runRelease(inputOptions) {
|
|
|
566
571
|
let publishOutput;
|
|
567
572
|
if (!options.skipPublish) {
|
|
568
573
|
info("Publishing...");
|
|
569
|
-
publishOutput = await runPublishStep(versionOutput, options, packageNotes, notesFiles);
|
|
574
|
+
publishOutput = await runPublishStep(versionOutput, options, releaseNotes ?? packageNotes, notesFiles);
|
|
570
575
|
success("Publish complete");
|
|
571
576
|
}
|
|
572
|
-
return { versionOutput, notesGenerated, packageNotes, publishOutput };
|
|
577
|
+
return { versionOutput, notesGenerated, packageNotes, releaseNotes, publishOutput };
|
|
573
578
|
}
|
|
574
579
|
async function runVersionStep(options) {
|
|
575
580
|
const { loadConfig: loadConfig2, VersionEngine, enableJsonOutput, getJsonData } = await import("@releasekit/version");
|
|
@@ -605,14 +610,11 @@ async function runVersionStep(options) {
|
|
|
605
610
|
return getJsonData();
|
|
606
611
|
}
|
|
607
612
|
async function runNotesStep(versionOutput, options) {
|
|
608
|
-
const { parseVersionOutput, runPipeline, loadConfig: loadConfig2
|
|
613
|
+
const { parseVersionOutput, runPipeline, loadConfig: loadConfig2 } = await import("@releasekit/notes");
|
|
609
614
|
const config = loadConfig2(options.projectDir, options.config);
|
|
610
|
-
if (config.output.length === 0) {
|
|
611
|
-
config.output = getDefaultConfig().output;
|
|
612
|
-
}
|
|
613
615
|
const input = parseVersionOutput(JSON.stringify(versionOutput));
|
|
614
616
|
const result = await runPipeline(input, config, options.dryRun);
|
|
615
|
-
return { packageNotes: result.packageNotes, files: result.files };
|
|
617
|
+
return { packageNotes: result.packageNotes, releaseNotes: result.releaseNotes, files: result.files };
|
|
616
618
|
}
|
|
617
619
|
async function runPublishStep(versionOutput, options, releaseNotes, additionalFiles) {
|
|
618
620
|
const { runPipeline, loadConfig: loadConfig2 } = await import("@releasekit/publish");
|
|
@@ -623,7 +625,7 @@ async function runPublishStep(versionOutput, options, releaseNotes, additionalFi
|
|
|
623
625
|
const publishOptions = {
|
|
624
626
|
dryRun: options.dryRun,
|
|
625
627
|
registry: "all",
|
|
626
|
-
npmAuth: "auto",
|
|
628
|
+
npmAuth: options.npmAuth ?? "auto",
|
|
627
629
|
skipGit: options.skipGit,
|
|
628
630
|
skipPublish: false,
|
|
629
631
|
skipGithubRelease: options.skipGithubRelease,
|
|
@@ -638,6 +640,7 @@ async function runPublishStep(versionOutput, options, releaseNotes, additionalFi
|
|
|
638
640
|
|
|
639
641
|
export {
|
|
640
642
|
readPackageVersion,
|
|
643
|
+
error,
|
|
641
644
|
warn,
|
|
642
645
|
info,
|
|
643
646
|
success,
|
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createReleaseCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WNKYXS62.js";
|
|
5
5
|
import {
|
|
6
6
|
runPreview
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-I5CGC253.js";
|
|
8
8
|
import {
|
|
9
9
|
EXIT_CODES,
|
|
10
10
|
readPackageVersion
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YZHGXRG6.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { realpathSync } from "fs";
|
package/dist/dispatcher.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createReleaseCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WNKYXS62.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
EXIT_CODES,
|
|
7
|
+
error,
|
|
8
|
+
info,
|
|
9
|
+
readPackageVersion,
|
|
10
|
+
success
|
|
11
|
+
} from "./chunk-YZHGXRG6.js";
|
|
8
12
|
|
|
9
13
|
// src/dispatcher.ts
|
|
10
14
|
import { realpathSync } from "fs";
|
|
@@ -12,10 +16,62 @@ import { fileURLToPath } from "url";
|
|
|
12
16
|
import { createNotesCommand } from "@releasekit/notes/cli";
|
|
13
17
|
import { createPublishCommand } from "@releasekit/publish/cli";
|
|
14
18
|
import { createVersionCommand } from "@releasekit/version/cli";
|
|
19
|
+
import { Command as Command2 } from "commander";
|
|
20
|
+
|
|
21
|
+
// src/init-command.ts
|
|
22
|
+
import * as fs from "fs";
|
|
23
|
+
import { detectMonorepo } from "@releasekit/notes";
|
|
15
24
|
import { Command } from "commander";
|
|
25
|
+
function createInitCommand() {
|
|
26
|
+
return new Command("init").description("Create a default releasekit.config.json").option("-f, --force", "Overwrite existing config").action((options) => {
|
|
27
|
+
const configPath = "releasekit.config.json";
|
|
28
|
+
if (fs.existsSync(configPath) && !options.force) {
|
|
29
|
+
error(`Config file already exists at ${configPath}. Use --force to overwrite.`);
|
|
30
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
31
|
+
} else {
|
|
32
|
+
let changelogMode;
|
|
33
|
+
try {
|
|
34
|
+
const detected = detectMonorepo(process.cwd());
|
|
35
|
+
changelogMode = detected.isMonorepo ? "packages" : "root";
|
|
36
|
+
info(
|
|
37
|
+
detected.isMonorepo ? "Monorepo detected \u2014 using mode: packages" : "Single-package repo detected \u2014 using mode: root"
|
|
38
|
+
);
|
|
39
|
+
} catch {
|
|
40
|
+
changelogMode = "root";
|
|
41
|
+
info("Could not detect project type \u2014 using mode: root");
|
|
42
|
+
}
|
|
43
|
+
let packageName;
|
|
44
|
+
try {
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8"));
|
|
46
|
+
packageName = pkg.name;
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
const isScoped = packageName?.startsWith("@") ?? false;
|
|
50
|
+
const defaultConfig = {
|
|
51
|
+
$schema: "https://goosewobbler.github.io/releasekit/schema.json",
|
|
52
|
+
notes: {
|
|
53
|
+
changelog: {
|
|
54
|
+
mode: changelogMode
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
publish: {
|
|
58
|
+
npm: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
...isScoped ? { access: "public" } : {}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
65
|
+
success(`Created ${configPath}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/dispatcher.ts
|
|
16
71
|
function createDispatcherProgram() {
|
|
17
|
-
const program = new
|
|
72
|
+
const program = new Command2().name("releasekit").description("Unified release pipeline: version, changelog, and publish").version(readPackageVersion(import.meta.url));
|
|
18
73
|
program.addCommand(createReleaseCommand(), { isDefault: true });
|
|
74
|
+
program.addCommand(createInitCommand());
|
|
19
75
|
program.addCommand(createVersionCommand());
|
|
20
76
|
program.addCommand(createNotesCommand());
|
|
21
77
|
program.addCommand(createPublishCommand());
|
package/dist/index.js
CHANGED
package/docs/ci-setup.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# CI Setup
|
|
2
|
+
|
|
3
|
+
This guide covers common GitHub Actions patterns for automating releases with `@releasekit/release`.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
All workflows require:
|
|
8
|
+
|
|
9
|
+
- `fetch-depth: 0` on checkout — ReleaseKit reads git history to determine version bumps
|
|
10
|
+
- A `GITHUB_TOKEN` with `contents: write` permission for tagging and GitHub Releases
|
|
11
|
+
- Node.js 20+
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Minimal Setup (push to main)
|
|
16
|
+
|
|
17
|
+
Trigger a release on every push to `main`. If there are no releasable commits, the command exits cleanly with code 0 and does nothing.
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
# .github/workflows/release.yml
|
|
21
|
+
name: Release
|
|
22
|
+
|
|
23
|
+
on:
|
|
24
|
+
push:
|
|
25
|
+
branches: [main]
|
|
26
|
+
|
|
27
|
+
permissions:
|
|
28
|
+
contents: write
|
|
29
|
+
id-token: write # for npm OIDC trusted publishing
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
release:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
with:
|
|
37
|
+
fetch-depth: 0
|
|
38
|
+
|
|
39
|
+
- uses: actions/setup-node@v4
|
|
40
|
+
with:
|
|
41
|
+
node-version: '20'
|
|
42
|
+
registry-url: 'https://registry.npmjs.org'
|
|
43
|
+
|
|
44
|
+
- run: npm ci
|
|
45
|
+
|
|
46
|
+
- run: npx releasekit release
|
|
47
|
+
env:
|
|
48
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
# For OIDC trusted publishing (recommended) — no NPM_TOKEN needed.
|
|
50
|
+
# For token-based publishing: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Label-Based Trigger
|
|
56
|
+
|
|
57
|
+
Only release when a PR is merged with a release label. Conventional commits determine the changelog entries; the label controls whether and at what level to bump.
|
|
58
|
+
|
|
59
|
+
**Required labels** (customisable in config):
|
|
60
|
+
|
|
61
|
+
| Label | Effect |
|
|
62
|
+
|-------|--------|
|
|
63
|
+
| `release:patch` | Bump patch version |
|
|
64
|
+
| `release:minor` | Bump minor version |
|
|
65
|
+
| `release:major` | Bump major version |
|
|
66
|
+
| `release:stable` | Graduate a prerelease to stable |
|
|
67
|
+
| `release:prerelease` | Create a prerelease |
|
|
68
|
+
| `release:skip` | Suppress release on this PR |
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
# .github/workflows/release.yml
|
|
72
|
+
name: Release
|
|
73
|
+
|
|
74
|
+
on:
|
|
75
|
+
push:
|
|
76
|
+
branches: [main]
|
|
77
|
+
|
|
78
|
+
permissions:
|
|
79
|
+
contents: write
|
|
80
|
+
id-token: write
|
|
81
|
+
pull-requests: read
|
|
82
|
+
|
|
83
|
+
jobs:
|
|
84
|
+
release:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
with:
|
|
89
|
+
fetch-depth: 0
|
|
90
|
+
|
|
91
|
+
- uses: actions/setup-node@v4
|
|
92
|
+
with:
|
|
93
|
+
node-version: '20'
|
|
94
|
+
registry-url: 'https://registry.npmjs.org'
|
|
95
|
+
|
|
96
|
+
- run: npm ci
|
|
97
|
+
|
|
98
|
+
- run: npx releasekit release
|
|
99
|
+
env:
|
|
100
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Configure the trigger in `releasekit.config.json`:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"ci": {
|
|
108
|
+
"releaseTrigger": "label",
|
|
109
|
+
"labels": {
|
|
110
|
+
"major": "release:major",
|
|
111
|
+
"minor": "release:minor",
|
|
112
|
+
"patch": "release:patch",
|
|
113
|
+
"skip": "release:skip"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Without a `release:patch/minor/major` label on the merged PR, no release is triggered. The `labels` block shown above reflects the defaults — omit it if your repository already uses those label names.
|
|
120
|
+
|
|
121
|
+
See [@releasekit/release — CI Configuration](../README.md#ci-configuration) for all `ci.*` options.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## PR Preview Comments
|
|
126
|
+
|
|
127
|
+
Post a comment on every PR showing what would be released if merged. Requires `pull-requests: write`.
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
# .github/workflows/release-preview.yml
|
|
131
|
+
name: Release Preview
|
|
132
|
+
|
|
133
|
+
on:
|
|
134
|
+
pull_request:
|
|
135
|
+
branches: [main]
|
|
136
|
+
types: [opened, synchronize, labeled, unlabeled]
|
|
137
|
+
|
|
138
|
+
concurrency:
|
|
139
|
+
group: release-preview-${{ github.event.pull_request.number }}
|
|
140
|
+
cancel-in-progress: true
|
|
141
|
+
|
|
142
|
+
permissions:
|
|
143
|
+
pull-requests: write
|
|
144
|
+
contents: read
|
|
145
|
+
|
|
146
|
+
jobs:
|
|
147
|
+
preview:
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
steps:
|
|
150
|
+
- uses: actions/checkout@v4
|
|
151
|
+
with:
|
|
152
|
+
fetch-depth: 0
|
|
153
|
+
|
|
154
|
+
- uses: actions/setup-node@v4
|
|
155
|
+
with:
|
|
156
|
+
node-version: '20'
|
|
157
|
+
|
|
158
|
+
- run: npm ci
|
|
159
|
+
|
|
160
|
+
- run: npx releasekit preview
|
|
161
|
+
env:
|
|
162
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A ready-to-use template is available at [`templates/workflows/release-preview.yml`](../../../templates/workflows/release-preview.yml).
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## npm OIDC Trusted Publishing (Recommended)
|
|
170
|
+
|
|
171
|
+
With OIDC, no `NPM_TOKEN` secret is required. The workflow exchanges a GitHub-issued OIDC token for a short-lived npm token at publish time.
|
|
172
|
+
|
|
173
|
+
**Requirements:**
|
|
174
|
+
- npm `>=9.5.0`
|
|
175
|
+
- Each package must have an **Automation policy** configured at npmjs.com (Settings → Automation policies → add a GitHub Actions OIDC publisher for your repo and workflow)
|
|
176
|
+
|
|
177
|
+
**Important:** `actions/setup-node` with `registry-url` writes a project `.npmrc` that injects `_authToken=${NODE_AUTH_TOKEN}`. When `NODE_AUTH_TOKEN` is unset, npm resolves this to an empty token and fails with `ENEEDAUTH` instead of falling through to the OIDC exchange. Delete the file before publishing:
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
permissions:
|
|
181
|
+
contents: write
|
|
182
|
+
id-token: write # grants the OIDC token
|
|
183
|
+
|
|
184
|
+
steps:
|
|
185
|
+
- uses: actions/setup-node@v4
|
|
186
|
+
with:
|
|
187
|
+
node-version: '20'
|
|
188
|
+
registry-url: 'https://registry.npmjs.org'
|
|
189
|
+
|
|
190
|
+
- run: npx releasekit release
|
|
191
|
+
env:
|
|
192
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
193
|
+
# No NPM_TOKEN needed with OIDC
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
ReleaseKit detects OIDC availability automatically (`npm-auth: auto`). To force it:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"publish": {
|
|
201
|
+
"npm": { "auth": "oidc" }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Prerelease Workflow
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
# Manual dispatch for prereleases
|
|
212
|
+
on:
|
|
213
|
+
workflow_dispatch:
|
|
214
|
+
inputs:
|
|
215
|
+
prerelease:
|
|
216
|
+
description: 'Prerelease identifier (e.g. beta, rc)'
|
|
217
|
+
required: true
|
|
218
|
+
default: 'beta'
|
|
219
|
+
|
|
220
|
+
jobs:
|
|
221
|
+
prerelease:
|
|
222
|
+
runs-on: ubuntu-latest
|
|
223
|
+
steps:
|
|
224
|
+
- uses: actions/checkout@v4
|
|
225
|
+
with:
|
|
226
|
+
fetch-depth: 0
|
|
227
|
+
|
|
228
|
+
- uses: actions/setup-node@v4
|
|
229
|
+
with:
|
|
230
|
+
node-version: '20'
|
|
231
|
+
registry-url: 'https://registry.npmjs.org'
|
|
232
|
+
|
|
233
|
+
- run: npm ci
|
|
234
|
+
|
|
235
|
+
- run: npx releasekit release --prerelease ${{ inputs.prerelease }}
|
|
236
|
+
env:
|
|
237
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Monorepo Targeted Release
|
|
243
|
+
|
|
244
|
+
Release only specific packages:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npx releasekit release --target @myorg/core,@myorg/cli
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Or version all packages together:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npx releasekit release --sync
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Dry Run in CI
|
|
259
|
+
|
|
260
|
+
Useful for verifying pipeline setup before enabling real releases:
|
|
261
|
+
|
|
262
|
+
```yaml
|
|
263
|
+
- run: npx releasekit release --dry-run --json
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`--dry-run` prints what would happen without modifying any files, creating tags, or publishing packages. `--json` emits structured output for inspection.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@releasekit/release",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Unified release pipeline: version, changelog, and publish in a single command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"license": "MIT",
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
+
"docs",
|
|
40
41
|
"README.md",
|
|
41
42
|
"LICENSE"
|
|
42
43
|
],
|
|
@@ -49,9 +50,9 @@
|
|
|
49
50
|
"commander": "^14.0.3",
|
|
50
51
|
"smol-toml": "^1.6.1",
|
|
51
52
|
"zod": "^4.3.6",
|
|
52
|
-
"@releasekit/notes": "0.
|
|
53
|
-
"@releasekit/
|
|
54
|
-
"@releasekit/
|
|
53
|
+
"@releasekit/notes": "0.4.0",
|
|
54
|
+
"@releasekit/publish": "0.4.0",
|
|
55
|
+
"@releasekit/version": "0.4.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@biomejs/biome": "^2.4.6",
|
|
@@ -61,8 +62,8 @@
|
|
|
61
62
|
"tsup": "^8.5.1",
|
|
62
63
|
"typescript": "^5.9.3",
|
|
63
64
|
"vitest": "^4.1.0",
|
|
64
|
-
"@releasekit/
|
|
65
|
-
"@releasekit/
|
|
65
|
+
"@releasekit/config": "0.0.0",
|
|
66
|
+
"@releasekit/core": "0.0.0"
|
|
66
67
|
},
|
|
67
68
|
"engines": {
|
|
68
69
|
"node": ">=20"
|