@releasekit/publish 0.2.0-next.9 → 0.2.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.
@@ -1,5 +1,456 @@
1
- // src/config.ts
2
- import { loadPublishConfig } from "@releasekit/config";
1
+ // ../core/dist/index.js
2
+ import chalk from "chalk";
3
+ var LOG_LEVELS = {
4
+ error: 0,
5
+ warn: 1,
6
+ info: 2,
7
+ debug: 3,
8
+ trace: 4
9
+ };
10
+ var PREFIXES = {
11
+ error: "[ERROR]",
12
+ warn: "[WARN]",
13
+ info: "[INFO]",
14
+ debug: "[DEBUG]",
15
+ trace: "[TRACE]"
16
+ };
17
+ var COLORS = {
18
+ error: chalk.red,
19
+ warn: chalk.yellow,
20
+ info: chalk.blue,
21
+ debug: chalk.gray,
22
+ trace: chalk.dim
23
+ };
24
+ var currentLevel = "info";
25
+ var quietMode = false;
26
+ function setLogLevel(level) {
27
+ currentLevel = level;
28
+ }
29
+ function setJsonMode(_json) {
30
+ }
31
+ function shouldLog(level) {
32
+ if (quietMode && level !== "error") return false;
33
+ return LOG_LEVELS[level] <= LOG_LEVELS[currentLevel];
34
+ }
35
+ function log(message, level = "info") {
36
+ if (!shouldLog(level)) return;
37
+ const formatted = COLORS[level](`${PREFIXES[level]} ${message}`);
38
+ console.error(formatted);
39
+ }
40
+ function warn(message) {
41
+ log(message, "warn");
42
+ }
43
+ function info(message) {
44
+ log(message, "info");
45
+ }
46
+ function success(message) {
47
+ if (!shouldLog("info")) return;
48
+ console.error(chalk.green(`[SUCCESS] ${message}`));
49
+ }
50
+ function debug(message) {
51
+ log(message, "debug");
52
+ }
53
+ var ReleaseKitError = class _ReleaseKitError extends Error {
54
+ constructor(message) {
55
+ super(message);
56
+ this.name = this.constructor.name;
57
+ }
58
+ logError() {
59
+ log(this.message, "error");
60
+ if (this.suggestions.length > 0) {
61
+ log("\nSuggested solutions:", "info");
62
+ for (const [i, suggestion] of this.suggestions.entries()) {
63
+ log(`${i + 1}. ${suggestion}`, "info");
64
+ }
65
+ }
66
+ }
67
+ static isReleaseKitError(error2) {
68
+ return error2 instanceof _ReleaseKitError;
69
+ }
70
+ };
71
+ var EXIT_CODES = {
72
+ SUCCESS: 0,
73
+ GENERAL_ERROR: 1,
74
+ CONFIG_ERROR: 2,
75
+ INPUT_ERROR: 3,
76
+ TEMPLATE_ERROR: 4,
77
+ LLM_ERROR: 5,
78
+ GITHUB_ERROR: 6,
79
+ GIT_ERROR: 7,
80
+ VERSION_ERROR: 8,
81
+ PUBLISH_ERROR: 9
82
+ };
83
+
84
+ // ../config/dist/index.js
85
+ import * as fs from "fs";
86
+ import * as path from "path";
87
+ import * as TOML from "smol-toml";
88
+ import * as fs3 from "fs";
89
+ import * as path3 from "path";
90
+ import { z as z2 } from "zod";
91
+ import { z } from "zod";
92
+ import * as fs2 from "fs";
93
+ import * as os from "os";
94
+ import * as path2 from "path";
95
+ function parseCargoToml(cargoPath) {
96
+ const content = fs.readFileSync(cargoPath, "utf-8");
97
+ return TOML.parse(content);
98
+ }
99
+ var ConfigError = class extends ReleaseKitError {
100
+ code = "CONFIG_ERROR";
101
+ suggestions;
102
+ constructor(message, suggestions) {
103
+ super(message);
104
+ this.suggestions = suggestions ?? [
105
+ "Check that releasekit.config.json exists and is valid JSON",
106
+ "Run with --verbose for more details"
107
+ ];
108
+ }
109
+ };
110
+ function mergeGitConfig(topLevel, packageLevel) {
111
+ if (!topLevel && !packageLevel) return void 0;
112
+ const base = topLevel ?? {
113
+ remote: "origin",
114
+ branch: "main",
115
+ pushMethod: "auto"
116
+ };
117
+ if (!packageLevel) return base;
118
+ return {
119
+ remote: packageLevel.remote ?? base.remote,
120
+ branch: packageLevel.branch ?? base.branch,
121
+ pushMethod: packageLevel.pushMethod ?? base.pushMethod,
122
+ httpsTokenEnv: packageLevel.httpsTokenEnv ?? base.httpsTokenEnv,
123
+ push: packageLevel.push,
124
+ skipHooks: packageLevel.skipHooks ?? base.skipHooks
125
+ };
126
+ }
127
+ var MAX_JSONC_LENGTH = 1e5;
128
+ function parseJsonc(content) {
129
+ if (content.length > MAX_JSONC_LENGTH) {
130
+ throw new Error(`JSONC content too long: ${content.length} characters (max ${MAX_JSONC_LENGTH})`);
131
+ }
132
+ try {
133
+ return JSON.parse(content);
134
+ } catch {
135
+ const cleaned = content.replace(/\/\/[^\r\n]{0,10000}$/gm, "").replace(/\/\*[\s\S]{0,50000}?\*\//g, "").trim();
136
+ return JSON.parse(cleaned);
137
+ }
138
+ }
139
+ var GitConfigSchema = z.object({
140
+ remote: z.string().default("origin"),
141
+ branch: z.string().default("main"),
142
+ pushMethod: z.enum(["auto", "ssh", "https"]).default("auto"),
143
+ /**
144
+ * Optional env var name containing a GitHub token for HTTPS pushes.
145
+ * When set, publish steps can use this token without mutating git remotes.
146
+ */
147
+ httpsTokenEnv: z.string().optional(),
148
+ push: z.boolean().optional(),
149
+ skipHooks: z.boolean().optional()
150
+ });
151
+ var MonorepoConfigSchema = z.object({
152
+ mode: z.enum(["root", "packages", "both"]).optional(),
153
+ rootPath: z.string().optional(),
154
+ packagesPath: z.string().optional(),
155
+ mainPackage: z.string().optional()
156
+ });
157
+ var BranchPatternSchema = z.object({
158
+ pattern: z.string(),
159
+ releaseType: z.enum(["major", "minor", "patch", "prerelease"])
160
+ });
161
+ var VersionCargoConfigSchema = z.object({
162
+ enabled: z.boolean().default(true),
163
+ paths: z.array(z.string()).optional()
164
+ });
165
+ var VersionConfigSchema = z.object({
166
+ tagTemplate: z.string().default("v{version}"),
167
+ packageSpecificTags: z.boolean().default(false),
168
+ preset: z.string().default("conventional"),
169
+ sync: z.boolean().default(true),
170
+ packages: z.array(z.string()).default([]),
171
+ mainPackage: z.string().optional(),
172
+ updateInternalDependencies: z.enum(["major", "minor", "patch", "no-internal-update"]).default("minor"),
173
+ skip: z.array(z.string()).optional(),
174
+ commitMessage: z.string().optional(),
175
+ versionStrategy: z.enum(["branchPattern", "commitMessage"]).default("commitMessage"),
176
+ branchPatterns: z.array(BranchPatternSchema).optional(),
177
+ defaultReleaseType: z.enum(["major", "minor", "patch", "prerelease"]).optional(),
178
+ mismatchStrategy: z.enum(["error", "warn", "ignore", "prefer-package", "prefer-git"]).default("warn"),
179
+ versionPrefix: z.string().default(""),
180
+ prereleaseIdentifier: z.string().optional(),
181
+ strictReachable: z.boolean().default(false),
182
+ cargo: VersionCargoConfigSchema.optional()
183
+ });
184
+ var NpmConfigSchema = z.object({
185
+ enabled: z.boolean().default(true),
186
+ auth: z.enum(["auto", "oidc", "token"]).default("auto"),
187
+ provenance: z.boolean().default(true),
188
+ access: z.enum(["public", "restricted"]).default("public"),
189
+ registry: z.string().default("https://registry.npmjs.org"),
190
+ copyFiles: z.array(z.string()).default(["LICENSE"]),
191
+ tag: z.string().default("latest")
192
+ });
193
+ var CargoPublishConfigSchema = z.object({
194
+ enabled: z.boolean().default(false),
195
+ noVerify: z.boolean().default(false),
196
+ publishOrder: z.array(z.string()).default([]),
197
+ clean: z.boolean().default(false)
198
+ });
199
+ var PublishGitConfigSchema = z.object({
200
+ push: z.boolean().default(true),
201
+ pushMethod: z.enum(["auto", "ssh", "https"]).optional(),
202
+ remote: z.string().optional(),
203
+ branch: z.string().optional(),
204
+ httpsTokenEnv: z.string().optional(),
205
+ skipHooks: z.boolean().optional()
206
+ });
207
+ var GitHubReleaseConfigSchema = z.object({
208
+ enabled: z.boolean().default(true),
209
+ draft: z.boolean().default(true),
210
+ perPackage: z.boolean().default(true),
211
+ prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
212
+ /**
213
+ * Controls how release notes are sourced for GitHub releases.
214
+ * - 'auto': Use RELEASE_NOTES.md if it exists, then per-package changelog
215
+ * data from the version output, then GitHub's auto-generated notes.
216
+ * - 'github': Always use GitHub's auto-generated notes.
217
+ * - 'none': No notes body.
218
+ * - Any other string: Treated as a file path to read notes from.
219
+ */
220
+ releaseNotes: z.union([z.literal("auto"), z.literal("github"), z.literal("none"), z.string()]).default("auto")
221
+ });
222
+ var VerifyRegistryConfigSchema = z.object({
223
+ enabled: z.boolean().default(true),
224
+ maxAttempts: z.number().int().positive().default(5),
225
+ initialDelay: z.number().int().positive().default(15e3),
226
+ backoffMultiplier: z.number().positive().default(2)
227
+ });
228
+ var VerifyConfigSchema = z.object({
229
+ npm: VerifyRegistryConfigSchema.default({
230
+ enabled: true,
231
+ maxAttempts: 5,
232
+ initialDelay: 15e3,
233
+ backoffMultiplier: 2
234
+ }),
235
+ cargo: VerifyRegistryConfigSchema.default({
236
+ enabled: true,
237
+ maxAttempts: 10,
238
+ initialDelay: 3e4,
239
+ backoffMultiplier: 2
240
+ })
241
+ });
242
+ var PublishConfigSchema = z.object({
243
+ git: PublishGitConfigSchema.optional(),
244
+ npm: NpmConfigSchema.default({
245
+ enabled: true,
246
+ auth: "auto",
247
+ provenance: true,
248
+ access: "public",
249
+ registry: "https://registry.npmjs.org",
250
+ copyFiles: ["LICENSE"],
251
+ tag: "latest"
252
+ }),
253
+ cargo: CargoPublishConfigSchema.default({
254
+ enabled: false,
255
+ noVerify: false,
256
+ publishOrder: [],
257
+ clean: false
258
+ }),
259
+ githubRelease: GitHubReleaseConfigSchema.default({
260
+ enabled: true,
261
+ draft: true,
262
+ perPackage: true,
263
+ prerelease: "auto",
264
+ releaseNotes: "auto"
265
+ }),
266
+ verify: VerifyConfigSchema.default({
267
+ npm: {
268
+ enabled: true,
269
+ maxAttempts: 5,
270
+ initialDelay: 15e3,
271
+ backoffMultiplier: 2
272
+ },
273
+ cargo: {
274
+ enabled: true,
275
+ maxAttempts: 10,
276
+ initialDelay: 3e4,
277
+ backoffMultiplier: 2
278
+ }
279
+ })
280
+ });
281
+ var TemplateConfigSchema = z.object({
282
+ path: z.string().optional(),
283
+ engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
284
+ });
285
+ var OutputConfigSchema = z.object({
286
+ format: z.enum(["markdown", "github-release", "json"]),
287
+ file: z.string().optional(),
288
+ options: z.record(z.string(), z.unknown()).optional(),
289
+ templates: TemplateConfigSchema.optional()
290
+ });
291
+ var LLMOptionsSchema = z.object({
292
+ timeout: z.number().optional(),
293
+ maxTokens: z.number().optional(),
294
+ temperature: z.number().optional()
295
+ });
296
+ var LLMRetryConfigSchema = z.object({
297
+ maxAttempts: z.number().int().positive().optional(),
298
+ initialDelay: z.number().nonnegative().optional(),
299
+ maxDelay: z.number().positive().optional(),
300
+ backoffFactor: z.number().positive().optional()
301
+ });
302
+ var LLMTasksConfigSchema = z.object({
303
+ summarize: z.boolean().optional(),
304
+ enhance: z.boolean().optional(),
305
+ categorize: z.boolean().optional(),
306
+ releaseNotes: z.boolean().optional()
307
+ });
308
+ var LLMCategorySchema = z.object({
309
+ name: z.string(),
310
+ description: z.string(),
311
+ scopes: z.array(z.string()).optional()
312
+ });
313
+ var ScopeRulesSchema = z.object({
314
+ allowed: z.array(z.string()).optional(),
315
+ caseSensitive: z.boolean().default(false),
316
+ invalidScopeAction: z.enum(["remove", "keep", "fallback"]).default("remove"),
317
+ fallbackScope: z.string().optional()
318
+ });
319
+ var ScopeConfigSchema = z.object({
320
+ mode: z.enum(["restricted", "packages", "none", "unrestricted"]).default("unrestricted"),
321
+ rules: ScopeRulesSchema.optional()
322
+ });
323
+ var LLMPromptOverridesSchema = z.object({
324
+ enhance: z.string().optional(),
325
+ categorize: z.string().optional(),
326
+ enhanceAndCategorize: z.string().optional(),
327
+ summarize: z.string().optional(),
328
+ releaseNotes: z.string().optional()
329
+ });
330
+ var LLMPromptsConfigSchema = z.object({
331
+ instructions: LLMPromptOverridesSchema.optional(),
332
+ templates: LLMPromptOverridesSchema.optional()
333
+ });
334
+ var LLMConfigSchema = z.object({
335
+ provider: z.string(),
336
+ model: z.string(),
337
+ baseURL: z.string().optional(),
338
+ apiKey: z.string().optional(),
339
+ options: LLMOptionsSchema.optional(),
340
+ concurrency: z.number().int().positive().optional(),
341
+ retry: LLMRetryConfigSchema.optional(),
342
+ tasks: LLMTasksConfigSchema.optional(),
343
+ categories: z.array(LLMCategorySchema).optional(),
344
+ style: z.string().optional(),
345
+ scopes: ScopeConfigSchema.optional(),
346
+ prompts: LLMPromptsConfigSchema.optional()
347
+ });
348
+ var NotesInputConfigSchema = z.object({
349
+ source: z.string().optional(),
350
+ file: z.string().optional()
351
+ });
352
+ var NotesConfigSchema = z.object({
353
+ input: NotesInputConfigSchema.optional(),
354
+ output: z.array(OutputConfigSchema).default([{ format: "markdown", file: "CHANGELOG.md" }]),
355
+ monorepo: MonorepoConfigSchema.optional(),
356
+ templates: TemplateConfigSchema.optional(),
357
+ llm: LLMConfigSchema.optional(),
358
+ updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
359
+ });
360
+ var ReleaseKitConfigSchema = z.object({
361
+ git: GitConfigSchema.optional(),
362
+ monorepo: MonorepoConfigSchema.optional(),
363
+ version: VersionConfigSchema.optional(),
364
+ publish: PublishConfigSchema.optional(),
365
+ notes: NotesConfigSchema.optional()
366
+ });
367
+ var MAX_INPUT_LENGTH = 1e4;
368
+ function substituteVariables(value) {
369
+ if (value.length > MAX_INPUT_LENGTH) {
370
+ throw new Error(`Input too long: ${value.length} characters (max ${MAX_INPUT_LENGTH})`);
371
+ }
372
+ const envPattern = /\{env:([^}]{1,1000})\}/g;
373
+ const filePattern = /\{file:([^}]{1,1000})\}/g;
374
+ let result = value;
375
+ result = result.replace(envPattern, (_, varName) => {
376
+ return process.env[varName] ?? "";
377
+ });
378
+ result = result.replace(filePattern, (_, filePath) => {
379
+ const expandedPath = filePath.startsWith("~") ? path2.join(os.homedir(), filePath.slice(1)) : filePath;
380
+ try {
381
+ return fs2.readFileSync(expandedPath, "utf-8").trim();
382
+ } catch {
383
+ return "";
384
+ }
385
+ });
386
+ return result;
387
+ }
388
+ var SOLE_REFERENCE_PATTERN = /^\{(?:env|file):[^}]+\}$/;
389
+ function substituteInObject(obj) {
390
+ if (typeof obj === "string") {
391
+ const result = substituteVariables(obj);
392
+ if (result === "" && SOLE_REFERENCE_PATTERN.test(obj)) {
393
+ return void 0;
394
+ }
395
+ return result;
396
+ }
397
+ if (Array.isArray(obj)) {
398
+ return obj.map((item) => substituteInObject(item));
399
+ }
400
+ if (obj && typeof obj === "object") {
401
+ const result = {};
402
+ for (const [key, value] of Object.entries(obj)) {
403
+ result[key] = substituteInObject(value);
404
+ }
405
+ return result;
406
+ }
407
+ return obj;
408
+ }
409
+ var AUTH_DIR = path2.join(os.homedir(), ".config", "releasekit");
410
+ var AUTH_FILE = path2.join(AUTH_DIR, "auth.json");
411
+ var CONFIG_FILE = "releasekit.config.json";
412
+ function loadConfigFile(configPath) {
413
+ if (!fs3.existsSync(configPath)) {
414
+ return {};
415
+ }
416
+ try {
417
+ const content = fs3.readFileSync(configPath, "utf-8");
418
+ const parsed = parseJsonc(content);
419
+ const substituted = substituteInObject(parsed);
420
+ return ReleaseKitConfigSchema.parse(substituted);
421
+ } catch (error) {
422
+ if (error instanceof z2.ZodError) {
423
+ const issues = error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
424
+ throw new ConfigError(`Config validation errors:
425
+ ${issues}`);
426
+ }
427
+ if (error instanceof SyntaxError) {
428
+ throw new ConfigError(`Invalid JSON in config file: ${error.message}`);
429
+ }
430
+ throw error;
431
+ }
432
+ }
433
+ function loadConfig(options) {
434
+ const cwd = options?.cwd ?? process.cwd();
435
+ const configPath = options?.configPath ?? path3.join(cwd, CONFIG_FILE);
436
+ return loadConfigFile(configPath);
437
+ }
438
+ function loadPublishConfig(options) {
439
+ const config = loadConfig(options);
440
+ if (!config.publish) return void 0;
441
+ const mergedGit = mergeGitConfig(config.git, config.publish.git);
442
+ return {
443
+ ...config.publish,
444
+ git: mergedGit ? {
445
+ push: mergedGit.push ?? true,
446
+ pushMethod: mergedGit.pushMethod,
447
+ remote: mergedGit.remote,
448
+ branch: mergedGit.branch,
449
+ httpsTokenEnv: mergedGit.httpsTokenEnv,
450
+ skipHooks: mergedGit.skipHooks
451
+ } : void 0
452
+ };
453
+ }
3
454
 
4
455
  // src/types.ts
5
456
  function getDefaultConfig() {
@@ -23,14 +474,16 @@ function getDefaultConfig() {
23
474
  push: true,
24
475
  pushMethod: "auto",
25
476
  remote: "origin",
26
- branch: "main"
477
+ branch: "main",
478
+ httpsTokenEnv: void 0,
479
+ skipHooks: false
27
480
  },
28
481
  githubRelease: {
29
482
  enabled: true,
30
483
  draft: true,
31
- generateNotes: true,
32
- perPackage: false,
33
- prerelease: "auto"
484
+ perPackage: true,
485
+ prerelease: "auto",
486
+ releaseNotes: "auto"
34
487
  },
35
488
  verify: {
36
489
  npm: {
@@ -71,15 +524,16 @@ function toPublishConfig(config) {
71
524
  push: config.git.push ?? defaults.git.push,
72
525
  pushMethod: config.git.pushMethod ?? defaults.git.pushMethod,
73
526
  remote: config.git.remote ?? defaults.git.remote,
74
- branch: config.git.branch ?? defaults.git.branch
527
+ branch: config.git.branch ?? defaults.git.branch,
528
+ httpsTokenEnv: config.git.httpsTokenEnv ?? defaults.git.httpsTokenEnv,
529
+ skipHooks: config.git.skipHooks ?? defaults.git.skipHooks
75
530
  } : defaults.git,
76
531
  githubRelease: {
77
532
  enabled: config.githubRelease?.enabled ?? defaults.githubRelease.enabled,
78
533
  draft: config.githubRelease?.draft ?? defaults.githubRelease.draft,
79
- generateNotes: config.githubRelease?.generateNotes ?? defaults.githubRelease.generateNotes,
80
534
  perPackage: config.githubRelease?.perPackage ?? defaults.githubRelease.perPackage,
81
535
  prerelease: config.githubRelease?.prerelease ?? defaults.githubRelease.prerelease,
82
- notesFile: config.githubRelease?.notesFile
536
+ releaseNotes: config.githubRelease?.releaseNotes ?? defaults.githubRelease.releaseNotes
83
537
  },
84
538
  verify: {
85
539
  npm: {
@@ -99,7 +553,7 @@ function toPublishConfig(config) {
99
553
  }
100
554
 
101
555
  // src/config.ts
102
- function loadConfig(options) {
556
+ function loadConfig2(options) {
103
557
  const baseConfig = loadPublishConfig(options);
104
558
  return toPublishConfig(baseConfig);
105
559
  }
@@ -108,7 +562,6 @@ function getDefaultConfig2() {
108
562
  }
109
563
 
110
564
  // src/errors/index.ts
111
- import { ReleaseKitError } from "@releasekit/core";
112
565
  var BasePublishError = class _BasePublishError extends ReleaseKitError {
113
566
  code;
114
567
  suggestions;
@@ -247,9 +700,20 @@ function createPublishError(code, details) {
247
700
 
248
701
  // src/utils/exec.ts
249
702
  import { execFile } from "child_process";
250
- import { debug, info } from "@releasekit/core";
703
+ function redactArg(arg) {
704
+ try {
705
+ const url = new URL(arg);
706
+ if (url.username || url.password) {
707
+ url.username = url.username ? "***" : "";
708
+ url.password = url.password ? "***" : "";
709
+ return url.toString();
710
+ }
711
+ } catch {
712
+ }
713
+ return arg;
714
+ }
251
715
  async function execCommand(file, args, options = {}) {
252
- const displayCommand = options.label ?? [file, ...args].join(" ");
716
+ const displayCommand = options.label ?? [file, ...args.map(redactArg)].join(" ");
253
717
  if (options.dryRun) {
254
718
  info(`[DRY RUN] Would execute: ${displayCommand}`);
255
719
  return { stdout: "", stderr: "", exitCode: 0 };
@@ -305,7 +769,7 @@ function detectNpmAuth() {
305
769
  if (process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
306
770
  return "oidc";
307
771
  }
308
- if (process.env.NPM_TOKEN) {
772
+ if (process.env.NPM_TOKEN || process.env.NODE_AUTH_TOKEN) {
309
773
  return "token";
310
774
  }
311
775
  return null;
@@ -323,15 +787,14 @@ async function detectGitPushMethod(remote, cwd) {
323
787
  }
324
788
 
325
789
  // src/utils/cargo.ts
326
- import * as fs from "fs";
327
- import { parseCargoToml } from "@releasekit/config";
328
- import * as TOML from "smol-toml";
790
+ import * as fs4 from "fs";
791
+ import * as TOML2 from "smol-toml";
329
792
  function updateCargoVersion(cargoPath, newVersion) {
330
793
  try {
331
794
  const cargo = parseCargoToml(cargoPath);
332
795
  if (cargo.package) {
333
796
  cargo.package.version = newVersion;
334
- fs.writeFileSync(cargoPath, TOML.stringify(cargo));
797
+ fs4.writeFileSync(cargoPath, TOML2.stringify(cargo));
335
798
  }
336
799
  } catch (error) {
337
800
  throw createPublishError(
@@ -368,11 +831,11 @@ function getDistTag(version, defaultTag = "latest") {
368
831
  }
369
832
 
370
833
  // src/utils/package-manager.ts
371
- import * as fs2 from "fs";
372
- import * as path from "path";
834
+ import * as fs5 from "fs";
835
+ import * as path4 from "path";
373
836
  function detectPackageManager(cwd) {
374
- if (fs2.existsSync(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
375
- if (fs2.existsSync(path.join(cwd, "yarn.lock"))) return "yarn";
837
+ if (fs5.existsSync(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
838
+ if (fs5.existsSync(path4.join(cwd, "yarn.lock"))) return "yarn";
376
839
  return "npm";
377
840
  }
378
841
  function buildPublishCommand(pm, packageName, _packageDir, options) {
@@ -397,25 +860,24 @@ function buildViewCommand(pm, packageName, version) {
397
860
  }
398
861
 
399
862
  // src/stages/cargo-publish.ts
400
- import * as fs3 from "fs";
401
- import * as path2 from "path";
402
- import { debug as debug2, success, warn } from "@releasekit/core";
863
+ import * as fs6 from "fs";
864
+ import * as path5 from "path";
403
865
  async function runCargoPublishStage(ctx) {
404
866
  const { input, config, cliOptions, cwd } = ctx;
405
867
  const dryRun = cliOptions.dryRun;
406
868
  if (!config.cargo.enabled) {
407
- debug2("Cargo publishing disabled in config");
869
+ debug("Cargo publishing disabled in config");
408
870
  return;
409
871
  }
410
872
  if (!hasCargoAuth() && !dryRun) {
411
873
  throw createPublishError("CARGO_AUTH_ERROR" /* CARGO_AUTH_ERROR */, "CARGO_REGISTRY_TOKEN not set");
412
874
  }
413
875
  const crates = findCrates(
414
- input.updates.map((u) => ({ dir: path2.dirname(path2.resolve(cwd, u.filePath)), ...u })),
876
+ input.updates.map((u) => ({ dir: path5.dirname(path5.resolve(cwd, u.filePath)), ...u })),
415
877
  cwd
416
878
  );
417
879
  if (crates.length === 0) {
418
- debug2("No Cargo crates found to publish");
880
+ debug("No Cargo crates found to publish");
419
881
  return;
420
882
  }
421
883
  const ordered = orderCrates(crates, config.cargo.publishOrder);
@@ -464,8 +926,8 @@ async function runCargoPublishStage(ctx) {
464
926
  function findCrates(updates, _cwd) {
465
927
  const crates = [];
466
928
  for (const update of updates) {
467
- const cargoPath = path2.join(update.dir, "Cargo.toml");
468
- if (!fs3.existsSync(cargoPath)) {
929
+ const cargoPath = path5.join(update.dir, "Cargo.toml");
930
+ if (!fs6.existsSync(cargoPath)) {
469
931
  continue;
470
932
  }
471
933
  try {
@@ -513,9 +975,9 @@ function topologicalSort(crates) {
513
975
  }
514
976
  for (const crate of crates) {
515
977
  for (const depPath of crate.pathDeps) {
516
- const resolvedDir = path2.resolve(crate.dir, depPath);
978
+ const resolvedDir = path5.resolve(crate.dir, depPath);
517
979
  for (const other of crates) {
518
- if (path2.resolve(other.dir) === resolvedDir && nameSet.has(other.name)) {
980
+ if (path5.resolve(other.dir) === resolvedDir && nameSet.has(other.name)) {
519
981
  graph.get(crate.name)?.push(other.name);
520
982
  }
521
983
  }
@@ -557,18 +1019,21 @@ function topologicalSort(crates) {
557
1019
  }
558
1020
 
559
1021
  // src/stages/git-commit.ts
560
- import * as path3 from "path";
561
- import { info as info2, success as success2 } from "@releasekit/core";
1022
+ import * as path6 from "path";
562
1023
  async function runGitCommitStage(ctx) {
563
- const { input, cliOptions, cwd } = ctx;
1024
+ const { input, config, cliOptions, cwd } = ctx;
564
1025
  const dryRun = cliOptions.dryRun;
1026
+ const skipHooks = config.git.skipHooks ?? false;
565
1027
  if (!input.commitMessage) {
566
- info2("No commit message provided, skipping git commit");
1028
+ info("No commit message provided, skipping git commit");
567
1029
  return;
568
1030
  }
569
- const filePaths = input.updates.map((u) => path3.resolve(cwd, u.filePath));
1031
+ const filePaths = input.updates.map((u) => path6.resolve(cwd, u.filePath));
1032
+ if (ctx.additionalFiles) {
1033
+ filePaths.push(...ctx.additionalFiles.map((f) => path6.resolve(cwd, f)));
1034
+ }
570
1035
  if (filePaths.length === 0) {
571
- info2("No files to commit");
1036
+ info("No files to commit");
572
1037
  return;
573
1038
  }
574
1039
  try {
@@ -583,15 +1048,20 @@ async function runGitCommitStage(ctx) {
583
1048
  `git add failed: ${error instanceof Error ? error.message : String(error)}`
584
1049
  );
585
1050
  }
1051
+ const commitArgs = ["commit"];
1052
+ if (skipHooks) {
1053
+ commitArgs.push("--no-verify");
1054
+ }
1055
+ commitArgs.push("-m", input.commitMessage);
586
1056
  try {
587
- await execCommand("git", ["commit", "-m", input.commitMessage], {
1057
+ await execCommand("git", commitArgs, {
588
1058
  cwd,
589
1059
  dryRun,
590
1060
  label: `git commit -m "${input.commitMessage}"`
591
1061
  });
592
1062
  ctx.output.git.committed = true;
593
1063
  if (!dryRun) {
594
- success2("Created git commit");
1064
+ success("Created git commit");
595
1065
  }
596
1066
  } catch (error) {
597
1067
  throw createPublishError(
@@ -609,7 +1079,7 @@ async function runGitCommitStage(ctx) {
609
1079
  });
610
1080
  ctx.output.git.tags.push(tag);
611
1081
  if (!dryRun) {
612
- success2(`Created tag: ${tag}`);
1082
+ success(`Created tag: ${tag}`);
613
1083
  }
614
1084
  } catch (error) {
615
1085
  throw createPublishError(
@@ -621,16 +1091,27 @@ async function runGitCommitStage(ctx) {
621
1091
  }
622
1092
 
623
1093
  // src/stages/git-push.ts
624
- import { info as info3, success as success3 } from "@releasekit/core";
1094
+ function toGithubAuthedUrl(remoteUrl, token) {
1095
+ try {
1096
+ const url = new URL(remoteUrl);
1097
+ if (url.protocol !== "https:") return void 0;
1098
+ if (url.host !== "github.com") return void 0;
1099
+ url.username = "x-access-token";
1100
+ url.password = token;
1101
+ return url.toString();
1102
+ } catch {
1103
+ return void 0;
1104
+ }
1105
+ }
625
1106
  async function runGitPushStage(ctx) {
626
1107
  const { config, cliOptions, cwd, output } = ctx;
627
1108
  const dryRun = cliOptions.dryRun;
628
1109
  if (!config.git.push) {
629
- info3("Git push disabled in config, skipping");
1110
+ info("Git push disabled in config, skipping");
630
1111
  return;
631
1112
  }
632
1113
  if (!output.git.committed && output.git.tags.length === 0) {
633
- info3("Nothing to push (no commits or tags created)");
1114
+ info("Nothing to push (no commits or tags created)");
634
1115
  return;
635
1116
  }
636
1117
  const { remote, branch } = config.git;
@@ -642,16 +1123,26 @@ async function runGitPushStage(ctx) {
642
1123
  pushMethod = "https";
643
1124
  }
644
1125
  }
1126
+ const httpsTokenEnv = config.git.httpsTokenEnv;
1127
+ const httpsToken = httpsTokenEnv ? process.env[httpsTokenEnv] : void 0;
645
1128
  try {
1129
+ let pushRemote = remote;
1130
+ if (pushMethod === "https" && httpsToken) {
1131
+ const remoteUrlResult = await execCommand("git", ["remote", "get-url", remote], { cwd, dryRun: false });
1132
+ const authed = toGithubAuthedUrl(remoteUrlResult.stdout.trim(), httpsToken);
1133
+ if (authed) {
1134
+ pushRemote = authed;
1135
+ }
1136
+ }
646
1137
  if (output.git.committed) {
647
- await execCommand("git", ["push", remote, branch], {
1138
+ await execCommand("git", ["push", pushRemote, branch], {
648
1139
  cwd,
649
1140
  dryRun,
650
1141
  label: `git push ${remote} ${branch}`
651
1142
  });
652
1143
  }
653
1144
  if (output.git.tags.length > 0) {
654
- await execCommand("git", ["push", remote, "--tags"], {
1145
+ await execCommand("git", ["push", pushRemote, "--tags"], {
655
1146
  cwd,
656
1147
  dryRun,
657
1148
  label: `git push ${remote} --tags`
@@ -659,7 +1150,7 @@ async function runGitPushStage(ctx) {
659
1150
  }
660
1151
  ctx.output.git.pushed = true;
661
1152
  if (!dryRun) {
662
- success3(`Pushed to ${remote}/${branch}`);
1153
+ success(`Pushed to ${remote}/${branch}`);
663
1154
  }
664
1155
  } catch (error) {
665
1156
  throw createPublishError(
@@ -670,33 +1161,82 @@ async function runGitPushStage(ctx) {
670
1161
  }
671
1162
 
672
1163
  // src/stages/github-release.ts
673
- import * as fs4 from "fs";
674
- import { debug as debug3, info as info4, success as success4, warn as warn2 } from "@releasekit/core";
1164
+ import * as fs7 from "fs";
1165
+ function resolveNotes(notesSetting, tag, changelogs, pipelineNotes) {
1166
+ if (notesSetting === "none") {
1167
+ return { useGithubNotes: false };
1168
+ }
1169
+ if (notesSetting === "github") {
1170
+ return { useGithubNotes: true };
1171
+ }
1172
+ if (notesSetting !== "auto") {
1173
+ const body = readFileIfExists(notesSetting);
1174
+ if (body) return { body, useGithubNotes: false };
1175
+ debug(`Notes file not found: ${notesSetting}, falling back to GitHub auto-notes`);
1176
+ return { useGithubNotes: true };
1177
+ }
1178
+ if (pipelineNotes) {
1179
+ const body = findNotesForTag(tag, pipelineNotes);
1180
+ if (body) return { body, useGithubNotes: false };
1181
+ }
1182
+ const packageBody = formatChangelogForTag(tag, changelogs);
1183
+ if (packageBody) {
1184
+ return { body: packageBody, useGithubNotes: false };
1185
+ }
1186
+ return { useGithubNotes: true };
1187
+ }
1188
+ function isVersionOnlyTag(tag) {
1189
+ return /^v?\d+\.\d+\.\d+/.test(tag);
1190
+ }
1191
+ function findNotesForTag(tag, notes) {
1192
+ for (const [packageName, body] of Object.entries(notes)) {
1193
+ if (tag.startsWith(`${packageName}@`) && body.trim()) {
1194
+ return body;
1195
+ }
1196
+ }
1197
+ const entries = Object.values(notes).filter((b) => b.trim());
1198
+ if (entries.length === 1 && isVersionOnlyTag(tag)) return entries[0];
1199
+ return void 0;
1200
+ }
1201
+ function readFileIfExists(filePath) {
1202
+ try {
1203
+ const content = fs7.readFileSync(filePath, "utf-8").trim();
1204
+ return content || void 0;
1205
+ } catch {
1206
+ return void 0;
1207
+ }
1208
+ }
1209
+ function formatChangelogForTag(tag, changelogs) {
1210
+ if (changelogs.length === 0) return void 0;
1211
+ const changelog = changelogs.find((c) => tag.startsWith(`${c.packageName}@`));
1212
+ const target = changelog ?? (changelogs.length === 1 && isVersionOnlyTag(tag) ? changelogs[0] : void 0);
1213
+ if (!target || target.entries.length === 0) return void 0;
1214
+ const lines = [];
1215
+ for (const entry of target.entries) {
1216
+ const scope = entry.scope ? `**${entry.scope}:** ` : "";
1217
+ lines.push(`- ${scope}${entry.description}`);
1218
+ }
1219
+ return lines.join("\n");
1220
+ }
675
1221
  async function runGithubReleaseStage(ctx) {
676
1222
  const { config, cliOptions, output } = ctx;
677
1223
  const dryRun = cliOptions.dryRun;
678
1224
  if (!config.githubRelease.enabled) {
679
- debug3("GitHub releases disabled in config");
1225
+ debug("GitHub releases disabled in config");
680
1226
  return;
681
1227
  }
682
1228
  const tags = output.git.tags.length > 0 ? output.git.tags : ctx.input.tags;
683
1229
  if (tags.length === 0) {
684
- info4("No tags available for GitHub release");
1230
+ info("No tags available for GitHub release");
685
1231
  return;
686
1232
  }
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
1233
  const firstTag = tags[0];
696
1234
  if (!firstTag) return;
697
1235
  const tagsToRelease = config.githubRelease.perPackage ? tags : [firstTag];
698
1236
  for (const tag of tagsToRelease) {
699
- const versionMatch = tag.match(/(\d+\.\d+\.\d+.*)$/);
1237
+ const MAX_TAG_LENGTH = 1e3;
1238
+ const truncatedTag = tag.length > MAX_TAG_LENGTH ? tag.slice(0, MAX_TAG_LENGTH) : tag;
1239
+ const versionMatch = truncatedTag.match(/(\d{1,20}\.\d{1,20}\.\d{1,20}(?:[-+.]?[a-zA-Z0-9.-]{0,100})?)$/);
700
1240
  const version = versionMatch?.[1] ?? "";
701
1241
  const isPreRel = config.githubRelease.prerelease === "auto" ? version ? isPrerelease(version) : false : config.githubRelease.prerelease;
702
1242
  const result = {
@@ -712,9 +1252,15 @@ async function runGithubReleaseStage(ctx) {
712
1252
  if (isPreRel) {
713
1253
  ghArgs.push("--prerelease");
714
1254
  }
715
- if (notesBody) {
716
- ghArgs.push("--notes", notesBody);
717
- } else if (config.githubRelease.generateNotes) {
1255
+ const { body, useGithubNotes } = resolveNotes(
1256
+ config.githubRelease.releaseNotes,
1257
+ tag,
1258
+ ctx.input.changelogs,
1259
+ ctx.releaseNotes
1260
+ );
1261
+ if (body) {
1262
+ ghArgs.push("--notes", body);
1263
+ } else if (useGithubNotes) {
718
1264
  ghArgs.push("--generate-notes");
719
1265
  }
720
1266
  try {
@@ -727,25 +1273,91 @@ async function runGithubReleaseStage(ctx) {
727
1273
  result.url = execResult.stdout.trim();
728
1274
  }
729
1275
  if (!dryRun) {
730
- success4(`Created GitHub release for ${tag}`);
1276
+ success(`Created GitHub release for ${tag}`);
731
1277
  }
732
1278
  } catch (error) {
733
1279
  result.reason = error instanceof Error ? error.message : String(error);
734
- warn2(`Failed to create GitHub release for ${tag}: ${result.reason}`);
1280
+ warn(`Failed to create GitHub release for ${tag}: ${result.reason}`);
735
1281
  }
736
1282
  ctx.output.githubReleases.push(result);
737
1283
  }
738
1284
  }
739
1285
 
740
1286
  // src/stages/npm-publish.ts
741
- import * as fs5 from "fs";
742
- import * as path4 from "path";
743
- import { debug as debug4, info as info5, success as success5, warn as warn3 } from "@releasekit/core";
1287
+ import * as fs9 from "fs";
1288
+ import * as path8 from "path";
1289
+
1290
+ // src/utils/npm-env.ts
1291
+ import * as fs8 from "fs";
1292
+ import * as os2 from "os";
1293
+ import * as path7 from "path";
1294
+ function writeTempNpmrc(contents) {
1295
+ const dir = fs8.mkdtempSync(path7.join(os2.tmpdir(), "releasekit-npmrc-"));
1296
+ const npmrcPath = path7.join(dir, ".npmrc");
1297
+ fs8.writeFileSync(npmrcPath, contents, "utf-8");
1298
+ return {
1299
+ npmrcPath,
1300
+ cleanup: () => {
1301
+ try {
1302
+ fs8.rmSync(dir, { recursive: true, force: true });
1303
+ } catch {
1304
+ }
1305
+ }
1306
+ };
1307
+ }
1308
+ function createNpmSubprocessIsolation(options) {
1309
+ const { authMethod, registryUrl } = options;
1310
+ const baseEnv = {};
1311
+ if (!authMethod) return { env: baseEnv, cleanup: () => {
1312
+ } };
1313
+ const token = process.env.NPM_TOKEN ?? process.env.NODE_AUTH_TOKEN;
1314
+ const registryHost = (() => {
1315
+ try {
1316
+ return new URL(registryUrl).host;
1317
+ } catch {
1318
+ return "registry.npmjs.org";
1319
+ }
1320
+ })();
1321
+ const lines = [`registry=${registryUrl}`];
1322
+ if (authMethod === "oidc") {
1323
+ lines.push("always-auth=false");
1324
+ }
1325
+ if (authMethod === "token" && token) {
1326
+ lines.push(`//${registryHost}/:_authToken=${token}`);
1327
+ }
1328
+ lines.push("");
1329
+ const { npmrcPath, cleanup } = writeTempNpmrc(lines.join("\n"));
1330
+ debug(`Using isolated npm userconfig: ${npmrcPath}`);
1331
+ const isOidc = authMethod === "oidc";
1332
+ return {
1333
+ env: {
1334
+ ...baseEnv,
1335
+ // Ensure npm and tools that read npm_config_* pick up our temp file
1336
+ NPM_CONFIG_USERCONFIG: npmrcPath,
1337
+ npm_config_userconfig: npmrcPath,
1338
+ // Auth-specific hardening
1339
+ ...isOidc ? {
1340
+ // Prevent setup-node's always-auth from forcing token lookups
1341
+ NPM_CONFIG_ALWAYS_AUTH: "false",
1342
+ npm_config_always_auth: "false",
1343
+ // Explicitly prevent token-based publishing from being selected implicitly
1344
+ NODE_AUTH_TOKEN: void 0,
1345
+ NPM_TOKEN: void 0
1346
+ } : {
1347
+ // Ensure CLIs that expect NODE_AUTH_TOKEN can still work
1348
+ NODE_AUTH_TOKEN: token
1349
+ }
1350
+ },
1351
+ cleanup
1352
+ };
1353
+ }
1354
+
1355
+ // src/stages/npm-publish.ts
744
1356
  async function runNpmPublishStage(ctx) {
745
1357
  const { input, config, cliOptions, cwd } = ctx;
746
1358
  const dryRun = cliOptions.dryRun;
747
1359
  if (!config.npm.enabled) {
748
- info5("NPM publishing disabled in config");
1360
+ info("NPM publishing disabled in config");
749
1361
  return;
750
1362
  }
751
1363
  const authMethod = config.npm.auth === "auto" ? detectNpmAuth() : config.npm.auth;
@@ -753,107 +1365,116 @@ async function runNpmPublishStage(ctx) {
753
1365
  throw createPublishError("NPM_AUTH_ERROR" /* NPM_AUTH_ERROR */, "No NPM authentication method detected");
754
1366
  }
755
1367
  const useProvenance = config.npm.provenance && authMethod === "oidc";
756
- for (const update of input.updates) {
757
- const result = {
758
- packageName: update.packageName,
759
- version: update.newVersion,
760
- registry: "npm",
761
- success: false,
762
- skipped: false
763
- };
764
- const pkgJsonPath = path4.resolve(cwd, update.filePath);
765
- try {
766
- const pkgContent = fs5.readFileSync(pkgJsonPath, "utf-8");
767
- const pkgJson = JSON.parse(pkgContent);
768
- if (pkgJson.private) {
769
- result.skipped = true;
770
- result.success = true;
771
- result.reason = "Package is private";
772
- ctx.output.npm.push(result);
773
- debug4(`Skipping private package: ${update.packageName}`);
774
- continue;
1368
+ const npmIsolation = createNpmSubprocessIsolation({
1369
+ authMethod,
1370
+ registryUrl: config.npm.registry
1371
+ });
1372
+ try {
1373
+ for (const update of input.updates) {
1374
+ const result = {
1375
+ packageName: update.packageName,
1376
+ version: update.newVersion,
1377
+ registry: "npm",
1378
+ success: false,
1379
+ skipped: false
1380
+ };
1381
+ const pkgJsonPath = path8.resolve(cwd, update.filePath);
1382
+ try {
1383
+ const pkgContent = fs9.readFileSync(pkgJsonPath, "utf-8");
1384
+ const pkgJson = JSON.parse(pkgContent);
1385
+ if (pkgJson.private) {
1386
+ result.skipped = true;
1387
+ result.success = true;
1388
+ result.reason = "Package is private";
1389
+ ctx.output.npm.push(result);
1390
+ debug(`Skipping private package: ${update.packageName}`);
1391
+ continue;
1392
+ }
1393
+ } catch {
1394
+ if (update.filePath.endsWith("Cargo.toml")) {
1395
+ result.skipped = true;
1396
+ result.success = true;
1397
+ result.reason = "Not an npm package";
1398
+ ctx.output.npm.push(result);
1399
+ continue;
1400
+ }
775
1401
  }
776
- } catch {
777
- if (update.filePath.endsWith("Cargo.toml")) {
1402
+ const { file: viewFile, args: viewArgs } = buildViewCommand(
1403
+ ctx.packageManager,
1404
+ update.packageName,
1405
+ update.newVersion
1406
+ );
1407
+ const viewResult = await execCommandSafe(viewFile, viewArgs, {
1408
+ cwd,
1409
+ dryRun: false,
1410
+ // Always check, even in dry-run
1411
+ env: npmIsolation.env
1412
+ });
1413
+ if (viewResult.exitCode === 0 && viewResult.stdout.trim()) {
1414
+ result.alreadyPublished = true;
778
1415
  result.skipped = true;
779
1416
  result.success = true;
780
- result.reason = "Not an npm package";
1417
+ result.reason = "Already published";
781
1418
  ctx.output.npm.push(result);
1419
+ warn(`${update.packageName}@${update.newVersion} is already published, skipping`);
782
1420
  continue;
783
1421
  }
784
- }
785
- const { file: viewFile, args: viewArgs } = buildViewCommand(
786
- ctx.packageManager,
787
- update.packageName,
788
- update.newVersion
789
- );
790
- const viewResult = await execCommandSafe(viewFile, viewArgs, {
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}`
1422
+ const distTag = getDistTag(update.newVersion, config.npm.tag);
1423
+ const pkgDir = path8.dirname(path8.resolve(cwd, update.filePath));
1424
+ const { file: pubFile, args: pubArgs } = buildPublishCommand(ctx.packageManager, update.packageName, pkgDir, {
1425
+ access: config.npm.access,
1426
+ tag: distTag,
1427
+ provenance: useProvenance,
1428
+ noGitChecks: true
817
1429
  });
818
- result.success = true;
819
- if (!dryRun) {
820
- success5(`Published ${update.packageName}@${update.newVersion} to npm`);
1430
+ try {
1431
+ await execCommand(pubFile, pubArgs, {
1432
+ cwd,
1433
+ dryRun,
1434
+ label: `npm publish ${update.packageName}@${update.newVersion}`,
1435
+ env: npmIsolation.env
1436
+ });
1437
+ result.success = true;
1438
+ if (!dryRun) {
1439
+ success(`Published ${update.packageName}@${update.newVersion} to npm`);
1440
+ }
1441
+ } catch (error) {
1442
+ result.reason = error instanceof Error ? error.message : String(error);
1443
+ warn(`Failed to publish ${update.packageName}: ${result.reason}`);
821
1444
  }
822
- } catch (error) {
823
- result.reason = error instanceof Error ? error.message : String(error);
824
- warn3(`Failed to publish ${update.packageName}: ${result.reason}`);
1445
+ ctx.output.npm.push(result);
825
1446
  }
826
- ctx.output.npm.push(result);
1447
+ } finally {
1448
+ npmIsolation.cleanup();
827
1449
  }
828
1450
  }
829
1451
 
830
1452
  // src/stages/prepare.ts
831
- import * as fs6 from "fs";
832
- import * as path5 from "path";
833
- import { debug as debug5, info as info6 } from "@releasekit/core";
1453
+ import * as fs10 from "fs";
1454
+ import * as path9 from "path";
834
1455
  async function runPrepareStage(ctx) {
835
1456
  const { input, config, cliOptions, cwd } = ctx;
836
1457
  if (config.npm.enabled && config.npm.copyFiles.length > 0) {
837
1458
  for (const update of input.updates) {
838
- const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
1459
+ const pkgDir = path9.dirname(path9.resolve(cwd, update.filePath));
839
1460
  for (const file of config.npm.copyFiles) {
840
- const src = path5.resolve(cwd, file);
841
- const dest = path5.join(pkgDir, file);
842
- if (!fs6.existsSync(src)) {
843
- debug5(`Source file not found, skipping copy: ${src}`);
1461
+ const src = path9.resolve(cwd, file);
1462
+ const dest = path9.join(pkgDir, file);
1463
+ if (!fs10.existsSync(src)) {
1464
+ debug(`Source file not found, skipping copy: ${src}`);
844
1465
  continue;
845
1466
  }
846
- if (path5.resolve(path5.dirname(src)) === path5.resolve(pkgDir)) {
847
- debug5(`Skipping copy of ${file} - same directory as source`);
1467
+ if (path9.resolve(path9.dirname(src)) === path9.resolve(pkgDir)) {
1468
+ debug(`Skipping copy of ${file} - same directory as source`);
848
1469
  continue;
849
1470
  }
850
1471
  if (cliOptions.dryRun) {
851
- info6(`[DRY RUN] Would copy ${src} \u2192 ${dest}`);
1472
+ info(`[DRY RUN] Would copy ${src} \u2192 ${dest}`);
852
1473
  continue;
853
1474
  }
854
1475
  try {
855
- fs6.copyFileSync(src, dest);
856
- debug5(`Copied ${file} \u2192 ${pkgDir}`);
1476
+ fs10.copyFileSync(src, dest);
1477
+ debug(`Copied ${file} \u2192 ${pkgDir}`);
857
1478
  } catch (error) {
858
1479
  throw createPublishError(
859
1480
  "FILE_COPY_ERROR" /* FILE_COPY_ERROR */,
@@ -865,26 +1486,22 @@ async function runPrepareStage(ctx) {
865
1486
  }
866
1487
  if (config.cargo.enabled) {
867
1488
  for (const update of input.updates) {
868
- const pkgDir = path5.dirname(path5.resolve(cwd, update.filePath));
869
- const cargoPath = path5.join(pkgDir, "Cargo.toml");
870
- if (!fs6.existsSync(cargoPath)) {
1489
+ const pkgDir = path9.dirname(path9.resolve(cwd, update.filePath));
1490
+ const cargoPath = path9.join(pkgDir, "Cargo.toml");
1491
+ if (!fs10.existsSync(cargoPath)) {
871
1492
  continue;
872
1493
  }
873
1494
  if (cliOptions.dryRun) {
874
- info6(`[DRY RUN] Would update ${cargoPath} to version ${update.newVersion}`);
1495
+ info(`[DRY RUN] Would update ${cargoPath} to version ${update.newVersion}`);
875
1496
  continue;
876
1497
  }
877
1498
  updateCargoVersion(cargoPath, update.newVersion);
878
- debug5(`Updated ${cargoPath} to version ${update.newVersion}`);
1499
+ debug(`Updated ${cargoPath} to version ${update.newVersion}`);
879
1500
  }
880
1501
  }
881
1502
  }
882
1503
 
883
- // src/stages/verify.ts
884
- import { debug as debug7, info as info7, success as success6, warn as warn4 } from "@releasekit/core";
885
-
886
1504
  // src/utils/retry.ts
887
- import { debug as debug6 } from "@releasekit/core";
888
1505
  async function withRetry(fn, options, shouldRetry) {
889
1506
  let lastError;
890
1507
  let delay = options.initialDelay;
@@ -897,7 +1514,7 @@ async function withRetry(fn, options, shouldRetry) {
897
1514
  throw error;
898
1515
  }
899
1516
  if (attempt < options.maxAttempts) {
900
- debug6(`Attempt ${attempt}/${options.maxAttempts} failed, retrying in ${delay}ms...`);
1517
+ debug(`Attempt ${attempt}/${options.maxAttempts} failed, retrying in ${delay}ms...`);
901
1518
  await sleep(delay);
902
1519
  delay = Math.floor(delay * options.backoffMultiplier);
903
1520
  }
@@ -923,7 +1540,7 @@ async function runVerifyStage(ctx) {
923
1540
  attempts: 0
924
1541
  };
925
1542
  if (cliOptions.dryRun) {
926
- info7(`[DRY RUN] Would verify ${pkg.packageName}@${pkg.version} on npm`);
1543
+ info(`[DRY RUN] Would verify ${pkg.packageName}@${pkg.version} on npm`);
927
1544
  result.verified = true;
928
1545
  ctx.output.verification.push(result);
929
1546
  continue;
@@ -939,12 +1556,12 @@ async function runVerifyStage(ctx) {
939
1556
  if (viewResult.exitCode !== 0 || !viewResult.stdout.trim()) {
940
1557
  throw new Error(`${pkg.packageName}@${pkg.version} not yet available on npm`);
941
1558
  }
942
- debug7(`Verified ${pkg.packageName}@${pkg.version} on npm`);
1559
+ debug(`Verified ${pkg.packageName}@${pkg.version} on npm`);
943
1560
  }, config.verify.npm);
944
1561
  result.verified = true;
945
- success6(`Verified ${pkg.packageName}@${pkg.version} on npm`);
1562
+ success(`Verified ${pkg.packageName}@${pkg.version} on npm`);
946
1563
  } catch {
947
- warn4(`Failed to verify ${pkg.packageName}@${pkg.version} on npm after ${result.attempts} attempts`);
1564
+ warn(`Failed to verify ${pkg.packageName}@${pkg.version} on npm after ${result.attempts} attempts`);
948
1565
  }
949
1566
  ctx.output.verification.push(result);
950
1567
  }
@@ -960,7 +1577,7 @@ async function runVerifyStage(ctx) {
960
1577
  attempts: 0
961
1578
  };
962
1579
  if (cliOptions.dryRun) {
963
- info7(`[DRY RUN] Would verify ${crate.packageName}@${crate.version} on crates.io`);
1580
+ info(`[DRY RUN] Would verify ${crate.packageName}@${crate.version} on crates.io`);
964
1581
  result.verified = true;
965
1582
  ctx.output.verification.push(result);
966
1583
  continue;
@@ -972,12 +1589,12 @@ async function runVerifyStage(ctx) {
972
1589
  if (!response.ok) {
973
1590
  throw new Error(`${crate.packageName}@${crate.version} not yet available on crates.io`);
974
1591
  }
975
- debug7(`Verified ${crate.packageName}@${crate.version} on crates.io`);
1592
+ debug(`Verified ${crate.packageName}@${crate.version} on crates.io`);
976
1593
  }, config.verify.cargo);
977
1594
  result.verified = true;
978
- success6(`Verified ${crate.packageName}@${crate.version} on crates.io`);
1595
+ success(`Verified ${crate.packageName}@${crate.version} on crates.io`);
979
1596
  } catch {
980
- warn4(`Failed to verify ${crate.packageName}@${crate.version} on crates.io after ${result.attempts} attempts`);
1597
+ warn(`Failed to verify ${crate.packageName}@${crate.version} on crates.io after ${result.attempts} attempts`);
981
1598
  }
982
1599
  ctx.output.verification.push(result);
983
1600
  }
@@ -1012,6 +1629,8 @@ async function runPipeline(input, config, options) {
1012
1629
  cliOptions: options,
1013
1630
  packageManager: detectPackageManager(cwd),
1014
1631
  cwd,
1632
+ releaseNotes: options.releaseNotes,
1633
+ additionalFiles: options.additionalFiles,
1015
1634
  output: {
1016
1635
  dryRun: options.dryRun,
1017
1636
  git: { committed: false, tags: [], pushed: false },
@@ -1023,7 +1642,10 @@ async function runPipeline(input, config, options) {
1023
1642
  };
1024
1643
  try {
1025
1644
  await runPrepareStage(ctx);
1026
- if (!options.skipGit) {
1645
+ if (options.skipGitCommit && !options.skipGit) {
1646
+ ctx.output.git.committed = !!input.commitMessage;
1647
+ ctx.output.git.tags = [...input.tags];
1648
+ } else if (!options.skipGit) {
1027
1649
  await runGitCommitStage(ctx);
1028
1650
  }
1029
1651
  if (!options.skipPublish) {
@@ -1052,41 +1674,40 @@ async function runPipeline(input, config, options) {
1052
1674
  }
1053
1675
 
1054
1676
  // src/stages/input.ts
1055
- import * as fs7 from "fs";
1056
- import { info as info8 } from "@releasekit/core";
1057
- import { z } from "zod";
1058
- var VersionChangelogEntrySchema = z.object({
1059
- type: z.string(),
1060
- description: z.string(),
1061
- issueIds: z.array(z.string()).optional(),
1062
- scope: z.string().optional(),
1063
- originalType: z.string().optional()
1677
+ import * as fs11 from "fs";
1678
+ import { z as z3 } from "zod";
1679
+ var VersionChangelogEntrySchema = z3.object({
1680
+ type: z3.string(),
1681
+ description: z3.string(),
1682
+ issueIds: z3.array(z3.string()).optional(),
1683
+ scope: z3.string().optional(),
1684
+ originalType: z3.string().optional()
1064
1685
  });
1065
- var VersionPackageChangelogSchema = z.object({
1066
- packageName: z.string(),
1067
- version: z.string(),
1068
- previousVersion: z.string().nullable(),
1069
- revisionRange: z.string(),
1070
- repoUrl: z.string().nullable(),
1071
- entries: z.array(VersionChangelogEntrySchema)
1686
+ var VersionPackageChangelogSchema = z3.object({
1687
+ packageName: z3.string(),
1688
+ version: z3.string(),
1689
+ previousVersion: z3.string().nullable(),
1690
+ revisionRange: z3.string(),
1691
+ repoUrl: z3.string().nullable(),
1692
+ entries: z3.array(VersionChangelogEntrySchema)
1072
1693
  });
1073
- var VersionPackageUpdateSchema = z.object({
1074
- packageName: z.string(),
1075
- newVersion: z.string(),
1076
- filePath: z.string()
1694
+ var VersionPackageUpdateSchema = z3.object({
1695
+ packageName: z3.string(),
1696
+ newVersion: z3.string(),
1697
+ filePath: z3.string()
1077
1698
  });
1078
- var VersionOutputSchema = z.object({
1079
- dryRun: z.boolean(),
1080
- updates: z.array(VersionPackageUpdateSchema),
1081
- changelogs: z.array(VersionPackageChangelogSchema),
1082
- commitMessage: z.string().optional(),
1083
- tags: z.array(z.string())
1699
+ var VersionOutputSchema = z3.object({
1700
+ dryRun: z3.boolean(),
1701
+ updates: z3.array(VersionPackageUpdateSchema),
1702
+ changelogs: z3.array(VersionPackageChangelogSchema),
1703
+ commitMessage: z3.string().optional(),
1704
+ tags: z3.array(z3.string())
1084
1705
  });
1085
1706
  async function parseInput(inputPath) {
1086
1707
  let raw;
1087
1708
  if (inputPath) {
1088
1709
  try {
1089
- raw = fs7.readFileSync(inputPath, "utf-8");
1710
+ raw = fs11.readFileSync(inputPath, "utf-8");
1090
1711
  } catch {
1091
1712
  throw createPublishError("INPUT_PARSE_ERROR" /* INPUT_PARSE_ERROR */, `Could not read file: ${inputPath}`);
1092
1713
  }
@@ -1106,7 +1727,7 @@ async function parseInput(inputPath) {
1106
1727
  ${issues}`);
1107
1728
  }
1108
1729
  if (result.data.updates.length === 0) {
1109
- info8("No package updates in version output \u2014 pipeline will be a no-op");
1730
+ info("No package updates in version output \u2014 pipeline will be a no-op");
1110
1731
  }
1111
1732
  return result.data;
1112
1733
  }
@@ -1119,7 +1740,11 @@ async function readStdin() {
1119
1740
  }
1120
1741
 
1121
1742
  export {
1122
- loadConfig,
1743
+ setLogLevel,
1744
+ setJsonMode,
1745
+ EXIT_CODES,
1746
+ parseCargoToml,
1747
+ loadConfig2 as loadConfig,
1123
1748
  getDefaultConfig2 as getDefaultConfig,
1124
1749
  BasePublishError,
1125
1750
  PublishError,
@@ -1128,7 +1753,6 @@ export {
1128
1753
  createPublishError,
1129
1754
  detectNpmAuth,
1130
1755
  hasCargoAuth,
1131
- parseCargoToml,
1132
1756
  updateCargoVersion,
1133
1757
  extractPathDeps,
1134
1758
  isPrerelease,