@releasekit/publish 0.2.0-next.9 → 0.3.0-next.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/LICENSE +21 -0
- package/README.md +13 -1
- package/dist/{chunk-Y7Y6GQ6R.js → chunk-GOBII36Q.js} +297 -112
- package/dist/cli.cjs +317 -134
- package/dist/cli.js +1 -1
- package/dist/index.cjs +311 -128
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -1
- package/package.json +38 -28
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sam Maister
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -82,13 +82,21 @@ releasekit-publish --input version-output.json
|
|
|
82
82
|
### In CI (GitHub Actions)
|
|
83
83
|
|
|
84
84
|
```yaml
|
|
85
|
+
- name: Configure permissions (OIDC + git pushes)
|
|
86
|
+
# at job level:
|
|
87
|
+
# permissions:
|
|
88
|
+
# id-token: write
|
|
89
|
+
# contents: write
|
|
90
|
+
|
|
85
91
|
- name: Version
|
|
86
92
|
run: releasekit-version --json > version-output.json
|
|
87
93
|
|
|
88
94
|
- name: Publish
|
|
89
95
|
run: releasekit-publish --input version-output.json
|
|
96
|
+
# For OIDC trusted publishing: no npm token needed (recommended).
|
|
97
|
+
# For token-based publishing: set NPM_TOKEN (or NODE_AUTH_TOKEN).
|
|
90
98
|
env:
|
|
91
|
-
|
|
99
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
92
100
|
```
|
|
93
101
|
|
|
94
102
|
## Configuration
|
|
@@ -105,6 +113,10 @@ Configure via `releasekit.config.json`:
|
|
|
105
113
|
"access": "public",
|
|
106
114
|
"copyFiles": ["LICENSE"]
|
|
107
115
|
},
|
|
116
|
+
"git": {
|
|
117
|
+
"pushMethod": "auto",
|
|
118
|
+
"httpsTokenEnv": "GITHUB_TOKEN"
|
|
119
|
+
},
|
|
108
120
|
"cargo": {
|
|
109
121
|
"enabled": false,
|
|
110
122
|
"noVerify": false
|
|
@@ -23,14 +23,16 @@ function getDefaultConfig() {
|
|
|
23
23
|
push: true,
|
|
24
24
|
pushMethod: "auto",
|
|
25
25
|
remote: "origin",
|
|
26
|
-
branch: "main"
|
|
26
|
+
branch: "main",
|
|
27
|
+
httpsTokenEnv: void 0,
|
|
28
|
+
skipHooks: false
|
|
27
29
|
},
|
|
28
30
|
githubRelease: {
|
|
29
31
|
enabled: true,
|
|
30
32
|
draft: true,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
perPackage: true,
|
|
34
|
+
prerelease: "auto",
|
|
35
|
+
releaseNotes: "auto"
|
|
34
36
|
},
|
|
35
37
|
verify: {
|
|
36
38
|
npm: {
|
|
@@ -71,15 +73,16 @@ function toPublishConfig(config) {
|
|
|
71
73
|
push: config.git.push ?? defaults.git.push,
|
|
72
74
|
pushMethod: config.git.pushMethod ?? defaults.git.pushMethod,
|
|
73
75
|
remote: config.git.remote ?? defaults.git.remote,
|
|
74
|
-
branch: config.git.branch ?? defaults.git.branch
|
|
76
|
+
branch: config.git.branch ?? defaults.git.branch,
|
|
77
|
+
httpsTokenEnv: config.git.httpsTokenEnv ?? defaults.git.httpsTokenEnv,
|
|
78
|
+
skipHooks: config.git.skipHooks ?? defaults.git.skipHooks
|
|
75
79
|
} : defaults.git,
|
|
76
80
|
githubRelease: {
|
|
77
81
|
enabled: config.githubRelease?.enabled ?? defaults.githubRelease.enabled,
|
|
78
82
|
draft: config.githubRelease?.draft ?? defaults.githubRelease.draft,
|
|
79
|
-
generateNotes: config.githubRelease?.generateNotes ?? defaults.githubRelease.generateNotes,
|
|
80
83
|
perPackage: config.githubRelease?.perPackage ?? defaults.githubRelease.perPackage,
|
|
81
84
|
prerelease: config.githubRelease?.prerelease ?? defaults.githubRelease.prerelease,
|
|
82
|
-
|
|
85
|
+
releaseNotes: config.githubRelease?.releaseNotes ?? defaults.githubRelease.releaseNotes
|
|
83
86
|
},
|
|
84
87
|
verify: {
|
|
85
88
|
npm: {
|
|
@@ -248,8 +251,20 @@ function createPublishError(code, details) {
|
|
|
248
251
|
// src/utils/exec.ts
|
|
249
252
|
import { execFile } from "child_process";
|
|
250
253
|
import { debug, info } from "@releasekit/core";
|
|
254
|
+
function redactArg(arg) {
|
|
255
|
+
try {
|
|
256
|
+
const url = new URL(arg);
|
|
257
|
+
if (url.username || url.password) {
|
|
258
|
+
url.username = url.username ? "***" : "";
|
|
259
|
+
url.password = url.password ? "***" : "";
|
|
260
|
+
return url.toString();
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
return arg;
|
|
265
|
+
}
|
|
251
266
|
async function execCommand(file, args, options = {}) {
|
|
252
|
-
const displayCommand = options.label ?? [file, ...args].join(" ");
|
|
267
|
+
const displayCommand = options.label ?? [file, ...args.map(redactArg)].join(" ");
|
|
253
268
|
if (options.dryRun) {
|
|
254
269
|
info(`[DRY RUN] Would execute: ${displayCommand}`);
|
|
255
270
|
return { stdout: "", stderr: "", exitCode: 0 };
|
|
@@ -305,7 +320,7 @@ function detectNpmAuth() {
|
|
|
305
320
|
if (process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
|
|
306
321
|
return "oidc";
|
|
307
322
|
}
|
|
308
|
-
if (process.env.NPM_TOKEN) {
|
|
323
|
+
if (process.env.NPM_TOKEN || process.env.NODE_AUTH_TOKEN) {
|
|
309
324
|
return "token";
|
|
310
325
|
}
|
|
311
326
|
return null;
|
|
@@ -560,13 +575,17 @@ function topologicalSort(crates) {
|
|
|
560
575
|
import * as path3 from "path";
|
|
561
576
|
import { info as info2, success as success2 } from "@releasekit/core";
|
|
562
577
|
async function runGitCommitStage(ctx) {
|
|
563
|
-
const { input, cliOptions, cwd } = ctx;
|
|
578
|
+
const { input, config, cliOptions, cwd } = ctx;
|
|
564
579
|
const dryRun = cliOptions.dryRun;
|
|
580
|
+
const skipHooks = config.git.skipHooks ?? false;
|
|
565
581
|
if (!input.commitMessage) {
|
|
566
582
|
info2("No commit message provided, skipping git commit");
|
|
567
583
|
return;
|
|
568
584
|
}
|
|
569
585
|
const filePaths = input.updates.map((u) => path3.resolve(cwd, u.filePath));
|
|
586
|
+
if (ctx.additionalFiles) {
|
|
587
|
+
filePaths.push(...ctx.additionalFiles.map((f) => path3.resolve(cwd, f)));
|
|
588
|
+
}
|
|
570
589
|
if (filePaths.length === 0) {
|
|
571
590
|
info2("No files to commit");
|
|
572
591
|
return;
|
|
@@ -583,8 +602,13 @@ async function runGitCommitStage(ctx) {
|
|
|
583
602
|
`git add failed: ${error instanceof Error ? error.message : String(error)}`
|
|
584
603
|
);
|
|
585
604
|
}
|
|
605
|
+
const commitArgs = ["commit"];
|
|
606
|
+
if (skipHooks) {
|
|
607
|
+
commitArgs.push("--no-verify");
|
|
608
|
+
}
|
|
609
|
+
commitArgs.push("-m", input.commitMessage);
|
|
586
610
|
try {
|
|
587
|
-
await execCommand("git",
|
|
611
|
+
await execCommand("git", commitArgs, {
|
|
588
612
|
cwd,
|
|
589
613
|
dryRun,
|
|
590
614
|
label: `git commit -m "${input.commitMessage}"`
|
|
@@ -622,6 +646,18 @@ async function runGitCommitStage(ctx) {
|
|
|
622
646
|
|
|
623
647
|
// src/stages/git-push.ts
|
|
624
648
|
import { info as info3, success as success3 } from "@releasekit/core";
|
|
649
|
+
function toGithubAuthedUrl(remoteUrl, token) {
|
|
650
|
+
try {
|
|
651
|
+
const url = new URL(remoteUrl);
|
|
652
|
+
if (url.protocol !== "https:") return void 0;
|
|
653
|
+
if (url.host !== "github.com") return void 0;
|
|
654
|
+
url.username = "x-access-token";
|
|
655
|
+
url.password = token;
|
|
656
|
+
return url.toString();
|
|
657
|
+
} catch {
|
|
658
|
+
return void 0;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
625
661
|
async function runGitPushStage(ctx) {
|
|
626
662
|
const { config, cliOptions, cwd, output } = ctx;
|
|
627
663
|
const dryRun = cliOptions.dryRun;
|
|
@@ -642,16 +678,26 @@ async function runGitPushStage(ctx) {
|
|
|
642
678
|
pushMethod = "https";
|
|
643
679
|
}
|
|
644
680
|
}
|
|
681
|
+
const httpsTokenEnv = config.git.httpsTokenEnv;
|
|
682
|
+
const httpsToken = httpsTokenEnv ? process.env[httpsTokenEnv] : void 0;
|
|
645
683
|
try {
|
|
684
|
+
let pushRemote = remote;
|
|
685
|
+
if (pushMethod === "https" && httpsToken) {
|
|
686
|
+
const remoteUrlResult = await execCommand("git", ["remote", "get-url", remote], { cwd, dryRun: false });
|
|
687
|
+
const authed = toGithubAuthedUrl(remoteUrlResult.stdout.trim(), httpsToken);
|
|
688
|
+
if (authed) {
|
|
689
|
+
pushRemote = authed;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
646
692
|
if (output.git.committed) {
|
|
647
|
-
await execCommand("git", ["push",
|
|
693
|
+
await execCommand("git", ["push", pushRemote, branch], {
|
|
648
694
|
cwd,
|
|
649
695
|
dryRun,
|
|
650
696
|
label: `git push ${remote} ${branch}`
|
|
651
697
|
});
|
|
652
698
|
}
|
|
653
699
|
if (output.git.tags.length > 0) {
|
|
654
|
-
await execCommand("git", ["push",
|
|
700
|
+
await execCommand("git", ["push", pushRemote, "--tags"], {
|
|
655
701
|
cwd,
|
|
656
702
|
dryRun,
|
|
657
703
|
label: `git push ${remote} --tags`
|
|
@@ -672,6 +718,62 @@ async function runGitPushStage(ctx) {
|
|
|
672
718
|
// src/stages/github-release.ts
|
|
673
719
|
import * as fs4 from "fs";
|
|
674
720
|
import { debug as debug3, info as info4, success as success4, warn as warn2 } from "@releasekit/core";
|
|
721
|
+
function resolveNotes(notesSetting, tag, changelogs, pipelineNotes) {
|
|
722
|
+
if (notesSetting === "none") {
|
|
723
|
+
return { useGithubNotes: false };
|
|
724
|
+
}
|
|
725
|
+
if (notesSetting === "github") {
|
|
726
|
+
return { useGithubNotes: true };
|
|
727
|
+
}
|
|
728
|
+
if (notesSetting !== "auto") {
|
|
729
|
+
const body = readFileIfExists(notesSetting);
|
|
730
|
+
if (body) return { body, useGithubNotes: false };
|
|
731
|
+
debug3(`Notes file not found: ${notesSetting}, falling back to GitHub auto-notes`);
|
|
732
|
+
return { useGithubNotes: true };
|
|
733
|
+
}
|
|
734
|
+
if (pipelineNotes) {
|
|
735
|
+
const body = findNotesForTag(tag, pipelineNotes);
|
|
736
|
+
if (body) return { body, useGithubNotes: false };
|
|
737
|
+
}
|
|
738
|
+
const packageBody = formatChangelogForTag(tag, changelogs);
|
|
739
|
+
if (packageBody) {
|
|
740
|
+
return { body: packageBody, useGithubNotes: false };
|
|
741
|
+
}
|
|
742
|
+
return { useGithubNotes: true };
|
|
743
|
+
}
|
|
744
|
+
function isVersionOnlyTag(tag) {
|
|
745
|
+
return /^v?\d+\.\d+\.\d+/.test(tag);
|
|
746
|
+
}
|
|
747
|
+
function findNotesForTag(tag, notes) {
|
|
748
|
+
for (const [packageName, body] of Object.entries(notes)) {
|
|
749
|
+
if (tag.startsWith(`${packageName}@`) && body.trim()) {
|
|
750
|
+
return body;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const entries = Object.values(notes).filter((b) => b.trim());
|
|
754
|
+
if (entries.length === 1 && isVersionOnlyTag(tag)) return entries[0];
|
|
755
|
+
return void 0;
|
|
756
|
+
}
|
|
757
|
+
function readFileIfExists(filePath) {
|
|
758
|
+
try {
|
|
759
|
+
const content = fs4.readFileSync(filePath, "utf-8").trim();
|
|
760
|
+
return content || void 0;
|
|
761
|
+
} catch {
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function formatChangelogForTag(tag, changelogs) {
|
|
766
|
+
if (changelogs.length === 0) return void 0;
|
|
767
|
+
const changelog = changelogs.find((c) => tag.startsWith(`${c.packageName}@`));
|
|
768
|
+
const target = changelog ?? (changelogs.length === 1 && isVersionOnlyTag(tag) ? changelogs[0] : void 0);
|
|
769
|
+
if (!target || target.entries.length === 0) return void 0;
|
|
770
|
+
const lines = [];
|
|
771
|
+
for (const entry of target.entries) {
|
|
772
|
+
const scope = entry.scope ? `**${entry.scope}:** ` : "";
|
|
773
|
+
lines.push(`- ${scope}${entry.description}`);
|
|
774
|
+
}
|
|
775
|
+
return lines.join("\n");
|
|
776
|
+
}
|
|
675
777
|
async function runGithubReleaseStage(ctx) {
|
|
676
778
|
const { config, cliOptions, output } = ctx;
|
|
677
779
|
const dryRun = cliOptions.dryRun;
|
|
@@ -684,19 +786,13 @@ async function runGithubReleaseStage(ctx) {
|
|
|
684
786
|
info4("No tags available for GitHub release");
|
|
685
787
|
return;
|
|
686
788
|
}
|
|
687
|
-
let notesBody;
|
|
688
|
-
if (config.githubRelease.notesFile) {
|
|
689
|
-
try {
|
|
690
|
-
notesBody = fs4.readFileSync(config.githubRelease.notesFile, "utf-8");
|
|
691
|
-
} catch {
|
|
692
|
-
debug3(`Could not read notes file: ${config.githubRelease.notesFile}`);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
789
|
const firstTag = tags[0];
|
|
696
790
|
if (!firstTag) return;
|
|
697
791
|
const tagsToRelease = config.githubRelease.perPackage ? tags : [firstTag];
|
|
698
792
|
for (const tag of tagsToRelease) {
|
|
699
|
-
const
|
|
793
|
+
const MAX_TAG_LENGTH = 1e3;
|
|
794
|
+
const truncatedTag = tag.length > MAX_TAG_LENGTH ? tag.slice(0, MAX_TAG_LENGTH) : tag;
|
|
795
|
+
const versionMatch = truncatedTag.match(/(\d{1,20}\.\d{1,20}\.\d{1,20}(?:[-+.]?[a-zA-Z0-9.-]{0,100})?)$/);
|
|
700
796
|
const version = versionMatch?.[1] ?? "";
|
|
701
797
|
const isPreRel = config.githubRelease.prerelease === "auto" ? version ? isPrerelease(version) : false : config.githubRelease.prerelease;
|
|
702
798
|
const result = {
|
|
@@ -712,9 +808,15 @@ async function runGithubReleaseStage(ctx) {
|
|
|
712
808
|
if (isPreRel) {
|
|
713
809
|
ghArgs.push("--prerelease");
|
|
714
810
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
811
|
+
const { body, useGithubNotes } = resolveNotes(
|
|
812
|
+
config.githubRelease.releaseNotes,
|
|
813
|
+
tag,
|
|
814
|
+
ctx.input.changelogs,
|
|
815
|
+
ctx.releaseNotes
|
|
816
|
+
);
|
|
817
|
+
if (body) {
|
|
818
|
+
ghArgs.push("--notes", body);
|
|
819
|
+
} else if (useGithubNotes) {
|
|
718
820
|
ghArgs.push("--generate-notes");
|
|
719
821
|
}
|
|
720
822
|
try {
|
|
@@ -738,9 +840,77 @@ async function runGithubReleaseStage(ctx) {
|
|
|
738
840
|
}
|
|
739
841
|
|
|
740
842
|
// src/stages/npm-publish.ts
|
|
843
|
+
import * as fs6 from "fs";
|
|
844
|
+
import * as path5 from "path";
|
|
845
|
+
import { debug as debug5, info as info5, success as success5, warn as warn3 } from "@releasekit/core";
|
|
846
|
+
|
|
847
|
+
// src/utils/npm-env.ts
|
|
741
848
|
import * as fs5 from "fs";
|
|
849
|
+
import * as os from "os";
|
|
742
850
|
import * as path4 from "path";
|
|
743
|
-
import { debug as debug4
|
|
851
|
+
import { debug as debug4 } from "@releasekit/core";
|
|
852
|
+
function writeTempNpmrc(contents) {
|
|
853
|
+
const dir = fs5.mkdtempSync(path4.join(os.tmpdir(), "releasekit-npmrc-"));
|
|
854
|
+
const npmrcPath = path4.join(dir, ".npmrc");
|
|
855
|
+
fs5.writeFileSync(npmrcPath, contents, "utf-8");
|
|
856
|
+
return {
|
|
857
|
+
npmrcPath,
|
|
858
|
+
cleanup: () => {
|
|
859
|
+
try {
|
|
860
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
function createNpmSubprocessIsolation(options) {
|
|
867
|
+
const { authMethod, registryUrl } = options;
|
|
868
|
+
const baseEnv = {};
|
|
869
|
+
if (!authMethod) return { env: baseEnv, cleanup: () => {
|
|
870
|
+
} };
|
|
871
|
+
const token = process.env.NPM_TOKEN ?? process.env.NODE_AUTH_TOKEN;
|
|
872
|
+
const registryHost = (() => {
|
|
873
|
+
try {
|
|
874
|
+
return new URL(registryUrl).host;
|
|
875
|
+
} catch {
|
|
876
|
+
return "registry.npmjs.org";
|
|
877
|
+
}
|
|
878
|
+
})();
|
|
879
|
+
const lines = [`registry=${registryUrl}`];
|
|
880
|
+
if (authMethod === "oidc") {
|
|
881
|
+
lines.push("always-auth=false");
|
|
882
|
+
}
|
|
883
|
+
if (authMethod === "token" && token) {
|
|
884
|
+
lines.push(`//${registryHost}/:_authToken=${token}`);
|
|
885
|
+
}
|
|
886
|
+
lines.push("");
|
|
887
|
+
const { npmrcPath, cleanup } = writeTempNpmrc(lines.join("\n"));
|
|
888
|
+
debug4(`Using isolated npm userconfig: ${npmrcPath}`);
|
|
889
|
+
const isOidc = authMethod === "oidc";
|
|
890
|
+
return {
|
|
891
|
+
env: {
|
|
892
|
+
...baseEnv,
|
|
893
|
+
// Ensure npm and tools that read npm_config_* pick up our temp file
|
|
894
|
+
NPM_CONFIG_USERCONFIG: npmrcPath,
|
|
895
|
+
npm_config_userconfig: npmrcPath,
|
|
896
|
+
// Auth-specific hardening
|
|
897
|
+
...isOidc ? {
|
|
898
|
+
// Prevent setup-node's always-auth from forcing token lookups
|
|
899
|
+
NPM_CONFIG_ALWAYS_AUTH: "false",
|
|
900
|
+
npm_config_always_auth: "false",
|
|
901
|
+
// Explicitly prevent token-based publishing from being selected implicitly
|
|
902
|
+
NODE_AUTH_TOKEN: void 0,
|
|
903
|
+
NPM_TOKEN: void 0
|
|
904
|
+
} : {
|
|
905
|
+
// Ensure CLIs that expect NODE_AUTH_TOKEN can still work
|
|
906
|
+
NODE_AUTH_TOKEN: token
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
cleanup
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/stages/npm-publish.ts
|
|
744
914
|
async function runNpmPublishStage(ctx) {
|
|
745
915
|
const { input, config, cliOptions, cwd } = ctx;
|
|
746
916
|
const dryRun = cliOptions.dryRun;
|
|
@@ -753,98 +923,108 @@ async function runNpmPublishStage(ctx) {
|
|
|
753
923
|
throw createPublishError("NPM_AUTH_ERROR" /* NPM_AUTH_ERROR */, "No NPM authentication method detected");
|
|
754
924
|
}
|
|
755
925
|
const useProvenance = config.npm.provenance && authMethod === "oidc";
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
926
|
+
const npmIsolation = createNpmSubprocessIsolation({
|
|
927
|
+
authMethod,
|
|
928
|
+
registryUrl: config.npm.registry
|
|
929
|
+
});
|
|
930
|
+
try {
|
|
931
|
+
for (const update of input.updates) {
|
|
932
|
+
const result = {
|
|
933
|
+
packageName: update.packageName,
|
|
934
|
+
version: update.newVersion,
|
|
935
|
+
registry: "npm",
|
|
936
|
+
success: false,
|
|
937
|
+
skipped: false
|
|
938
|
+
};
|
|
939
|
+
const pkgJsonPath = path5.resolve(cwd, update.filePath);
|
|
940
|
+
try {
|
|
941
|
+
const pkgContent = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
942
|
+
const pkgJson = JSON.parse(pkgContent);
|
|
943
|
+
if (pkgJson.private) {
|
|
944
|
+
result.skipped = true;
|
|
945
|
+
result.success = true;
|
|
946
|
+
result.reason = "Package is private";
|
|
947
|
+
ctx.output.npm.push(result);
|
|
948
|
+
debug5(`Skipping private package: ${update.packageName}`);
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
} catch {
|
|
952
|
+
if (update.filePath.endsWith("Cargo.toml")) {
|
|
953
|
+
result.skipped = true;
|
|
954
|
+
result.success = true;
|
|
955
|
+
result.reason = "Not an npm package";
|
|
956
|
+
ctx.output.npm.push(result);
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
775
959
|
}
|
|
776
|
-
|
|
777
|
-
|
|
960
|
+
const { file: viewFile, args: viewArgs } = buildViewCommand(
|
|
961
|
+
ctx.packageManager,
|
|
962
|
+
update.packageName,
|
|
963
|
+
update.newVersion
|
|
964
|
+
);
|
|
965
|
+
const viewResult = await execCommandSafe(viewFile, viewArgs, {
|
|
966
|
+
cwd,
|
|
967
|
+
dryRun: false,
|
|
968
|
+
// Always check, even in dry-run
|
|
969
|
+
env: npmIsolation.env
|
|
970
|
+
});
|
|
971
|
+
if (viewResult.exitCode === 0 && viewResult.stdout.trim()) {
|
|
972
|
+
result.alreadyPublished = true;
|
|
778
973
|
result.skipped = true;
|
|
779
974
|
result.success = true;
|
|
780
|
-
result.reason = "
|
|
975
|
+
result.reason = "Already published";
|
|
781
976
|
ctx.output.npm.push(result);
|
|
977
|
+
warn3(`${update.packageName}@${update.newVersion} is already published, skipping`);
|
|
782
978
|
continue;
|
|
783
979
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
ctx.packageManager,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
cwd,
|
|
792
|
-
dryRun: false
|
|
793
|
-
// Always check, even in dry-run
|
|
794
|
-
});
|
|
795
|
-
if (viewResult.exitCode === 0 && viewResult.stdout.trim()) {
|
|
796
|
-
result.alreadyPublished = true;
|
|
797
|
-
result.skipped = true;
|
|
798
|
-
result.success = true;
|
|
799
|
-
result.reason = "Already published";
|
|
800
|
-
ctx.output.npm.push(result);
|
|
801
|
-
warn3(`${update.packageName}@${update.newVersion} is already published, skipping`);
|
|
802
|
-
continue;
|
|
803
|
-
}
|
|
804
|
-
const distTag = getDistTag(update.newVersion, config.npm.tag);
|
|
805
|
-
const pkgDir = path4.dirname(path4.resolve(cwd, update.filePath));
|
|
806
|
-
const { file: pubFile, args: pubArgs } = buildPublishCommand(ctx.packageManager, update.packageName, pkgDir, {
|
|
807
|
-
access: config.npm.access,
|
|
808
|
-
tag: distTag,
|
|
809
|
-
provenance: useProvenance,
|
|
810
|
-
noGitChecks: true
|
|
811
|
-
});
|
|
812
|
-
try {
|
|
813
|
-
await execCommand(pubFile, pubArgs, {
|
|
814
|
-
cwd,
|
|
815
|
-
dryRun,
|
|
816
|
-
label: `npm publish ${update.packageName}@${update.newVersion}`
|
|
980
|
+
const distTag = getDistTag(update.newVersion, config.npm.tag);
|
|
981
|
+
const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
|
|
982
|
+
const { file: pubFile, args: pubArgs } = buildPublishCommand(ctx.packageManager, update.packageName, pkgDir, {
|
|
983
|
+
access: config.npm.access,
|
|
984
|
+
tag: distTag,
|
|
985
|
+
provenance: useProvenance,
|
|
986
|
+
noGitChecks: true
|
|
817
987
|
});
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
988
|
+
try {
|
|
989
|
+
await execCommand(pubFile, pubArgs, {
|
|
990
|
+
cwd,
|
|
991
|
+
dryRun,
|
|
992
|
+
label: `npm publish ${update.packageName}@${update.newVersion}`,
|
|
993
|
+
env: npmIsolation.env
|
|
994
|
+
});
|
|
995
|
+
result.success = true;
|
|
996
|
+
if (!dryRun) {
|
|
997
|
+
success5(`Published ${update.packageName}@${update.newVersion} to npm`);
|
|
998
|
+
}
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
result.reason = error instanceof Error ? error.message : String(error);
|
|
1001
|
+
warn3(`Failed to publish ${update.packageName}: ${result.reason}`);
|
|
821
1002
|
}
|
|
822
|
-
|
|
823
|
-
result.reason = error instanceof Error ? error.message : String(error);
|
|
824
|
-
warn3(`Failed to publish ${update.packageName}: ${result.reason}`);
|
|
1003
|
+
ctx.output.npm.push(result);
|
|
825
1004
|
}
|
|
826
|
-
|
|
1005
|
+
} finally {
|
|
1006
|
+
npmIsolation.cleanup();
|
|
827
1007
|
}
|
|
828
1008
|
}
|
|
829
1009
|
|
|
830
1010
|
// src/stages/prepare.ts
|
|
831
|
-
import * as
|
|
832
|
-
import * as
|
|
833
|
-
import { debug as
|
|
1011
|
+
import * as fs7 from "fs";
|
|
1012
|
+
import * as path6 from "path";
|
|
1013
|
+
import { debug as debug6, info as info6 } from "@releasekit/core";
|
|
834
1014
|
async function runPrepareStage(ctx) {
|
|
835
1015
|
const { input, config, cliOptions, cwd } = ctx;
|
|
836
1016
|
if (config.npm.enabled && config.npm.copyFiles.length > 0) {
|
|
837
1017
|
for (const update of input.updates) {
|
|
838
|
-
const pkgDir =
|
|
1018
|
+
const pkgDir = path6.dirname(path6.resolve(cwd, update.filePath));
|
|
839
1019
|
for (const file of config.npm.copyFiles) {
|
|
840
|
-
const src =
|
|
841
|
-
const dest =
|
|
842
|
-
if (!
|
|
843
|
-
|
|
1020
|
+
const src = path6.resolve(cwd, file);
|
|
1021
|
+
const dest = path6.join(pkgDir, file);
|
|
1022
|
+
if (!fs7.existsSync(src)) {
|
|
1023
|
+
debug6(`Source file not found, skipping copy: ${src}`);
|
|
844
1024
|
continue;
|
|
845
1025
|
}
|
|
846
|
-
if (
|
|
847
|
-
|
|
1026
|
+
if (path6.resolve(path6.dirname(src)) === path6.resolve(pkgDir)) {
|
|
1027
|
+
debug6(`Skipping copy of ${file} - same directory as source`);
|
|
848
1028
|
continue;
|
|
849
1029
|
}
|
|
850
1030
|
if (cliOptions.dryRun) {
|
|
@@ -852,8 +1032,8 @@ async function runPrepareStage(ctx) {
|
|
|
852
1032
|
continue;
|
|
853
1033
|
}
|
|
854
1034
|
try {
|
|
855
|
-
|
|
856
|
-
|
|
1035
|
+
fs7.copyFileSync(src, dest);
|
|
1036
|
+
debug6(`Copied ${file} \u2192 ${pkgDir}`);
|
|
857
1037
|
} catch (error) {
|
|
858
1038
|
throw createPublishError(
|
|
859
1039
|
"FILE_COPY_ERROR" /* FILE_COPY_ERROR */,
|
|
@@ -865,9 +1045,9 @@ async function runPrepareStage(ctx) {
|
|
|
865
1045
|
}
|
|
866
1046
|
if (config.cargo.enabled) {
|
|
867
1047
|
for (const update of input.updates) {
|
|
868
|
-
const pkgDir =
|
|
869
|
-
const cargoPath =
|
|
870
|
-
if (!
|
|
1048
|
+
const pkgDir = path6.dirname(path6.resolve(cwd, update.filePath));
|
|
1049
|
+
const cargoPath = path6.join(pkgDir, "Cargo.toml");
|
|
1050
|
+
if (!fs7.existsSync(cargoPath)) {
|
|
871
1051
|
continue;
|
|
872
1052
|
}
|
|
873
1053
|
if (cliOptions.dryRun) {
|
|
@@ -875,16 +1055,16 @@ async function runPrepareStage(ctx) {
|
|
|
875
1055
|
continue;
|
|
876
1056
|
}
|
|
877
1057
|
updateCargoVersion(cargoPath, update.newVersion);
|
|
878
|
-
|
|
1058
|
+
debug6(`Updated ${cargoPath} to version ${update.newVersion}`);
|
|
879
1059
|
}
|
|
880
1060
|
}
|
|
881
1061
|
}
|
|
882
1062
|
|
|
883
1063
|
// src/stages/verify.ts
|
|
884
|
-
import { debug as
|
|
1064
|
+
import { debug as debug8, info as info7, success as success6, warn as warn4 } from "@releasekit/core";
|
|
885
1065
|
|
|
886
1066
|
// src/utils/retry.ts
|
|
887
|
-
import { debug as
|
|
1067
|
+
import { debug as debug7 } from "@releasekit/core";
|
|
888
1068
|
async function withRetry(fn, options, shouldRetry) {
|
|
889
1069
|
let lastError;
|
|
890
1070
|
let delay = options.initialDelay;
|
|
@@ -897,7 +1077,7 @@ async function withRetry(fn, options, shouldRetry) {
|
|
|
897
1077
|
throw error;
|
|
898
1078
|
}
|
|
899
1079
|
if (attempt < options.maxAttempts) {
|
|
900
|
-
|
|
1080
|
+
debug7(`Attempt ${attempt}/${options.maxAttempts} failed, retrying in ${delay}ms...`);
|
|
901
1081
|
await sleep(delay);
|
|
902
1082
|
delay = Math.floor(delay * options.backoffMultiplier);
|
|
903
1083
|
}
|
|
@@ -939,7 +1119,7 @@ async function runVerifyStage(ctx) {
|
|
|
939
1119
|
if (viewResult.exitCode !== 0 || !viewResult.stdout.trim()) {
|
|
940
1120
|
throw new Error(`${pkg.packageName}@${pkg.version} not yet available on npm`);
|
|
941
1121
|
}
|
|
942
|
-
|
|
1122
|
+
debug8(`Verified ${pkg.packageName}@${pkg.version} on npm`);
|
|
943
1123
|
}, config.verify.npm);
|
|
944
1124
|
result.verified = true;
|
|
945
1125
|
success6(`Verified ${pkg.packageName}@${pkg.version} on npm`);
|
|
@@ -972,7 +1152,7 @@ async function runVerifyStage(ctx) {
|
|
|
972
1152
|
if (!response.ok) {
|
|
973
1153
|
throw new Error(`${crate.packageName}@${crate.version} not yet available on crates.io`);
|
|
974
1154
|
}
|
|
975
|
-
|
|
1155
|
+
debug8(`Verified ${crate.packageName}@${crate.version} on crates.io`);
|
|
976
1156
|
}, config.verify.cargo);
|
|
977
1157
|
result.verified = true;
|
|
978
1158
|
success6(`Verified ${crate.packageName}@${crate.version} on crates.io`);
|
|
@@ -1012,6 +1192,8 @@ async function runPipeline(input, config, options) {
|
|
|
1012
1192
|
cliOptions: options,
|
|
1013
1193
|
packageManager: detectPackageManager(cwd),
|
|
1014
1194
|
cwd,
|
|
1195
|
+
releaseNotes: options.releaseNotes,
|
|
1196
|
+
additionalFiles: options.additionalFiles,
|
|
1015
1197
|
output: {
|
|
1016
1198
|
dryRun: options.dryRun,
|
|
1017
1199
|
git: { committed: false, tags: [], pushed: false },
|
|
@@ -1023,7 +1205,10 @@ async function runPipeline(input, config, options) {
|
|
|
1023
1205
|
};
|
|
1024
1206
|
try {
|
|
1025
1207
|
await runPrepareStage(ctx);
|
|
1026
|
-
if (!options.skipGit) {
|
|
1208
|
+
if (options.skipGitCommit && !options.skipGit) {
|
|
1209
|
+
ctx.output.git.committed = !!input.commitMessage;
|
|
1210
|
+
ctx.output.git.tags = [...input.tags];
|
|
1211
|
+
} else if (!options.skipGit) {
|
|
1027
1212
|
await runGitCommitStage(ctx);
|
|
1028
1213
|
}
|
|
1029
1214
|
if (!options.skipPublish) {
|
|
@@ -1052,7 +1237,7 @@ async function runPipeline(input, config, options) {
|
|
|
1052
1237
|
}
|
|
1053
1238
|
|
|
1054
1239
|
// src/stages/input.ts
|
|
1055
|
-
import * as
|
|
1240
|
+
import * as fs8 from "fs";
|
|
1056
1241
|
import { info as info8 } from "@releasekit/core";
|
|
1057
1242
|
import { z } from "zod";
|
|
1058
1243
|
var VersionChangelogEntrySchema = z.object({
|
|
@@ -1086,7 +1271,7 @@ async function parseInput(inputPath) {
|
|
|
1086
1271
|
let raw;
|
|
1087
1272
|
if (inputPath) {
|
|
1088
1273
|
try {
|
|
1089
|
-
raw =
|
|
1274
|
+
raw = fs8.readFileSync(inputPath, "utf-8");
|
|
1090
1275
|
} catch {
|
|
1091
1276
|
throw createPublishError("INPUT_PARSE_ERROR" /* INPUT_PARSE_ERROR */, `Could not read file: ${inputPath}`);
|
|
1092
1277
|
}
|