@releasekit/publish 0.3.1 → 0.4.1
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 +14 -3
- package/dist/{chunk-GIMIZS5B.js → chunk-ZMMZ7S6G.js} +87 -63
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/docs/github-releases.md +177 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @releasekit/publish
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@releasekit/publish)
|
|
4
|
+
[](https://www.npmjs.com/package/@releasekit/publish)
|
|
5
|
+
[](https://www.npmjs.com/package/@releasekit/publish)
|
|
6
|
+
|
|
7
|
+
**Publish packages to npm and crates.io with git tagging and GitHub releases.**
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
@@ -50,6 +54,8 @@ The publish pipeline runs in order:
|
|
|
50
54
|
8. **Git Push** - Push commits and tags to remote
|
|
51
55
|
9. **GitHub Release** - Create GitHub releases
|
|
52
56
|
|
|
57
|
+
The pipeline is **fail-fast**: the first package publish failure throws immediately. Git push and GitHub release are skipped, so the version commit and tag remain local until the issue is fixed and the release is retried.
|
|
58
|
+
|
|
53
59
|
## CLI Reference
|
|
54
60
|
|
|
55
61
|
| Flag | Description | Default |
|
|
@@ -59,7 +65,7 @@ The publish pipeline runs in order:
|
|
|
59
65
|
| `--registry <type>` | Registry to publish to: `npm`, `cargo`, `all` | `all` |
|
|
60
66
|
| `--npm-auth <method>` | NPM auth method: `oidc`, `token`, `auto` | `auto` |
|
|
61
67
|
| `--dry-run` | Simulate all operations | `false` |
|
|
62
|
-
| `--skip-git` | Skip git commit/tag/push | `false` |
|
|
68
|
+
| `--skip-git` | Skip git commit/tag/push (also skips GitHub release — no tag to release against) | `false` |
|
|
63
69
|
| `--skip-publish` | Skip registry publishing | `false` |
|
|
64
70
|
| `--skip-github-release` | Skip GitHub Release creation | `false` |
|
|
65
71
|
| `--skip-verification` | Skip post-publish verification | `false` |
|
|
@@ -126,7 +132,7 @@ Configure via `releasekit.config.json`:
|
|
|
126
132
|
"githubRelease": {
|
|
127
133
|
"enabled": true,
|
|
128
134
|
"draft": true,
|
|
129
|
-
"
|
|
135
|
+
"body": "auto"
|
|
130
136
|
},
|
|
131
137
|
"verify": {
|
|
132
138
|
"npm": {
|
|
@@ -144,6 +150,11 @@ Configure via `releasekit.config.json`:
|
|
|
144
150
|
|
|
145
151
|
See [@releasekit/config](../config/README.md) for full configuration options.
|
|
146
152
|
|
|
153
|
+
## Documentation
|
|
154
|
+
|
|
155
|
+
**Guides**
|
|
156
|
+
- [GitHub Releases](./docs/github-releases.md) — release body options, LLM prose, draft workflow
|
|
157
|
+
|
|
147
158
|
## License
|
|
148
159
|
|
|
149
160
|
MIT
|
|
@@ -223,14 +223,14 @@ var GitHubReleaseConfigSchema = z.object({
|
|
|
223
223
|
perPackage: z.boolean().default(true),
|
|
224
224
|
prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
|
|
225
225
|
/**
|
|
226
|
-
* Controls
|
|
227
|
-
* - 'auto': Use
|
|
228
|
-
*
|
|
229
|
-
* - '
|
|
230
|
-
* - '
|
|
231
|
-
* -
|
|
226
|
+
* Controls the source for the GitHub release body.
|
|
227
|
+
* - 'auto': Use release notes if enabled, else changelog, else GitHub auto-generated.
|
|
228
|
+
* - 'releaseNotes': Use LLM-generated release notes (requires notes.releaseNotes.enabled: true).
|
|
229
|
+
* - 'changelog': Use formatted changelog entries.
|
|
230
|
+
* - 'generated': Use GitHub's auto-generated notes.
|
|
231
|
+
* - 'none': No body.
|
|
232
232
|
*/
|
|
233
|
-
|
|
233
|
+
body: z.enum(["auto", "releaseNotes", "changelog", "generated", "none"]).default("auto")
|
|
234
234
|
});
|
|
235
235
|
var VerifyRegistryConfigSchema = z.object({
|
|
236
236
|
enabled: z.boolean().default(true),
|
|
@@ -274,7 +274,7 @@ var PublishConfigSchema = z.object({
|
|
|
274
274
|
draft: true,
|
|
275
275
|
perPackage: true,
|
|
276
276
|
prerelease: "auto",
|
|
277
|
-
|
|
277
|
+
body: "auto"
|
|
278
278
|
}),
|
|
279
279
|
verify: VerifyConfigSchema.default({
|
|
280
280
|
npm: {
|
|
@@ -295,10 +295,10 @@ var TemplateConfigSchema = z.object({
|
|
|
295
295
|
path: z.string().optional(),
|
|
296
296
|
engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
|
|
297
297
|
});
|
|
298
|
-
var
|
|
299
|
-
|
|
298
|
+
var LocationModeSchema = z.enum(["root", "packages", "both"]);
|
|
299
|
+
var ChangelogConfigSchema = z.object({
|
|
300
|
+
mode: LocationModeSchema.optional(),
|
|
300
301
|
file: z.string().optional(),
|
|
301
|
-
options: z.record(z.string(), z.unknown()).optional(),
|
|
302
302
|
templates: TemplateConfigSchema.optional()
|
|
303
303
|
});
|
|
304
304
|
var LLMOptionsSchema = z.object({
|
|
@@ -358,17 +358,20 @@ var LLMConfigSchema = z.object({
|
|
|
358
358
|
scopes: ScopeConfigSchema.optional(),
|
|
359
359
|
prompts: LLMPromptsConfigSchema.optional()
|
|
360
360
|
});
|
|
361
|
+
var ReleaseNotesConfigSchema = z.object({
|
|
362
|
+
mode: LocationModeSchema.optional(),
|
|
363
|
+
file: z.string().optional(),
|
|
364
|
+
templates: TemplateConfigSchema.optional(),
|
|
365
|
+
llm: LLMConfigSchema.optional()
|
|
366
|
+
});
|
|
361
367
|
var NotesInputConfigSchema = z.object({
|
|
362
368
|
source: z.string().optional(),
|
|
363
369
|
file: z.string().optional()
|
|
364
370
|
});
|
|
365
371
|
var NotesConfigSchema = z.object({
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
templates: TemplateConfigSchema.optional(),
|
|
370
|
-
llm: LLMConfigSchema.optional(),
|
|
371
|
-
updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
|
|
372
|
+
changelog: z.union([z.literal(false), ChangelogConfigSchema]).optional(),
|
|
373
|
+
releaseNotes: z.union([z.literal(false), ReleaseNotesConfigSchema]).optional(),
|
|
374
|
+
updateStrategy: z.enum(["prepend", "regenerate"]).optional()
|
|
372
375
|
});
|
|
373
376
|
var CILabelsConfigSchema = z.object({
|
|
374
377
|
stable: z.string().default("release:stable"),
|
|
@@ -544,7 +547,7 @@ function getDefaultConfig() {
|
|
|
544
547
|
draft: true,
|
|
545
548
|
perPackage: true,
|
|
546
549
|
prerelease: "auto",
|
|
547
|
-
|
|
550
|
+
body: "auto"
|
|
548
551
|
},
|
|
549
552
|
verify: {
|
|
550
553
|
npm: {
|
|
@@ -594,7 +597,7 @@ function toPublishConfig(config) {
|
|
|
594
597
|
draft: config.githubRelease?.draft ?? defaults.githubRelease.draft,
|
|
595
598
|
perPackage: config.githubRelease?.perPackage ?? defaults.githubRelease.perPackage,
|
|
596
599
|
prerelease: config.githubRelease?.prerelease ?? defaults.githubRelease.prerelease,
|
|
597
|
-
|
|
600
|
+
body: config.githubRelease?.body ?? defaults.githubRelease.body
|
|
598
601
|
},
|
|
599
602
|
verify: {
|
|
600
603
|
npm: {
|
|
@@ -977,11 +980,15 @@ async function runCargoPublishStage(ctx) {
|
|
|
977
980
|
if (!dryRun) {
|
|
978
981
|
success(`Published ${crate.name}@${crate.version} to crates.io`);
|
|
979
982
|
}
|
|
983
|
+
ctx.output.cargo.push(result);
|
|
980
984
|
} catch (error) {
|
|
981
985
|
result.reason = error instanceof Error ? error.message : String(error);
|
|
982
|
-
|
|
986
|
+
ctx.output.cargo.push(result);
|
|
987
|
+
throw createPublishError(
|
|
988
|
+
"CARGO_PUBLISH_ERROR" /* CARGO_PUBLISH_ERROR */,
|
|
989
|
+
`${crate.name}@${crate.version}: ${result.reason}`
|
|
990
|
+
);
|
|
983
991
|
}
|
|
984
|
-
ctx.output.cargo.push(result);
|
|
985
992
|
}
|
|
986
993
|
}
|
|
987
994
|
function findCrates(updates, _cwd) {
|
|
@@ -1237,21 +1244,34 @@ async function runGitPushStage(ctx) {
|
|
|
1237
1244
|
}
|
|
1238
1245
|
|
|
1239
1246
|
// src/stages/github-release.ts
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
if (notesSetting === "none") {
|
|
1247
|
+
function resolveNotes(bodySource, tag, changelogs, releaseNotesEnabled, pipelineNotes) {
|
|
1248
|
+
if (bodySource === "none") {
|
|
1243
1249
|
return { useGithubNotes: false };
|
|
1244
1250
|
}
|
|
1245
|
-
if (
|
|
1251
|
+
if (bodySource === "generated") {
|
|
1246
1252
|
return { useGithubNotes: true };
|
|
1247
1253
|
}
|
|
1248
|
-
if (
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1254
|
+
if (bodySource === "releaseNotes") {
|
|
1255
|
+
if (!releaseNotesEnabled) {
|
|
1256
|
+
warn("releaseNotes is not enabled in notes config but body is set to releaseNotes");
|
|
1257
|
+
return { useGithubNotes: true };
|
|
1258
|
+
}
|
|
1259
|
+
if (pipelineNotes) {
|
|
1260
|
+
const body = findNotesForTag(tag, pipelineNotes);
|
|
1261
|
+
if (body) return { body, useGithubNotes: false };
|
|
1262
|
+
}
|
|
1263
|
+
warn("No release notes found in pipeline output, falling back to GitHub auto-notes");
|
|
1264
|
+
return { useGithubNotes: true };
|
|
1265
|
+
}
|
|
1266
|
+
if (bodySource === "changelog") {
|
|
1267
|
+
const packageBody2 = formatChangelogForTag(tag, changelogs);
|
|
1268
|
+
if (packageBody2) {
|
|
1269
|
+
return { body: packageBody2, useGithubNotes: false };
|
|
1270
|
+
}
|
|
1271
|
+
warn("No changelog found for tag, falling back to GitHub auto-notes");
|
|
1252
1272
|
return { useGithubNotes: true };
|
|
1253
1273
|
}
|
|
1254
|
-
if (pipelineNotes) {
|
|
1274
|
+
if (releaseNotesEnabled && pipelineNotes) {
|
|
1255
1275
|
const body = findNotesForTag(tag, pipelineNotes);
|
|
1256
1276
|
if (body) return { body, useGithubNotes: false };
|
|
1257
1277
|
}
|
|
@@ -1264,6 +1284,15 @@ function resolveNotes(notesSetting, tag, changelogs, pipelineNotes) {
|
|
|
1264
1284
|
function isVersionOnlyTag(tag) {
|
|
1265
1285
|
return /^v?\d+\.\d+\.\d+/.test(tag);
|
|
1266
1286
|
}
|
|
1287
|
+
function getTitleFromTag(tag) {
|
|
1288
|
+
const atIndex = tag.lastIndexOf("@");
|
|
1289
|
+
if (atIndex === -1) {
|
|
1290
|
+
return tag;
|
|
1291
|
+
}
|
|
1292
|
+
const packageName = tag.slice(0, atIndex);
|
|
1293
|
+
const version = tag.slice(atIndex + 1);
|
|
1294
|
+
return `${packageName} @ ${version}`;
|
|
1295
|
+
}
|
|
1267
1296
|
function findNotesForTag(tag, notes) {
|
|
1268
1297
|
for (const [packageName, body] of Object.entries(notes)) {
|
|
1269
1298
|
if (tag.startsWith(`${packageName}@`) && body.trim()) {
|
|
@@ -1274,14 +1303,6 @@ function findNotesForTag(tag, notes) {
|
|
|
1274
1303
|
if (entries.length === 1 && isVersionOnlyTag(tag)) return entries[0];
|
|
1275
1304
|
return void 0;
|
|
1276
1305
|
}
|
|
1277
|
-
function readFileIfExists(filePath) {
|
|
1278
|
-
try {
|
|
1279
|
-
const content = fs7.readFileSync(filePath, "utf-8").trim();
|
|
1280
|
-
return content || void 0;
|
|
1281
|
-
} catch {
|
|
1282
|
-
return void 0;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
1306
|
function formatChangelogForTag(tag, changelogs) {
|
|
1286
1307
|
if (changelogs.length === 0) return void 0;
|
|
1287
1308
|
const changelog = changelogs.find((c) => tag.startsWith(`${c.packageName}@`));
|
|
@@ -1322,16 +1343,19 @@ async function runGithubReleaseStage(ctx) {
|
|
|
1322
1343
|
success: false
|
|
1323
1344
|
};
|
|
1324
1345
|
const ghArgs = ["release", "create", tag];
|
|
1346
|
+
ghArgs.push("--title", getTitleFromTag(tag));
|
|
1325
1347
|
if (config.githubRelease.draft) {
|
|
1326
1348
|
ghArgs.push("--draft");
|
|
1327
1349
|
}
|
|
1328
1350
|
if (isPreRel) {
|
|
1329
1351
|
ghArgs.push("--prerelease");
|
|
1330
1352
|
}
|
|
1353
|
+
const releaseNotesEnabled = !!(ctx.releaseNotes && Object.keys(ctx.releaseNotes).length > 0);
|
|
1331
1354
|
const { body, useGithubNotes } = resolveNotes(
|
|
1332
|
-
config.githubRelease.
|
|
1355
|
+
config.githubRelease.body,
|
|
1333
1356
|
tag,
|
|
1334
1357
|
ctx.input.changelogs,
|
|
1358
|
+
releaseNotesEnabled,
|
|
1335
1359
|
ctx.releaseNotes
|
|
1336
1360
|
);
|
|
1337
1361
|
if (body) {
|
|
@@ -1360,22 +1384,22 @@ async function runGithubReleaseStage(ctx) {
|
|
|
1360
1384
|
}
|
|
1361
1385
|
|
|
1362
1386
|
// src/stages/npm-publish.ts
|
|
1363
|
-
import * as
|
|
1387
|
+
import * as fs8 from "fs";
|
|
1364
1388
|
import * as path8 from "path";
|
|
1365
1389
|
|
|
1366
1390
|
// src/utils/npm-env.ts
|
|
1367
|
-
import * as
|
|
1391
|
+
import * as fs7 from "fs";
|
|
1368
1392
|
import * as os2 from "os";
|
|
1369
1393
|
import * as path7 from "path";
|
|
1370
1394
|
function writeTempNpmrc(contents) {
|
|
1371
|
-
const dir =
|
|
1395
|
+
const dir = fs7.mkdtempSync(path7.join(os2.tmpdir(), "releasekit-npmrc-"));
|
|
1372
1396
|
const npmrcPath = path7.join(dir, ".npmrc");
|
|
1373
|
-
|
|
1397
|
+
fs7.writeFileSync(npmrcPath, contents, "utf-8");
|
|
1374
1398
|
return {
|
|
1375
1399
|
npmrcPath,
|
|
1376
1400
|
cleanup: () => {
|
|
1377
1401
|
try {
|
|
1378
|
-
|
|
1402
|
+
fs7.rmSync(dir, { recursive: true, force: true });
|
|
1379
1403
|
} catch {
|
|
1380
1404
|
}
|
|
1381
1405
|
}
|
|
@@ -1395,9 +1419,6 @@ function createNpmSubprocessIsolation(options) {
|
|
|
1395
1419
|
}
|
|
1396
1420
|
})();
|
|
1397
1421
|
const lines = [`registry=${registryUrl}`];
|
|
1398
|
-
if (authMethod === "oidc") {
|
|
1399
|
-
lines.push("always-auth=false");
|
|
1400
|
-
}
|
|
1401
1422
|
if (authMethod === "token" && token) {
|
|
1402
1423
|
lines.push(`//${registryHost}/:_authToken=${token}`);
|
|
1403
1424
|
}
|
|
@@ -1413,10 +1434,7 @@ function createNpmSubprocessIsolation(options) {
|
|
|
1413
1434
|
npm_config_userconfig: npmrcPath,
|
|
1414
1435
|
// Auth-specific hardening
|
|
1415
1436
|
...isOidc ? {
|
|
1416
|
-
// Prevent
|
|
1417
|
-
NPM_CONFIG_ALWAYS_AUTH: "false",
|
|
1418
|
-
npm_config_always_auth: "false",
|
|
1419
|
-
// Explicitly prevent token-based publishing from being selected implicitly
|
|
1437
|
+
// Prevent any ambient token from overriding OIDC trusted publishing
|
|
1420
1438
|
NODE_AUTH_TOKEN: void 0,
|
|
1421
1439
|
NPM_TOKEN: void 0
|
|
1422
1440
|
} : {
|
|
@@ -1456,7 +1474,7 @@ async function runNpmPublishStage(ctx) {
|
|
|
1456
1474
|
};
|
|
1457
1475
|
const pkgJsonPath = path8.resolve(cwd, update.filePath);
|
|
1458
1476
|
try {
|
|
1459
|
-
const pkgContent =
|
|
1477
|
+
const pkgContent = fs8.readFileSync(pkgJsonPath, "utf-8");
|
|
1460
1478
|
const pkgJson = JSON.parse(pkgContent);
|
|
1461
1479
|
if (pkgJson.private) {
|
|
1462
1480
|
result.skipped = true;
|
|
@@ -1514,11 +1532,15 @@ async function runNpmPublishStage(ctx) {
|
|
|
1514
1532
|
if (!dryRun) {
|
|
1515
1533
|
success(`Published ${update.packageName}@${update.newVersion} to npm`);
|
|
1516
1534
|
}
|
|
1535
|
+
ctx.output.npm.push(result);
|
|
1517
1536
|
} catch (error) {
|
|
1518
1537
|
result.reason = error instanceof Error ? error.message : String(error);
|
|
1519
|
-
|
|
1538
|
+
ctx.output.npm.push(result);
|
|
1539
|
+
throw createPublishError(
|
|
1540
|
+
"NPM_PUBLISH_ERROR" /* NPM_PUBLISH_ERROR */,
|
|
1541
|
+
`${update.packageName}@${update.newVersion}: ${result.reason}`
|
|
1542
|
+
);
|
|
1520
1543
|
}
|
|
1521
|
-
ctx.output.npm.push(result);
|
|
1522
1544
|
}
|
|
1523
1545
|
} finally {
|
|
1524
1546
|
npmIsolation.cleanup();
|
|
@@ -1526,7 +1548,7 @@ async function runNpmPublishStage(ctx) {
|
|
|
1526
1548
|
}
|
|
1527
1549
|
|
|
1528
1550
|
// src/stages/prepare.ts
|
|
1529
|
-
import * as
|
|
1551
|
+
import * as fs9 from "fs";
|
|
1530
1552
|
import * as path9 from "path";
|
|
1531
1553
|
async function runPrepareStage(ctx) {
|
|
1532
1554
|
const { input, config, cliOptions, cwd } = ctx;
|
|
@@ -1536,7 +1558,7 @@ async function runPrepareStage(ctx) {
|
|
|
1536
1558
|
for (const file of config.npm.copyFiles) {
|
|
1537
1559
|
const src = path9.resolve(cwd, file);
|
|
1538
1560
|
const dest = path9.join(pkgDir, file);
|
|
1539
|
-
if (!
|
|
1561
|
+
if (!fs9.existsSync(src)) {
|
|
1540
1562
|
debug(`Source file not found, skipping copy: ${src}`);
|
|
1541
1563
|
continue;
|
|
1542
1564
|
}
|
|
@@ -1549,7 +1571,7 @@ async function runPrepareStage(ctx) {
|
|
|
1549
1571
|
continue;
|
|
1550
1572
|
}
|
|
1551
1573
|
try {
|
|
1552
|
-
|
|
1574
|
+
fs9.copyFileSync(src, dest);
|
|
1553
1575
|
debug(`Copied ${file} \u2192 ${pkgDir}`);
|
|
1554
1576
|
} catch (error) {
|
|
1555
1577
|
throw createPublishError(
|
|
@@ -1564,7 +1586,7 @@ async function runPrepareStage(ctx) {
|
|
|
1564
1586
|
for (const update of input.updates) {
|
|
1565
1587
|
const pkgDir = path9.dirname(path9.resolve(cwd, update.filePath));
|
|
1566
1588
|
const cargoPath = path9.join(pkgDir, "Cargo.toml");
|
|
1567
|
-
if (!
|
|
1589
|
+
if (!fs9.existsSync(cargoPath)) {
|
|
1568
1590
|
continue;
|
|
1569
1591
|
}
|
|
1570
1592
|
if (cliOptions.dryRun) {
|
|
@@ -1713,7 +1735,8 @@ async function runPipeline(input, config, options) {
|
|
|
1713
1735
|
npm: [],
|
|
1714
1736
|
cargo: [],
|
|
1715
1737
|
verification: [],
|
|
1716
|
-
githubReleases: []
|
|
1738
|
+
githubReleases: [],
|
|
1739
|
+
publishSucceeded: false
|
|
1717
1740
|
}
|
|
1718
1741
|
};
|
|
1719
1742
|
try {
|
|
@@ -1731,14 +1754,15 @@ async function runPipeline(input, config, options) {
|
|
|
1731
1754
|
if (options.registry === "all" || options.registry === "cargo") {
|
|
1732
1755
|
await runCargoPublishStage(ctx);
|
|
1733
1756
|
}
|
|
1757
|
+
ctx.output.publishSucceeded = ctx.output.npm.every((r) => r.success) && ctx.output.cargo.every((r) => r.success);
|
|
1734
1758
|
}
|
|
1735
1759
|
if (!options.skipVerification && !options.skipPublish) {
|
|
1736
1760
|
await runVerifyStage(ctx);
|
|
1737
1761
|
}
|
|
1738
|
-
if (!options.skipGit) {
|
|
1762
|
+
if (!options.skipGit && (options.skipPublish || ctx.output.publishSucceeded)) {
|
|
1739
1763
|
await runGitPushStage(ctx);
|
|
1740
1764
|
}
|
|
1741
|
-
if (!options.skipGithubRelease) {
|
|
1765
|
+
if (!options.skipGithubRelease && ctx.output.git.pushed) {
|
|
1742
1766
|
await runGithubReleaseStage(ctx);
|
|
1743
1767
|
}
|
|
1744
1768
|
} catch (error) {
|
|
@@ -1750,7 +1774,7 @@ async function runPipeline(input, config, options) {
|
|
|
1750
1774
|
}
|
|
1751
1775
|
|
|
1752
1776
|
// src/stages/input.ts
|
|
1753
|
-
import * as
|
|
1777
|
+
import * as fs10 from "fs";
|
|
1754
1778
|
import { z as z3 } from "zod";
|
|
1755
1779
|
var VersionChangelogEntrySchema = z3.object({
|
|
1756
1780
|
type: z3.string(),
|
|
@@ -1783,7 +1807,7 @@ async function parseInput(inputPath) {
|
|
|
1783
1807
|
let raw;
|
|
1784
1808
|
if (inputPath) {
|
|
1785
1809
|
try {
|
|
1786
|
-
raw =
|
|
1810
|
+
raw = fs10.readFileSync(inputPath, "utf-8");
|
|
1787
1811
|
} catch {
|
|
1788
1812
|
throw createPublishError("INPUT_PARSE_ERROR" /* INPUT_PARSE_ERROR */, `Could not read file: ${inputPath}`);
|
|
1789
1813
|
}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# GitHub Releases
|
|
2
|
+
|
|
3
|
+
`@releasekit/publish` creates a GitHub Release for each published package. This guide covers configuration options and how to use LLM-generated prose as the release body.
|
|
4
|
+
|
|
5
|
+
## Enabling GitHub Releases
|
|
6
|
+
|
|
7
|
+
GitHub Releases are enabled by default. Requires `GITHUB_TOKEN` with `contents: write` permission.
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"publish": {
|
|
12
|
+
"githubRelease": {
|
|
13
|
+
"enabled": true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
To disable:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"publish": {
|
|
24
|
+
"githubRelease": { "enabled": false }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Release Body (`body`)
|
|
32
|
+
|
|
33
|
+
Controls what appears in the GitHub Release description.
|
|
34
|
+
|
|
35
|
+
| Value | Behaviour |
|
|
36
|
+
|-------|-----------|
|
|
37
|
+
| `"auto"` (default) | Use LLM release notes if available, otherwise changelog entries, otherwise GitHub auto-generated notes |
|
|
38
|
+
| `"releaseNotes"` | Use LLM-generated prose release notes (requires `notes.releaseNotes.llm.tasks.releaseNotes: true`) |
|
|
39
|
+
| `"changelog"` | Use the formatted changelog entries for this version |
|
|
40
|
+
| `"generated"` | GitHub's auto-generated release notes (from merged PRs and commits) |
|
|
41
|
+
| `"none"` | No release body |
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"publish": {
|
|
46
|
+
"githubRelease": {
|
|
47
|
+
"body": "releaseNotes"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Using LLM-Generated Release Notes
|
|
56
|
+
|
|
57
|
+
To populate the GitHub Release body with LLM-written prose:
|
|
58
|
+
|
|
59
|
+
**1. Enable the `releaseNotes` LLM task in notes config:**
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"notes": {
|
|
64
|
+
"releaseNotes": {
|
|
65
|
+
"llm": {
|
|
66
|
+
"provider": "openai",
|
|
67
|
+
"model": "gpt-4o-mini",
|
|
68
|
+
"tasks": { "releaseNotes": true }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"publish": {
|
|
73
|
+
"githubRelease": {
|
|
74
|
+
"body": "releaseNotes"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The notes step runs first, the LLM generates prose release notes, and the publish step forwards them to the GitHub Release API.
|
|
81
|
+
|
|
82
|
+
If `body` is `"auto"` (default), LLM release notes are used automatically when available — no extra config needed.
|
|
83
|
+
|
|
84
|
+
**2. Optionally write release notes to a file** as well:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"notes": {
|
|
89
|
+
"releaseNotes": {
|
|
90
|
+
"mode": "root",
|
|
91
|
+
"llm": {
|
|
92
|
+
"provider": "anthropic",
|
|
93
|
+
"model": "claude-haiku-4-5",
|
|
94
|
+
"tasks": { "releaseNotes": true }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
When `mode` is set, a `RELEASE_NOTES.md` file is written in addition to the GitHub Release being populated.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Draft Releases
|
|
106
|
+
|
|
107
|
+
Releases are created as drafts by default, giving you a chance to review before publishing.
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"publish": {
|
|
112
|
+
"githubRelease": {
|
|
113
|
+
"draft": true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Set `"draft": false` to publish releases immediately.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Prerelease Marking
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"publish": {
|
|
128
|
+
"githubRelease": {
|
|
129
|
+
"prerelease": "auto"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
| Value | Behaviour |
|
|
136
|
+
|-------|-----------|
|
|
137
|
+
| `"auto"` (default) | Marked as prerelease when the version contains a prerelease identifier (e.g. `1.0.0-beta.1`) |
|
|
138
|
+
| `true` | Always marked as prerelease |
|
|
139
|
+
| `false` | Never marked as prerelease |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Per-Package Releases
|
|
144
|
+
|
|
145
|
+
In a monorepo, a separate GitHub Release is created for each published package by default.
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"publish": {
|
|
150
|
+
"githubRelease": {
|
|
151
|
+
"perPackage": true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Set `"perPackage": false` to create a single release for the entire repo.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Full Configuration Reference
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"publish": {
|
|
166
|
+
"githubRelease": {
|
|
167
|
+
"enabled": true,
|
|
168
|
+
"draft": true,
|
|
169
|
+
"prerelease": "auto",
|
|
170
|
+
"perPackage": true,
|
|
171
|
+
"body": "auto"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
See the [@releasekit/publish README](../README.md) for all options.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@releasekit/publish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Publish packages to npm and crates.io with git tagging and GitHub releases",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"files": [
|
|
43
43
|
"dist",
|
|
44
|
+
"docs",
|
|
44
45
|
"README.md",
|
|
45
46
|
"LICENSE"
|
|
46
47
|
],
|
|
@@ -62,8 +63,8 @@
|
|
|
62
63
|
"tsup": "^8.5.1",
|
|
63
64
|
"typescript": "^5.9.3",
|
|
64
65
|
"vitest": "^4.1.0",
|
|
65
|
-
"@releasekit/
|
|
66
|
-
"@releasekit/
|
|
66
|
+
"@releasekit/core": "0.0.0",
|
|
67
|
+
"@releasekit/config": "0.0.0"
|
|
67
68
|
},
|
|
68
69
|
"engines": {
|
|
69
70
|
"node": ">=20"
|