pubz 0.3.0 → 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 +7 -2
- package/dist/cli.js +174 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# `pubz`
|
|
2
2
|
|
|
3
|
+
<img width="1024" height="1024" alt="image" src="https://github.com/user-attachments/assets/11ffa33c-e895-4a7d-b2c3-dfadde8dd124" />
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
3
7
|
```bash
|
|
4
8
|
bunx pubz
|
|
5
9
|
```
|
|
@@ -14,8 +18,9 @@ bunx pubz
|
|
|
14
18
|
6. Commits version changes
|
|
15
19
|
7. Prompts you for where you want to publish (e.g. `npm` or private registry)
|
|
16
20
|
8. Builds packages
|
|
17
|
-
9.
|
|
18
|
-
10.
|
|
21
|
+
9. Transforms `workspace:` definitions to hard version numbers (so `npm` can be used for publishing with OIDC support).
|
|
22
|
+
10. Publishes to npm
|
|
23
|
+
11. Prompts you to create a `git tag` and push it
|
|
19
24
|
|
|
20
25
|
## Options
|
|
21
26
|
|
package/dist/cli.js
CHANGED
|
@@ -195,6 +195,9 @@ function prompt(question) {
|
|
|
195
195
|
function closePrompt() {
|
|
196
196
|
rl.close();
|
|
197
197
|
}
|
|
198
|
+
function pausePrompt() {
|
|
199
|
+
rl.close();
|
|
200
|
+
}
|
|
198
201
|
function resetPrompt() {
|
|
199
202
|
rl.close();
|
|
200
203
|
rl = readline.createInterface({
|
|
@@ -317,10 +320,25 @@ async function multiSelect(message, options, allSelectedByDefault = true) {
|
|
|
317
320
|
|
|
318
321
|
// src/auth.ts
|
|
319
322
|
import { spawn } from "node:child_process";
|
|
323
|
+
import { homedir } from "node:os";
|
|
324
|
+
|
|
325
|
+
// src/log.ts
|
|
326
|
+
var verboseEnabled = false;
|
|
327
|
+
function setVerbose(enabled) {
|
|
328
|
+
verboseEnabled = enabled;
|
|
329
|
+
}
|
|
330
|
+
function debug(...args) {
|
|
331
|
+
if (verboseEnabled) {
|
|
332
|
+
console.error("[debug]", ...args);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/auth.ts
|
|
320
337
|
async function checkNpmAuth(registry) {
|
|
321
338
|
return new Promise((resolve2) => {
|
|
322
339
|
const proc = spawn("npm", ["whoami", "--registry", registry], {
|
|
323
|
-
stdio: ["
|
|
340
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
341
|
+
cwd: homedir()
|
|
324
342
|
});
|
|
325
343
|
let stdout = "";
|
|
326
344
|
let stderr = "";
|
|
@@ -334,6 +352,7 @@ async function checkNpmAuth(registry) {
|
|
|
334
352
|
if (code === 0 && stdout.trim()) {
|
|
335
353
|
resolve2({ authenticated: true, username: stdout.trim() });
|
|
336
354
|
} else {
|
|
355
|
+
debug(`npm whoami failed: code=${code}, stdout=${JSON.stringify(stdout)}, stderr=${JSON.stringify(stderr)}`);
|
|
337
356
|
resolve2({ authenticated: false });
|
|
338
357
|
}
|
|
339
358
|
});
|
|
@@ -341,10 +360,13 @@ async function checkNpmAuth(registry) {
|
|
|
341
360
|
}
|
|
342
361
|
async function npmLogin(registry) {
|
|
343
362
|
return new Promise((resolve2) => {
|
|
363
|
+
debug(`spawning: npm login --registry ${registry}`);
|
|
344
364
|
const proc = spawn("npm", ["login", "--registry", registry], {
|
|
345
|
-
stdio: "inherit"
|
|
365
|
+
stdio: "inherit",
|
|
366
|
+
cwd: homedir()
|
|
346
367
|
});
|
|
347
368
|
proc.on("close", (code) => {
|
|
369
|
+
debug(`npm login exited: code=${code}`);
|
|
348
370
|
if (code === 0) {
|
|
349
371
|
resolve2({ success: true });
|
|
350
372
|
} else {
|
|
@@ -357,6 +379,7 @@ async function npmLogin(registry) {
|
|
|
357
379
|
// src/publish.ts
|
|
358
380
|
import { spawn as spawn2 } from "node:child_process";
|
|
359
381
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
382
|
+
import { homedir as homedir2 } from "node:os";
|
|
360
383
|
import { join as join3 } from "node:path";
|
|
361
384
|
function run(command, args, cwd) {
|
|
362
385
|
return new Promise((resolve2) => {
|
|
@@ -455,7 +478,9 @@ async function publishPackage(pkg, registry, context, dryRun) {
|
|
|
455
478
|
let result;
|
|
456
479
|
if (context.useBrowserAuth) {
|
|
457
480
|
args.push("--auth-type", "web");
|
|
458
|
-
|
|
481
|
+
args.push(pkg.path);
|
|
482
|
+
context.onInteractiveStart?.();
|
|
483
|
+
const interactiveResult = await runInteractive(NPM_COMMAND, args, homedir2());
|
|
459
484
|
result = { code: interactiveResult.code, output: "" };
|
|
460
485
|
context.onInteractiveComplete?.();
|
|
461
486
|
} else {
|
|
@@ -535,6 +560,114 @@ async function pushGitTag(version, cwd, dryRun) {
|
|
|
535
560
|
return { success: true };
|
|
536
561
|
}
|
|
537
562
|
|
|
563
|
+
// src/changelog.ts
|
|
564
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
565
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
566
|
+
const sshMatch = remoteUrl.match(/^git@([^:]+):(.+?)(?:\.git)?$/);
|
|
567
|
+
if (sshMatch) {
|
|
568
|
+
return `https://${sshMatch[1]}/${sshMatch[2]}`;
|
|
569
|
+
}
|
|
570
|
+
const httpsMatch = remoteUrl.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?$/);
|
|
571
|
+
if (httpsMatch) {
|
|
572
|
+
return `https://${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
573
|
+
}
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
function runSilent(command, args, cwd) {
|
|
577
|
+
return new Promise((resolve2) => {
|
|
578
|
+
const proc = spawn3(command, args, {
|
|
579
|
+
cwd,
|
|
580
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
581
|
+
});
|
|
582
|
+
let output = "";
|
|
583
|
+
proc.stdout?.on("data", (data) => {
|
|
584
|
+
output += data.toString();
|
|
585
|
+
});
|
|
586
|
+
proc.stderr?.on("data", (data) => {
|
|
587
|
+
output += data.toString();
|
|
588
|
+
});
|
|
589
|
+
proc.on("close", (code) => {
|
|
590
|
+
resolve2({ code: code ?? 1, output });
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
async function getPreviousTag(cwd) {
|
|
595
|
+
const result = await runSilent("git", ["tag", "--sort=-version:refname"], cwd);
|
|
596
|
+
if (result.code !== 0)
|
|
597
|
+
return null;
|
|
598
|
+
const tags = result.output.trim().split(`
|
|
599
|
+
`).filter((t) => t.length > 0);
|
|
600
|
+
return tags[0] ?? null;
|
|
601
|
+
}
|
|
602
|
+
async function getRepoUrl(cwd) {
|
|
603
|
+
const result = await runSilent("git", ["remote", "get-url", "origin"], cwd);
|
|
604
|
+
if (result.code !== 0)
|
|
605
|
+
return null;
|
|
606
|
+
return parseGitRemoteUrl(result.output.trim());
|
|
607
|
+
}
|
|
608
|
+
async function getCommitsSince(ref, cwd) {
|
|
609
|
+
const result = await runSilent("git", ["log", `${ref}..HEAD`, "--oneline", "--no-decorate"], cwd);
|
|
610
|
+
if (result.code !== 0)
|
|
611
|
+
return [];
|
|
612
|
+
return result.output.trim().split(`
|
|
613
|
+
`).filter((line) => line.length > 0).map((line) => {
|
|
614
|
+
const spaceIdx = line.indexOf(" ");
|
|
615
|
+
return {
|
|
616
|
+
sha: line.slice(0, spaceIdx),
|
|
617
|
+
message: line.slice(spaceIdx + 1)
|
|
618
|
+
};
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
function isReleaseCommit(message) {
|
|
622
|
+
return /^chore: release v/.test(message);
|
|
623
|
+
}
|
|
624
|
+
function formatChangelogTerminal(commits) {
|
|
625
|
+
const filtered = commits.filter((c) => !isReleaseCommit(c.message));
|
|
626
|
+
if (filtered.length === 0)
|
|
627
|
+
return "";
|
|
628
|
+
return filtered.map((c) => ` ${dim(c.sha)} ${c.message}`).join(`
|
|
629
|
+
`);
|
|
630
|
+
}
|
|
631
|
+
function formatChangelogMarkdown(commits, repoUrl) {
|
|
632
|
+
const filtered = commits.filter((c) => !isReleaseCommit(c.message));
|
|
633
|
+
if (filtered.length === 0)
|
|
634
|
+
return "";
|
|
635
|
+
return filtered.map((c) => {
|
|
636
|
+
const shaRef = repoUrl ? `[\`${c.sha}\`](${repoUrl}/commit/${c.sha})` : `\`${c.sha}\``;
|
|
637
|
+
return `- ${shaRef} ${c.message}`;
|
|
638
|
+
}).join(`
|
|
639
|
+
`);
|
|
640
|
+
}
|
|
641
|
+
async function generateChangelog(cwd) {
|
|
642
|
+
const [previousTag, repoUrl] = await Promise.all([
|
|
643
|
+
getPreviousTag(cwd),
|
|
644
|
+
getRepoUrl(cwd)
|
|
645
|
+
]);
|
|
646
|
+
if (!previousTag) {
|
|
647
|
+
return { commits: [], terminal: "", markdown: "", previousTag: null, repoUrl };
|
|
648
|
+
}
|
|
649
|
+
const commits = await getCommitsSince(previousTag, cwd);
|
|
650
|
+
const terminal = formatChangelogTerminal(commits);
|
|
651
|
+
const markdown = formatChangelogMarkdown(commits, repoUrl);
|
|
652
|
+
return { commits, terminal, markdown, previousTag, repoUrl };
|
|
653
|
+
}
|
|
654
|
+
async function createGitHubRelease(version, body, cwd, dryRun) {
|
|
655
|
+
const tagName = `v${version}`;
|
|
656
|
+
if (dryRun) {
|
|
657
|
+
console.log(`[DRY RUN] Would create GitHub release for ${tagName}`);
|
|
658
|
+
return { success: true };
|
|
659
|
+
}
|
|
660
|
+
const result = await runSilent("gh", ["release", "create", tagName, "--title", tagName, "--notes", body], cwd);
|
|
661
|
+
if (result.code !== 0) {
|
|
662
|
+
return {
|
|
663
|
+
success: false,
|
|
664
|
+
error: result.output.trim() || `Failed to create GitHub release for ${tagName}`
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const url = result.output.trim();
|
|
668
|
+
return { success: true, url };
|
|
669
|
+
}
|
|
670
|
+
|
|
538
671
|
// src/version.ts
|
|
539
672
|
import { readFile as readFile3, writeFile } from "node:fs/promises";
|
|
540
673
|
async function transformWorkspaceProtocolForPublish(packages, newVersion, dryRun) {
|
|
@@ -702,6 +835,7 @@ Options:
|
|
|
702
835
|
--yes, -y Skip yes/no confirmation prompts (still asks for choices)
|
|
703
836
|
--ci CI mode: skip all prompts, auto-accept everything
|
|
704
837
|
--version <value> Version bump type (patch|minor|major) or explicit version (required with --ci)
|
|
838
|
+
--verbose Show debug logging
|
|
705
839
|
-h, --help Show this help message
|
|
706
840
|
|
|
707
841
|
Examples:
|
|
@@ -721,6 +855,7 @@ function parseArgs(args) {
|
|
|
721
855
|
skipConfirms: false,
|
|
722
856
|
ci: false,
|
|
723
857
|
version: "",
|
|
858
|
+
verbose: false,
|
|
724
859
|
help: false
|
|
725
860
|
};
|
|
726
861
|
for (let i = 0;i < args.length; i++) {
|
|
@@ -748,6 +883,9 @@ function parseArgs(args) {
|
|
|
748
883
|
case "--version":
|
|
749
884
|
options.version = args[++i] || "";
|
|
750
885
|
break;
|
|
886
|
+
case "--verbose":
|
|
887
|
+
options.verbose = true;
|
|
888
|
+
break;
|
|
751
889
|
case "-h":
|
|
752
890
|
case "--help":
|
|
753
891
|
options.help = true;
|
|
@@ -762,6 +900,7 @@ async function main() {
|
|
|
762
900
|
process.exit(0);
|
|
763
901
|
}
|
|
764
902
|
const options = parseArgs(process.argv.slice(2));
|
|
903
|
+
setVerbose(options.verbose);
|
|
765
904
|
if (options.help) {
|
|
766
905
|
printUsage();
|
|
767
906
|
process.exit(0);
|
|
@@ -930,7 +1069,9 @@ async function main() {
|
|
|
930
1069
|
console.log("");
|
|
931
1070
|
console.log(yellow("Not logged in to npm.") + " Starting login...");
|
|
932
1071
|
console.log("");
|
|
1072
|
+
pausePrompt();
|
|
933
1073
|
const loginResult = await npmLogin(registry);
|
|
1074
|
+
resetPrompt();
|
|
934
1075
|
if (!loginResult.success) {
|
|
935
1076
|
console.error(red(bold("Login failed:")) + ` ${loginResult.error}`);
|
|
936
1077
|
closePrompt();
|
|
@@ -1014,6 +1155,7 @@ async function main() {
|
|
|
1014
1155
|
const publishContext = {
|
|
1015
1156
|
otp: options.otp,
|
|
1016
1157
|
useBrowserAuth: !options.ci,
|
|
1158
|
+
onInteractiveStart: pausePrompt,
|
|
1017
1159
|
onInteractiveComplete: resetPrompt
|
|
1018
1160
|
};
|
|
1019
1161
|
let publishFailed = false;
|
|
@@ -1052,27 +1194,52 @@ async function main() {
|
|
|
1052
1194
|
console.log("");
|
|
1053
1195
|
console.log(`Published version: ${green(bold(newVersion))}`);
|
|
1054
1196
|
console.log("");
|
|
1197
|
+
const changelog = await generateChangelog(cwd);
|
|
1198
|
+
if (changelog.terminal) {
|
|
1199
|
+
console.log(bold("Changes since ") + cyan(changelog.previousTag ?? "initial") + bold(":"));
|
|
1200
|
+
console.log(changelog.terminal);
|
|
1201
|
+
console.log("");
|
|
1202
|
+
}
|
|
1055
1203
|
if (!options.dryRun) {
|
|
1056
1204
|
if (options.ci) {
|
|
1057
|
-
console.log("");
|
|
1058
1205
|
console.log(cyan("Creating git tag..."));
|
|
1059
1206
|
const tagResult = await createGitTag(newVersion, cwd, options.dryRun);
|
|
1060
1207
|
if (tagResult.success) {
|
|
1061
1208
|
console.log(cyan("Pushing tag to origin..."));
|
|
1062
1209
|
await pushGitTag(newVersion, cwd, options.dryRun);
|
|
1210
|
+
if (changelog.markdown) {
|
|
1211
|
+
console.log(cyan("Creating GitHub release..."));
|
|
1212
|
+
const releaseResult = await createGitHubRelease(newVersion, changelog.markdown, cwd, options.dryRun);
|
|
1213
|
+
if (releaseResult.success && releaseResult.url) {
|
|
1214
|
+
console.log(` Release created: ${cyan(releaseResult.url)}`);
|
|
1215
|
+
} else if (!releaseResult.success) {
|
|
1216
|
+
console.error(yellow(releaseResult.error ?? "Failed to create GitHub release"));
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1063
1219
|
} else {
|
|
1064
1220
|
console.error(red(tagResult.error ?? "Failed to create git tag"));
|
|
1065
1221
|
}
|
|
1066
1222
|
console.log("");
|
|
1067
|
-
} else
|
|
1068
|
-
const shouldTag = await confirm(`Create a git tag for ${cyan(`v${newVersion}`)}?`);
|
|
1223
|
+
} else {
|
|
1224
|
+
const shouldTag = skipConfirms || await confirm(`Create a git tag for ${cyan(`v${newVersion}`)}?`);
|
|
1069
1225
|
if (shouldTag) {
|
|
1070
1226
|
console.log("");
|
|
1071
1227
|
const tagResult = await createGitTag(newVersion, cwd, options.dryRun);
|
|
1072
1228
|
if (tagResult.success) {
|
|
1073
|
-
const shouldPush = await confirm("Push tag to origin?");
|
|
1229
|
+
const shouldPush = skipConfirms || await confirm("Push tag to origin?");
|
|
1074
1230
|
if (shouldPush) {
|
|
1075
1231
|
await pushGitTag(newVersion, cwd, options.dryRun);
|
|
1232
|
+
if (changelog.markdown) {
|
|
1233
|
+
const shouldRelease = skipConfirms || await confirm("Create a GitHub release?");
|
|
1234
|
+
if (shouldRelease) {
|
|
1235
|
+
const releaseResult = await createGitHubRelease(newVersion, changelog.markdown, cwd, options.dryRun);
|
|
1236
|
+
if (releaseResult.success && releaseResult.url) {
|
|
1237
|
+
console.log(` Release created: ${cyan(releaseResult.url)}`);
|
|
1238
|
+
} else if (!releaseResult.success) {
|
|
1239
|
+
console.error(yellow(releaseResult.error ?? "Failed to create GitHub release"));
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1076
1243
|
} else {
|
|
1077
1244
|
console.log(`Tag created locally. Push manually with: ${dim(`git push origin v${newVersion}`)}`);
|
|
1078
1245
|
}
|