@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/dist/cli.cjs CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli.ts
27
- var import_core12 = require("@releasekit/core");
27
+ var import_core13 = require("@releasekit/core");
28
28
  var import_commander = require("commander");
29
29
 
30
30
  // src/config.ts
@@ -52,14 +52,16 @@ function getDefaultConfig() {
52
52
  push: true,
53
53
  pushMethod: "auto",
54
54
  remote: "origin",
55
- branch: "main"
55
+ branch: "main",
56
+ httpsTokenEnv: void 0,
57
+ skipHooks: false
56
58
  },
57
59
  githubRelease: {
58
60
  enabled: true,
59
61
  draft: true,
60
- generateNotes: true,
61
- perPackage: false,
62
- prerelease: "auto"
62
+ perPackage: true,
63
+ prerelease: "auto",
64
+ releaseNotes: "auto"
63
65
  },
64
66
  verify: {
65
67
  npm: {
@@ -100,15 +102,16 @@ function toPublishConfig(config) {
100
102
  push: config.git.push ?? defaults.git.push,
101
103
  pushMethod: config.git.pushMethod ?? defaults.git.pushMethod,
102
104
  remote: config.git.remote ?? defaults.git.remote,
103
- branch: config.git.branch ?? defaults.git.branch
105
+ branch: config.git.branch ?? defaults.git.branch,
106
+ httpsTokenEnv: config.git.httpsTokenEnv ?? defaults.git.httpsTokenEnv,
107
+ skipHooks: config.git.skipHooks ?? defaults.git.skipHooks
104
108
  } : defaults.git,
105
109
  githubRelease: {
106
110
  enabled: config.githubRelease?.enabled ?? defaults.githubRelease.enabled,
107
111
  draft: config.githubRelease?.draft ?? defaults.githubRelease.draft,
108
- generateNotes: config.githubRelease?.generateNotes ?? defaults.githubRelease.generateNotes,
109
112
  perPackage: config.githubRelease?.perPackage ?? defaults.githubRelease.perPackage,
110
113
  prerelease: config.githubRelease?.prerelease ?? defaults.githubRelease.prerelease,
111
- notesFile: config.githubRelease?.notesFile
114
+ releaseNotes: config.githubRelease?.releaseNotes ?? defaults.githubRelease.releaseNotes
112
115
  },
113
116
  verify: {
114
117
  npm: {
@@ -261,8 +264,20 @@ var import_core3 = require("@releasekit/core");
261
264
  // src/utils/exec.ts
262
265
  var import_node_child_process = require("child_process");
263
266
  var import_core2 = require("@releasekit/core");
267
+ function redactArg(arg) {
268
+ try {
269
+ const url = new URL(arg);
270
+ if (url.username || url.password) {
271
+ url.username = url.username ? "***" : "";
272
+ url.password = url.password ? "***" : "";
273
+ return url.toString();
274
+ }
275
+ } catch {
276
+ }
277
+ return arg;
278
+ }
264
279
  async function execCommand(file, args, options = {}) {
265
- const displayCommand = options.label ?? [file, ...args].join(" ");
280
+ const displayCommand = options.label ?? [file, ...args.map(redactArg)].join(" ");
266
281
  if (options.dryRun) {
267
282
  (0, import_core2.info)(`[DRY RUN] Would execute: ${displayCommand}`);
268
283
  return { stdout: "", stderr: "", exitCode: 0 };
@@ -318,7 +333,7 @@ function detectNpmAuth() {
318
333
  if (process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
319
334
  return "oidc";
320
335
  }
321
- if (process.env.NPM_TOKEN) {
336
+ if (process.env.NPM_TOKEN || process.env.NODE_AUTH_TOKEN) {
322
337
  return "token";
323
338
  }
324
339
  return null;
@@ -527,13 +542,17 @@ function topologicalSort(crates) {
527
542
  var path2 = __toESM(require("path"), 1);
528
543
  var import_core4 = require("@releasekit/core");
529
544
  async function runGitCommitStage(ctx) {
530
- const { input, cliOptions, cwd } = ctx;
545
+ const { input, config, cliOptions, cwd } = ctx;
531
546
  const dryRun = cliOptions.dryRun;
547
+ const skipHooks = config.git.skipHooks ?? false;
532
548
  if (!input.commitMessage) {
533
549
  (0, import_core4.info)("No commit message provided, skipping git commit");
534
550
  return;
535
551
  }
536
552
  const filePaths = input.updates.map((u) => path2.resolve(cwd, u.filePath));
553
+ if (ctx.additionalFiles) {
554
+ filePaths.push(...ctx.additionalFiles.map((f) => path2.resolve(cwd, f)));
555
+ }
537
556
  if (filePaths.length === 0) {
538
557
  (0, import_core4.info)("No files to commit");
539
558
  return;
@@ -550,8 +569,13 @@ async function runGitCommitStage(ctx) {
550
569
  `git add failed: ${error instanceof Error ? error.message : String(error)}`
551
570
  );
552
571
  }
572
+ const commitArgs = ["commit"];
573
+ if (skipHooks) {
574
+ commitArgs.push("--no-verify");
575
+ }
576
+ commitArgs.push("-m", input.commitMessage);
553
577
  try {
554
- await execCommand("git", ["commit", "-m", input.commitMessage], {
578
+ await execCommand("git", commitArgs, {
555
579
  cwd,
556
580
  dryRun,
557
581
  label: `git commit -m "${input.commitMessage}"`
@@ -589,6 +613,18 @@ async function runGitCommitStage(ctx) {
589
613
 
590
614
  // src/stages/git-push.ts
591
615
  var import_core5 = require("@releasekit/core");
616
+ function toGithubAuthedUrl(remoteUrl, token) {
617
+ try {
618
+ const url = new URL(remoteUrl);
619
+ if (url.protocol !== "https:") return void 0;
620
+ if (url.host !== "github.com") return void 0;
621
+ url.username = "x-access-token";
622
+ url.password = token;
623
+ return url.toString();
624
+ } catch {
625
+ return void 0;
626
+ }
627
+ }
592
628
  async function runGitPushStage(ctx) {
593
629
  const { config, cliOptions, cwd, output } = ctx;
594
630
  const dryRun = cliOptions.dryRun;
@@ -609,16 +645,26 @@ async function runGitPushStage(ctx) {
609
645
  pushMethod = "https";
610
646
  }
611
647
  }
648
+ const httpsTokenEnv = config.git.httpsTokenEnv;
649
+ const httpsToken = httpsTokenEnv ? process.env[httpsTokenEnv] : void 0;
612
650
  try {
651
+ let pushRemote = remote;
652
+ if (pushMethod === "https" && httpsToken) {
653
+ const remoteUrlResult = await execCommand("git", ["remote", "get-url", remote], { cwd, dryRun: false });
654
+ const authed = toGithubAuthedUrl(remoteUrlResult.stdout.trim(), httpsToken);
655
+ if (authed) {
656
+ pushRemote = authed;
657
+ }
658
+ }
613
659
  if (output.git.committed) {
614
- await execCommand("git", ["push", remote, branch], {
660
+ await execCommand("git", ["push", pushRemote, branch], {
615
661
  cwd,
616
662
  dryRun,
617
663
  label: `git push ${remote} ${branch}`
618
664
  });
619
665
  }
620
666
  if (output.git.tags.length > 0) {
621
- await execCommand("git", ["push", remote, "--tags"], {
667
+ await execCommand("git", ["push", pushRemote, "--tags"], {
622
668
  cwd,
623
669
  dryRun,
624
670
  label: `git push ${remote} --tags`
@@ -655,6 +701,62 @@ function getDistTag(version, defaultTag = "latest") {
655
701
  }
656
702
 
657
703
  // src/stages/github-release.ts
704
+ function resolveNotes(notesSetting, tag, changelogs, pipelineNotes) {
705
+ if (notesSetting === "none") {
706
+ return { useGithubNotes: false };
707
+ }
708
+ if (notesSetting === "github") {
709
+ return { useGithubNotes: true };
710
+ }
711
+ if (notesSetting !== "auto") {
712
+ const body = readFileIfExists(notesSetting);
713
+ if (body) return { body, useGithubNotes: false };
714
+ (0, import_core6.debug)(`Notes file not found: ${notesSetting}, falling back to GitHub auto-notes`);
715
+ return { useGithubNotes: true };
716
+ }
717
+ if (pipelineNotes) {
718
+ const body = findNotesForTag(tag, pipelineNotes);
719
+ if (body) return { body, useGithubNotes: false };
720
+ }
721
+ const packageBody = formatChangelogForTag(tag, changelogs);
722
+ if (packageBody) {
723
+ return { body: packageBody, useGithubNotes: false };
724
+ }
725
+ return { useGithubNotes: true };
726
+ }
727
+ function isVersionOnlyTag(tag) {
728
+ return /^v?\d+\.\d+\.\d+/.test(tag);
729
+ }
730
+ function findNotesForTag(tag, notes) {
731
+ for (const [packageName, body] of Object.entries(notes)) {
732
+ if (tag.startsWith(`${packageName}@`) && body.trim()) {
733
+ return body;
734
+ }
735
+ }
736
+ const entries = Object.values(notes).filter((b) => b.trim());
737
+ if (entries.length === 1 && isVersionOnlyTag(tag)) return entries[0];
738
+ return void 0;
739
+ }
740
+ function readFileIfExists(filePath) {
741
+ try {
742
+ const content = fs3.readFileSync(filePath, "utf-8").trim();
743
+ return content || void 0;
744
+ } catch {
745
+ return void 0;
746
+ }
747
+ }
748
+ function formatChangelogForTag(tag, changelogs) {
749
+ if (changelogs.length === 0) return void 0;
750
+ const changelog = changelogs.find((c) => tag.startsWith(`${c.packageName}@`));
751
+ const target = changelog ?? (changelogs.length === 1 && isVersionOnlyTag(tag) ? changelogs[0] : void 0);
752
+ if (!target || target.entries.length === 0) return void 0;
753
+ const lines = [];
754
+ for (const entry of target.entries) {
755
+ const scope = entry.scope ? `**${entry.scope}:** ` : "";
756
+ lines.push(`- ${scope}${entry.description}`);
757
+ }
758
+ return lines.join("\n");
759
+ }
658
760
  async function runGithubReleaseStage(ctx) {
659
761
  const { config, cliOptions, output } = ctx;
660
762
  const dryRun = cliOptions.dryRun;
@@ -667,19 +769,13 @@ async function runGithubReleaseStage(ctx) {
667
769
  (0, import_core6.info)("No tags available for GitHub release");
668
770
  return;
669
771
  }
670
- let notesBody;
671
- if (config.githubRelease.notesFile) {
672
- try {
673
- notesBody = fs3.readFileSync(config.githubRelease.notesFile, "utf-8");
674
- } catch {
675
- (0, import_core6.debug)(`Could not read notes file: ${config.githubRelease.notesFile}`);
676
- }
677
- }
678
772
  const firstTag = tags[0];
679
773
  if (!firstTag) return;
680
774
  const tagsToRelease = config.githubRelease.perPackage ? tags : [firstTag];
681
775
  for (const tag of tagsToRelease) {
682
- const versionMatch = tag.match(/(\d+\.\d+\.\d+.*)$/);
776
+ const MAX_TAG_LENGTH = 1e3;
777
+ const truncatedTag = tag.length > MAX_TAG_LENGTH ? tag.slice(0, MAX_TAG_LENGTH) : tag;
778
+ const versionMatch = truncatedTag.match(/(\d{1,20}\.\d{1,20}\.\d{1,20}(?:[-+.]?[a-zA-Z0-9.-]{0,100})?)$/);
683
779
  const version = versionMatch?.[1] ?? "";
684
780
  const isPreRel = config.githubRelease.prerelease === "auto" ? version ? isPrerelease(version) : false : config.githubRelease.prerelease;
685
781
  const result = {
@@ -695,9 +791,15 @@ async function runGithubReleaseStage(ctx) {
695
791
  if (isPreRel) {
696
792
  ghArgs.push("--prerelease");
697
793
  }
698
- if (notesBody) {
699
- ghArgs.push("--notes", notesBody);
700
- } else if (config.githubRelease.generateNotes) {
794
+ const { body, useGithubNotes } = resolveNotes(
795
+ config.githubRelease.releaseNotes,
796
+ tag,
797
+ ctx.input.changelogs,
798
+ ctx.releaseNotes
799
+ );
800
+ if (body) {
801
+ ghArgs.push("--notes", body);
802
+ } else if (useGithubNotes) {
701
803
  ghArgs.push("--generate-notes");
702
804
  }
703
805
  try {
@@ -721,16 +823,82 @@ async function runGithubReleaseStage(ctx) {
721
823
  }
722
824
 
723
825
  // src/stages/npm-publish.ts
724
- var fs5 = __toESM(require("fs"), 1);
725
- var path4 = __toESM(require("path"), 1);
726
- var import_core7 = require("@releasekit/core");
826
+ var fs6 = __toESM(require("fs"), 1);
827
+ var path5 = __toESM(require("path"), 1);
828
+ var import_core8 = require("@releasekit/core");
727
829
 
728
- // src/utils/package-manager.ts
830
+ // src/utils/npm-env.ts
729
831
  var fs4 = __toESM(require("fs"), 1);
832
+ var os = __toESM(require("os"), 1);
730
833
  var path3 = __toESM(require("path"), 1);
834
+ var import_core7 = require("@releasekit/core");
835
+ function writeTempNpmrc(contents) {
836
+ const dir = fs4.mkdtempSync(path3.join(os.tmpdir(), "releasekit-npmrc-"));
837
+ const npmrcPath = path3.join(dir, ".npmrc");
838
+ fs4.writeFileSync(npmrcPath, contents, "utf-8");
839
+ return {
840
+ npmrcPath,
841
+ cleanup: () => {
842
+ try {
843
+ fs4.rmSync(dir, { recursive: true, force: true });
844
+ } catch {
845
+ }
846
+ }
847
+ };
848
+ }
849
+ function createNpmSubprocessIsolation(options) {
850
+ const { authMethod, registryUrl } = options;
851
+ const baseEnv = {};
852
+ if (!authMethod) return { env: baseEnv, cleanup: () => {
853
+ } };
854
+ const token = process.env.NPM_TOKEN ?? process.env.NODE_AUTH_TOKEN;
855
+ const registryHost = (() => {
856
+ try {
857
+ return new URL(registryUrl).host;
858
+ } catch {
859
+ return "registry.npmjs.org";
860
+ }
861
+ })();
862
+ const lines = [`registry=${registryUrl}`];
863
+ if (authMethod === "oidc") {
864
+ lines.push("always-auth=false");
865
+ }
866
+ if (authMethod === "token" && token) {
867
+ lines.push(`//${registryHost}/:_authToken=${token}`);
868
+ }
869
+ lines.push("");
870
+ const { npmrcPath, cleanup } = writeTempNpmrc(lines.join("\n"));
871
+ (0, import_core7.debug)(`Using isolated npm userconfig: ${npmrcPath}`);
872
+ const isOidc = authMethod === "oidc";
873
+ return {
874
+ env: {
875
+ ...baseEnv,
876
+ // Ensure npm and tools that read npm_config_* pick up our temp file
877
+ NPM_CONFIG_USERCONFIG: npmrcPath,
878
+ npm_config_userconfig: npmrcPath,
879
+ // Auth-specific hardening
880
+ ...isOidc ? {
881
+ // Prevent setup-node's always-auth from forcing token lookups
882
+ NPM_CONFIG_ALWAYS_AUTH: "false",
883
+ npm_config_always_auth: "false",
884
+ // Explicitly prevent token-based publishing from being selected implicitly
885
+ NODE_AUTH_TOKEN: void 0,
886
+ NPM_TOKEN: void 0
887
+ } : {
888
+ // Ensure CLIs that expect NODE_AUTH_TOKEN can still work
889
+ NODE_AUTH_TOKEN: token
890
+ }
891
+ },
892
+ cleanup
893
+ };
894
+ }
895
+
896
+ // src/utils/package-manager.ts
897
+ var fs5 = __toESM(require("fs"), 1);
898
+ var path4 = __toESM(require("path"), 1);
731
899
  function detectPackageManager(cwd) {
732
- if (fs4.existsSync(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
733
- if (fs4.existsSync(path3.join(cwd, "yarn.lock"))) return "yarn";
900
+ if (fs5.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
901
+ if (fs5.existsSync(path4.join(cwd, "yarn.lock"))) return "yarn";
734
902
  return "npm";
735
903
  }
736
904
  function buildPublishCommand(pm, packageName, _packageDir, options) {
@@ -759,7 +927,7 @@ async function runNpmPublishStage(ctx) {
759
927
  const { input, config, cliOptions, cwd } = ctx;
760
928
  const dryRun = cliOptions.dryRun;
761
929
  if (!config.npm.enabled) {
762
- (0, import_core7.info)("NPM publishing disabled in config");
930
+ (0, import_core8.info)("NPM publishing disabled in config");
763
931
  return;
764
932
  }
765
933
  const authMethod = config.npm.auth === "auto" ? detectNpmAuth() : config.npm.auth;
@@ -767,107 +935,117 @@ async function runNpmPublishStage(ctx) {
767
935
  throw createPublishError("NPM_AUTH_ERROR" /* NPM_AUTH_ERROR */, "No NPM authentication method detected");
768
936
  }
769
937
  const useProvenance = config.npm.provenance && authMethod === "oidc";
770
- for (const update of input.updates) {
771
- const result = {
772
- packageName: update.packageName,
773
- version: update.newVersion,
774
- registry: "npm",
775
- success: false,
776
- skipped: false
777
- };
778
- const pkgJsonPath = path4.resolve(cwd, update.filePath);
779
- try {
780
- const pkgContent = fs5.readFileSync(pkgJsonPath, "utf-8");
781
- const pkgJson = JSON.parse(pkgContent);
782
- if (pkgJson.private) {
783
- result.skipped = true;
784
- result.success = true;
785
- result.reason = "Package is private";
786
- ctx.output.npm.push(result);
787
- (0, import_core7.debug)(`Skipping private package: ${update.packageName}`);
788
- continue;
938
+ const npmIsolation = createNpmSubprocessIsolation({
939
+ authMethod,
940
+ registryUrl: config.npm.registry
941
+ });
942
+ try {
943
+ for (const update of input.updates) {
944
+ const result = {
945
+ packageName: update.packageName,
946
+ version: update.newVersion,
947
+ registry: "npm",
948
+ success: false,
949
+ skipped: false
950
+ };
951
+ const pkgJsonPath = path5.resolve(cwd, update.filePath);
952
+ try {
953
+ const pkgContent = fs6.readFileSync(pkgJsonPath, "utf-8");
954
+ const pkgJson = JSON.parse(pkgContent);
955
+ if (pkgJson.private) {
956
+ result.skipped = true;
957
+ result.success = true;
958
+ result.reason = "Package is private";
959
+ ctx.output.npm.push(result);
960
+ (0, import_core8.debug)(`Skipping private package: ${update.packageName}`);
961
+ continue;
962
+ }
963
+ } catch {
964
+ if (update.filePath.endsWith("Cargo.toml")) {
965
+ result.skipped = true;
966
+ result.success = true;
967
+ result.reason = "Not an npm package";
968
+ ctx.output.npm.push(result);
969
+ continue;
970
+ }
789
971
  }
790
- } catch {
791
- if (update.filePath.endsWith("Cargo.toml")) {
972
+ const { file: viewFile, args: viewArgs } = buildViewCommand(
973
+ ctx.packageManager,
974
+ update.packageName,
975
+ update.newVersion
976
+ );
977
+ const viewResult = await execCommandSafe(viewFile, viewArgs, {
978
+ cwd,
979
+ dryRun: false,
980
+ // Always check, even in dry-run
981
+ env: npmIsolation.env
982
+ });
983
+ if (viewResult.exitCode === 0 && viewResult.stdout.trim()) {
984
+ result.alreadyPublished = true;
792
985
  result.skipped = true;
793
986
  result.success = true;
794
- result.reason = "Not an npm package";
987
+ result.reason = "Already published";
795
988
  ctx.output.npm.push(result);
989
+ (0, import_core8.warn)(`${update.packageName}@${update.newVersion} is already published, skipping`);
796
990
  continue;
797
991
  }
798
- }
799
- const { file: viewFile, args: viewArgs } = buildViewCommand(
800
- ctx.packageManager,
801
- update.packageName,
802
- update.newVersion
803
- );
804
- const viewResult = await execCommandSafe(viewFile, viewArgs, {
805
- cwd,
806
- dryRun: false
807
- // Always check, even in dry-run
808
- });
809
- if (viewResult.exitCode === 0 && viewResult.stdout.trim()) {
810
- result.alreadyPublished = true;
811
- result.skipped = true;
812
- result.success = true;
813
- result.reason = "Already published";
814
- ctx.output.npm.push(result);
815
- (0, import_core7.warn)(`${update.packageName}@${update.newVersion} is already published, skipping`);
816
- continue;
817
- }
818
- const distTag = getDistTag(update.newVersion, config.npm.tag);
819
- const pkgDir = path4.dirname(path4.resolve(cwd, update.filePath));
820
- const { file: pubFile, args: pubArgs } = buildPublishCommand(ctx.packageManager, update.packageName, pkgDir, {
821
- access: config.npm.access,
822
- tag: distTag,
823
- provenance: useProvenance,
824
- noGitChecks: true
825
- });
826
- try {
827
- await execCommand(pubFile, pubArgs, {
828
- cwd,
829
- dryRun,
830
- label: `npm publish ${update.packageName}@${update.newVersion}`
992
+ const distTag = getDistTag(update.newVersion, config.npm.tag);
993
+ const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
994
+ const { file: pubFile, args: pubArgs } = buildPublishCommand(ctx.packageManager, update.packageName, pkgDir, {
995
+ access: config.npm.access,
996
+ tag: distTag,
997
+ provenance: useProvenance,
998
+ noGitChecks: true
831
999
  });
832
- result.success = true;
833
- if (!dryRun) {
834
- (0, import_core7.success)(`Published ${update.packageName}@${update.newVersion} to npm`);
1000
+ try {
1001
+ await execCommand(pubFile, pubArgs, {
1002
+ cwd,
1003
+ dryRun,
1004
+ label: `npm publish ${update.packageName}@${update.newVersion}`,
1005
+ env: npmIsolation.env
1006
+ });
1007
+ result.success = true;
1008
+ if (!dryRun) {
1009
+ (0, import_core8.success)(`Published ${update.packageName}@${update.newVersion} to npm`);
1010
+ }
1011
+ } catch (error) {
1012
+ result.reason = error instanceof Error ? error.message : String(error);
1013
+ (0, import_core8.warn)(`Failed to publish ${update.packageName}: ${result.reason}`);
835
1014
  }
836
- } catch (error) {
837
- result.reason = error instanceof Error ? error.message : String(error);
838
- (0, import_core7.warn)(`Failed to publish ${update.packageName}: ${result.reason}`);
1015
+ ctx.output.npm.push(result);
839
1016
  }
840
- ctx.output.npm.push(result);
1017
+ } finally {
1018
+ npmIsolation.cleanup();
841
1019
  }
842
1020
  }
843
1021
 
844
1022
  // src/stages/prepare.ts
845
- var fs6 = __toESM(require("fs"), 1);
846
- var path5 = __toESM(require("path"), 1);
847
- var import_core8 = require("@releasekit/core");
1023
+ var fs7 = __toESM(require("fs"), 1);
1024
+ var path6 = __toESM(require("path"), 1);
1025
+ var import_core9 = require("@releasekit/core");
848
1026
  async function runPrepareStage(ctx) {
849
1027
  const { input, config, cliOptions, cwd } = ctx;
850
1028
  if (config.npm.enabled && config.npm.copyFiles.length > 0) {
851
1029
  for (const update of input.updates) {
852
- const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
1030
+ const pkgDir = path6.dirname(path6.resolve(cwd, update.filePath));
853
1031
  for (const file of config.npm.copyFiles) {
854
- const src = path5.resolve(cwd, file);
855
- const dest = path5.join(pkgDir, file);
856
- if (!fs6.existsSync(src)) {
857
- (0, import_core8.debug)(`Source file not found, skipping copy: ${src}`);
1032
+ const src = path6.resolve(cwd, file);
1033
+ const dest = path6.join(pkgDir, file);
1034
+ if (!fs7.existsSync(src)) {
1035
+ (0, import_core9.debug)(`Source file not found, skipping copy: ${src}`);
858
1036
  continue;
859
1037
  }
860
- if (path5.resolve(path5.dirname(src)) === path5.resolve(pkgDir)) {
861
- (0, import_core8.debug)(`Skipping copy of ${file} - same directory as source`);
1038
+ if (path6.resolve(path6.dirname(src)) === path6.resolve(pkgDir)) {
1039
+ (0, import_core9.debug)(`Skipping copy of ${file} - same directory as source`);
862
1040
  continue;
863
1041
  }
864
1042
  if (cliOptions.dryRun) {
865
- (0, import_core8.info)(`[DRY RUN] Would copy ${src} \u2192 ${dest}`);
1043
+ (0, import_core9.info)(`[DRY RUN] Would copy ${src} \u2192 ${dest}`);
866
1044
  continue;
867
1045
  }
868
1046
  try {
869
- fs6.copyFileSync(src, dest);
870
- (0, import_core8.debug)(`Copied ${file} \u2192 ${pkgDir}`);
1047
+ fs7.copyFileSync(src, dest);
1048
+ (0, import_core9.debug)(`Copied ${file} \u2192 ${pkgDir}`);
871
1049
  } catch (error) {
872
1050
  throw createPublishError(
873
1051
  "FILE_COPY_ERROR" /* FILE_COPY_ERROR */,
@@ -879,26 +1057,26 @@ async function runPrepareStage(ctx) {
879
1057
  }
880
1058
  if (config.cargo.enabled) {
881
1059
  for (const update of input.updates) {
882
- const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
883
- const cargoPath = path5.join(pkgDir, "Cargo.toml");
884
- if (!fs6.existsSync(cargoPath)) {
1060
+ const pkgDir = path6.dirname(path6.resolve(cwd, update.filePath));
1061
+ const cargoPath = path6.join(pkgDir, "Cargo.toml");
1062
+ if (!fs7.existsSync(cargoPath)) {
885
1063
  continue;
886
1064
  }
887
1065
  if (cliOptions.dryRun) {
888
- (0, import_core8.info)(`[DRY RUN] Would update ${cargoPath} to version ${update.newVersion}`);
1066
+ (0, import_core9.info)(`[DRY RUN] Would update ${cargoPath} to version ${update.newVersion}`);
889
1067
  continue;
890
1068
  }
891
1069
  updateCargoVersion(cargoPath, update.newVersion);
892
- (0, import_core8.debug)(`Updated ${cargoPath} to version ${update.newVersion}`);
1070
+ (0, import_core9.debug)(`Updated ${cargoPath} to version ${update.newVersion}`);
893
1071
  }
894
1072
  }
895
1073
  }
896
1074
 
897
1075
  // src/stages/verify.ts
898
- var import_core10 = require("@releasekit/core");
1076
+ var import_core11 = require("@releasekit/core");
899
1077
 
900
1078
  // src/utils/retry.ts
901
- var import_core9 = require("@releasekit/core");
1079
+ var import_core10 = require("@releasekit/core");
902
1080
  async function withRetry(fn, options, shouldRetry) {
903
1081
  let lastError;
904
1082
  let delay = options.initialDelay;
@@ -911,7 +1089,7 @@ async function withRetry(fn, options, shouldRetry) {
911
1089
  throw error;
912
1090
  }
913
1091
  if (attempt < options.maxAttempts) {
914
- (0, import_core9.debug)(`Attempt ${attempt}/${options.maxAttempts} failed, retrying in ${delay}ms...`);
1092
+ (0, import_core10.debug)(`Attempt ${attempt}/${options.maxAttempts} failed, retrying in ${delay}ms...`);
915
1093
  await sleep(delay);
916
1094
  delay = Math.floor(delay * options.backoffMultiplier);
917
1095
  }
@@ -937,7 +1115,7 @@ async function runVerifyStage(ctx) {
937
1115
  attempts: 0
938
1116
  };
939
1117
  if (cliOptions.dryRun) {
940
- (0, import_core10.info)(`[DRY RUN] Would verify ${pkg.packageName}@${pkg.version} on npm`);
1118
+ (0, import_core11.info)(`[DRY RUN] Would verify ${pkg.packageName}@${pkg.version} on npm`);
941
1119
  result.verified = true;
942
1120
  ctx.output.verification.push(result);
943
1121
  continue;
@@ -953,12 +1131,12 @@ async function runVerifyStage(ctx) {
953
1131
  if (viewResult.exitCode !== 0 || !viewResult.stdout.trim()) {
954
1132
  throw new Error(`${pkg.packageName}@${pkg.version} not yet available on npm`);
955
1133
  }
956
- (0, import_core10.debug)(`Verified ${pkg.packageName}@${pkg.version} on npm`);
1134
+ (0, import_core11.debug)(`Verified ${pkg.packageName}@${pkg.version} on npm`);
957
1135
  }, config.verify.npm);
958
1136
  result.verified = true;
959
- (0, import_core10.success)(`Verified ${pkg.packageName}@${pkg.version} on npm`);
1137
+ (0, import_core11.success)(`Verified ${pkg.packageName}@${pkg.version} on npm`);
960
1138
  } catch {
961
- (0, import_core10.warn)(`Failed to verify ${pkg.packageName}@${pkg.version} on npm after ${result.attempts} attempts`);
1139
+ (0, import_core11.warn)(`Failed to verify ${pkg.packageName}@${pkg.version} on npm after ${result.attempts} attempts`);
962
1140
  }
963
1141
  ctx.output.verification.push(result);
964
1142
  }
@@ -974,7 +1152,7 @@ async function runVerifyStage(ctx) {
974
1152
  attempts: 0
975
1153
  };
976
1154
  if (cliOptions.dryRun) {
977
- (0, import_core10.info)(`[DRY RUN] Would verify ${crate.packageName}@${crate.version} on crates.io`);
1155
+ (0, import_core11.info)(`[DRY RUN] Would verify ${crate.packageName}@${crate.version} on crates.io`);
978
1156
  result.verified = true;
979
1157
  ctx.output.verification.push(result);
980
1158
  continue;
@@ -986,12 +1164,12 @@ async function runVerifyStage(ctx) {
986
1164
  if (!response.ok) {
987
1165
  throw new Error(`${crate.packageName}@${crate.version} not yet available on crates.io`);
988
1166
  }
989
- (0, import_core10.debug)(`Verified ${crate.packageName}@${crate.version} on crates.io`);
1167
+ (0, import_core11.debug)(`Verified ${crate.packageName}@${crate.version} on crates.io`);
990
1168
  }, config.verify.cargo);
991
1169
  result.verified = true;
992
- (0, import_core10.success)(`Verified ${crate.packageName}@${crate.version} on crates.io`);
1170
+ (0, import_core11.success)(`Verified ${crate.packageName}@${crate.version} on crates.io`);
993
1171
  } catch {
994
- (0, import_core10.warn)(`Failed to verify ${crate.packageName}@${crate.version} on crates.io after ${result.attempts} attempts`);
1172
+ (0, import_core11.warn)(`Failed to verify ${crate.packageName}@${crate.version} on crates.io after ${result.attempts} attempts`);
995
1173
  }
996
1174
  ctx.output.verification.push(result);
997
1175
  }
@@ -1026,6 +1204,8 @@ async function runPipeline(input, config, options) {
1026
1204
  cliOptions: options,
1027
1205
  packageManager: detectPackageManager(cwd),
1028
1206
  cwd,
1207
+ releaseNotes: options.releaseNotes,
1208
+ additionalFiles: options.additionalFiles,
1029
1209
  output: {
1030
1210
  dryRun: options.dryRun,
1031
1211
  git: { committed: false, tags: [], pushed: false },
@@ -1037,7 +1217,10 @@ async function runPipeline(input, config, options) {
1037
1217
  };
1038
1218
  try {
1039
1219
  await runPrepareStage(ctx);
1040
- if (!options.skipGit) {
1220
+ if (options.skipGitCommit && !options.skipGit) {
1221
+ ctx.output.git.committed = !!input.commitMessage;
1222
+ ctx.output.git.tags = [...input.tags];
1223
+ } else if (!options.skipGit) {
1041
1224
  await runGitCommitStage(ctx);
1042
1225
  }
1043
1226
  if (!options.skipPublish) {
@@ -1066,8 +1249,8 @@ async function runPipeline(input, config, options) {
1066
1249
  }
1067
1250
 
1068
1251
  // src/stages/input.ts
1069
- var fs7 = __toESM(require("fs"), 1);
1070
- var import_core11 = require("@releasekit/core");
1252
+ var fs8 = __toESM(require("fs"), 1);
1253
+ var import_core12 = require("@releasekit/core");
1071
1254
  var import_zod = require("zod");
1072
1255
  var VersionChangelogEntrySchema = import_zod.z.object({
1073
1256
  type: import_zod.z.string(),
@@ -1100,7 +1283,7 @@ async function parseInput(inputPath) {
1100
1283
  let raw;
1101
1284
  if (inputPath) {
1102
1285
  try {
1103
- raw = fs7.readFileSync(inputPath, "utf-8");
1286
+ raw = fs8.readFileSync(inputPath, "utf-8");
1104
1287
  } catch {
1105
1288
  throw createPublishError("INPUT_PARSE_ERROR" /* INPUT_PARSE_ERROR */, `Could not read file: ${inputPath}`);
1106
1289
  }
@@ -1120,7 +1303,7 @@ async function parseInput(inputPath) {
1120
1303
  ${issues}`);
1121
1304
  }
1122
1305
  if (result.data.updates.length === 0) {
1123
- (0, import_core11.info)("No package updates in version output \u2014 pipeline will be a no-op");
1306
+ (0, import_core12.info)("No package updates in version output \u2014 pipeline will be a no-op");
1124
1307
  }
1125
1308
  return result.data;
1126
1309
  }
@@ -1135,8 +1318,8 @@ async function readStdin() {
1135
1318
  // src/cli.ts
1136
1319
  var program = new import_commander.Command();
1137
1320
  program.name("releasekit-publish").description("Publish packages to registries with git tagging and GitHub releases").version("0.1.0").option("--input <path>", "Path to version output JSON (default: stdin)").option("--config <path>", "Path to releasekit config").option("--registry <type>", "Registry to publish to (npm, cargo, all)", "all").option("--npm-auth <method>", "NPM auth method (oidc, token, auto)", "auto").option("--dry-run", "Simulate all operations", false).option("--skip-git", "Skip git commit/tag/push", false).option("--skip-publish", "Skip registry publishing", false).option("--skip-github-release", "Skip GitHub Release creation", false).option("--skip-verification", "Skip post-publish verification", false).option("--json", "Output results as JSON", false).option("--verbose", "Verbose logging", false).action(async (options) => {
1138
- if (options.verbose) (0, import_core12.setLogLevel)("debug");
1139
- if (options.json) (0, import_core12.setJsonMode)(true);
1321
+ if (options.verbose) (0, import_core13.setLogLevel)("debug");
1322
+ if (options.json) (0, import_core13.setJsonMode)(true);
1140
1323
  try {
1141
1324
  const config = loadConfig({ configPath: options.config });
1142
1325
  const input = await parseInput(options.input);
@@ -1173,14 +1356,14 @@ program.name("releasekit-publish").description("Publish packages to registries w
1173
1356
  2
1174
1357
  )
1175
1358
  );
1176
- process.exit(import_core12.EXIT_CODES.PUBLISH_ERROR);
1359
+ process.exit(import_core13.EXIT_CODES.PUBLISH_ERROR);
1177
1360
  }
1178
1361
  if (BasePublishError.isPublishError(err)) {
1179
1362
  err.logError();
1180
- process.exit(import_core12.EXIT_CODES.PUBLISH_ERROR);
1363
+ process.exit(import_core13.EXIT_CODES.PUBLISH_ERROR);
1181
1364
  }
1182
1365
  console.error(err instanceof Error ? err.message : String(err));
1183
- process.exit(import_core12.EXIT_CODES.GENERAL_ERROR);
1366
+ process.exit(import_core13.EXIT_CODES.GENERAL_ERROR);
1184
1367
  }
1185
1368
  });
1186
1369
  program.parse();