pubm 0.2.3 → 0.2.5

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/bin/cli.js CHANGED
@@ -4694,6 +4694,7 @@ function consoleError(error) {
4694
4694
  }
4695
4695
 
4696
4696
  // src/tasks/preflight.ts
4697
+ import { createHash as createHash2 } from "node:crypto";
4697
4698
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
4698
4699
  import { exec as exec2 } from "tinyexec";
4699
4700
 
@@ -4846,13 +4847,29 @@ async function syncGhSecrets(tokens) {
4846
4847
  for (const [registry, token] of Object.entries(tokens)) {
4847
4848
  const config = TOKEN_CONFIG[registry];
4848
4849
  if (!config) continue;
4849
- await exec2("gh", ["secret", "set", config.ghSecretName], {
4850
- throwOnError: true,
4851
- nodeOptions: { input: token }
4850
+ const result = exec2("gh", ["secret", "set", config.ghSecretName], {
4851
+ throwOnError: true
4852
4852
  });
4853
+ const proc = result.process;
4854
+ if (proc?.stdin) {
4855
+ proc.stdin.end(token);
4856
+ }
4857
+ await result;
4853
4858
  }
4854
4859
  }
4860
+ function tokensSyncHash(tokens) {
4861
+ const sorted = Object.entries(tokens).sort(([a2], [b]) => a2.localeCompare(b));
4862
+ return createHash2("sha256").update(JSON.stringify(sorted)).digest("hex").slice(0, 16);
4863
+ }
4864
+ var SYNC_HASH_DB_KEY = "gh-secrets-sync-hash";
4855
4865
  async function promptGhSecretsSync(tokens, task) {
4866
+ const db = new Db();
4867
+ const currentHash = tokensSyncHash(tokens);
4868
+ const storedHash = db.get(SYNC_HASH_DB_KEY);
4869
+ if (storedHash === currentHash) {
4870
+ task.output = "Tokens already synced to GitHub Secrets.";
4871
+ return;
4872
+ }
4856
4873
  const shouldSync = await task.prompt(ListrEnquirerPromptAdapter).run({
4857
4874
  type: "toggle",
4858
4875
  message: "Sync tokens to GitHub Secrets?",
@@ -4863,6 +4880,7 @@ async function promptGhSecretsSync(tokens, task) {
4863
4880
  task.output = "Syncing tokens to GitHub Secrets...";
4864
4881
  try {
4865
4882
  await syncGhSecrets(tokens);
4883
+ db.set(SYNC_HASH_DB_KEY, currentHash);
4866
4884
  task.output = "Tokens synced to GitHub Secrets.";
4867
4885
  } catch (error) {
4868
4886
  throw new PreflightError(
@@ -5448,7 +5466,7 @@ import process14 from "node:process";
5448
5466
  import npmCli3 from "@npmcli/promise-spawn";
5449
5467
  import SemVer from "semver";
5450
5468
  import { isCI as isCI2 } from "std-env";
5451
- import { exec as exec8 } from "tinyexec";
5469
+ import { exec as exec9 } from "tinyexec";
5452
5470
 
5453
5471
  // src/utils/cli.ts
5454
5472
  var warningBadge = color.bgYellow(" Warning ");
@@ -5460,6 +5478,7 @@ function link2(text, url) {
5460
5478
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
5461
5479
  import path8 from "node:path";
5462
5480
  import { parse, stringify } from "smol-toml";
5481
+ import { exec as exec4 } from "tinyexec";
5463
5482
 
5464
5483
  // src/ecosystem/ecosystem.ts
5465
5484
  var Ecosystem = class {
@@ -5502,6 +5521,28 @@ var RustEcosystem = class extends Ecosystem {
5502
5521
  pkg.version = newVersion;
5503
5522
  await writeFile(filePath, stringify(cargo));
5504
5523
  }
5524
+ async syncLockfile() {
5525
+ const lockfilePath = await this.findLockfile();
5526
+ if (!lockfilePath) return void 0;
5527
+ const name = await this.packageName();
5528
+ await exec4("cargo", ["update", "--package", name], {
5529
+ nodeOptions: { cwd: path8.dirname(lockfilePath) }
5530
+ });
5531
+ return lockfilePath;
5532
+ }
5533
+ async findLockfile() {
5534
+ let dir = this.packagePath;
5535
+ const { root } = path8.parse(dir);
5536
+ while (dir !== root) {
5537
+ const candidate = path8.join(dir, "Cargo.lock");
5538
+ try {
5539
+ if ((await stat2(candidate)).isFile()) return candidate;
5540
+ } catch {
5541
+ }
5542
+ dir = path8.dirname(dir);
5543
+ }
5544
+ return void 0;
5545
+ }
5505
5546
  async dependencies() {
5506
5547
  const cargo = await this.readCargoToml();
5507
5548
  const deps = [];
@@ -5809,10 +5850,19 @@ async function replaceVersion(version2, packages) {
5809
5850
  { cause: error }
5810
5851
  );
5811
5852
  }
5812
- return path9.join(pkg.path, "Cargo.toml");
5853
+ let lockfilePath;
5854
+ try {
5855
+ lockfilePath = await eco.syncLockfile();
5856
+ } catch (error) {
5857
+ throw new AbstractError(
5858
+ `Failed to sync Cargo.lock at ${pkg.path}: ${error instanceof Error ? error.message : error}`,
5859
+ { cause: error }
5860
+ );
5861
+ }
5862
+ return [path9.join(pkg.path, "Cargo.toml"), lockfilePath];
5813
5863
  })
5814
5864
  ]);
5815
- return results.filter((v) => v);
5865
+ return [...new Set(results.flat().filter((v) => !!v))];
5816
5866
  }
5817
5867
 
5818
5868
  // src/utils/package-manager.ts
@@ -5851,7 +5901,7 @@ function collectRegistries(ctx) {
5851
5901
 
5852
5902
  // src/registry/crates.ts
5853
5903
  import path10 from "node:path";
5854
- import { exec as exec4, NonZeroExitError } from "tinyexec";
5904
+ import { exec as exec5, NonZeroExitError } from "tinyexec";
5855
5905
 
5856
5906
  // src/registry/registry.ts
5857
5907
  var Registry = class {
@@ -5891,7 +5941,7 @@ var CratesRegistry = class extends Registry {
5891
5941
  }
5892
5942
  async isInstalled() {
5893
5943
  try {
5894
- await exec4("cargo", ["--version"]);
5944
+ await exec5("cargo", ["--version"]);
5895
5945
  return true;
5896
5946
  } catch {
5897
5947
  return false;
@@ -5932,7 +5982,7 @@ var CratesRegistry = class extends Registry {
5932
5982
  if (manifestDir) {
5933
5983
  args.push("--manifest-path", path10.join(manifestDir, "Cargo.toml"));
5934
5984
  }
5935
- await exec4("cargo", args, { throwOnError: true });
5985
+ await exec5("cargo", args, { throwOnError: true });
5936
5986
  return true;
5937
5987
  } catch (error) {
5938
5988
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
@@ -5947,7 +5997,7 @@ ${stderr}` : "Failed to run `cargo publish`";
5947
5997
  if (manifestDir) {
5948
5998
  args.push("--manifest-path", path10.join(manifestDir, "Cargo.toml"));
5949
5999
  }
5950
- await exec4("cargo", args, { throwOnError: true });
6000
+ await exec5("cargo", args, { throwOnError: true });
5951
6001
  } catch (error) {
5952
6002
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5953
6003
  const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
@@ -6045,7 +6095,7 @@ var cratesPublishTasks = createCratesPublishTask();
6045
6095
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6046
6096
 
6047
6097
  // src/registry/jsr.ts
6048
- import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
6098
+ import { exec as exec6, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
6049
6099
 
6050
6100
  // src/utils/package-name.ts
6051
6101
  import { builtinModules } from "node:module";
@@ -6112,7 +6162,7 @@ var JsrRegisry = class extends Registry {
6112
6162
  this.client = new JsrClient(getApiEndpoint(this.registry));
6113
6163
  }
6114
6164
  async jsr(args) {
6115
- const { stdout } = await exec5("jsr", args, { throwOnError: true });
6165
+ const { stdout } = await exec6("jsr", args, { throwOnError: true });
6116
6166
  return stdout;
6117
6167
  }
6118
6168
  async isInstalled() {
@@ -6128,7 +6178,7 @@ var JsrRegisry = class extends Registry {
6128
6178
  }
6129
6179
  async ping() {
6130
6180
  try {
6131
- const { stdout } = await exec5(
6181
+ const { stdout } = await exec6(
6132
6182
  "ping",
6133
6183
  [new URL(this.registry).hostname, "-c", "1"],
6134
6184
  { throwOnError: true }
@@ -6143,7 +6193,7 @@ var JsrRegisry = class extends Registry {
6143
6193
  }
6144
6194
  async publish() {
6145
6195
  try {
6146
- await exec5(
6196
+ await exec6(
6147
6197
  "jsr",
6148
6198
  ["publish", "--allow-dirty", "--token", `${JsrClient.token}`],
6149
6199
  {
@@ -6174,7 +6224,7 @@ ${stderr}` : ""}`,
6174
6224
  }
6175
6225
  async dryRunPublish() {
6176
6226
  try {
6177
- await exec5(
6227
+ await exec6(
6178
6228
  "jsr",
6179
6229
  [
6180
6230
  "publish",
@@ -6432,7 +6482,9 @@ async function jsrRegistry() {
6432
6482
  }
6433
6483
 
6434
6484
  // src/registry/npm.ts
6435
- import { exec as exec6, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6485
+ import { tmpdir } from "node:os";
6486
+ import { join } from "node:path";
6487
+ import { exec as exec7, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6436
6488
  var NpmError = class extends AbstractError {
6437
6489
  constructor() {
6438
6490
  super(...arguments);
@@ -6445,7 +6497,7 @@ var NpmRegistry = class extends Registry {
6445
6497
  __publicField(this, "registry", "https://registry.npmjs.org");
6446
6498
  }
6447
6499
  async npm(args) {
6448
- const { stdout } = await exec6("npm", args, { throwOnError: true });
6500
+ const { stdout } = await exec7("npm", args, { throwOnError: true });
6449
6501
  return stdout;
6450
6502
  }
6451
6503
  async isInstalled() {
@@ -6556,7 +6608,7 @@ var NpmRegistry = class extends Registry {
6556
6608
  }
6557
6609
  async ping() {
6558
6610
  try {
6559
- await exec6("npm", ["ping"], { throwOnError: true });
6611
+ await exec7("npm", ["ping"], { throwOnError: true });
6560
6612
  return true;
6561
6613
  } catch (error) {
6562
6614
  throw new NpmError("Failed to run `npm ping`", { cause: error });
@@ -6587,11 +6639,22 @@ var NpmRegistry = class extends Registry {
6587
6639
  }
6588
6640
  async dryRunPublish() {
6589
6641
  try {
6590
- await this.npm(["publish", "--dry-run"]);
6591
- } catch (error) {
6592
- throw new NpmError("Failed to run `npm publish --dry-run`", {
6593
- cause: error
6642
+ await exec7("npm", ["publish", "--dry-run"], {
6643
+ throwOnError: true,
6644
+ nodeOptions: {
6645
+ env: {
6646
+ ...process.env,
6647
+ npm_config_cache: join(tmpdir(), "pubm-npm-cache")
6648
+ }
6649
+ }
6594
6650
  });
6651
+ } catch (error) {
6652
+ const stderr = error instanceof NonZeroExitError3 ? error.output?.stderr : void 0;
6653
+ throw new NpmError(
6654
+ `Failed to run \`npm publish --dry-run\`${stderr ? `
6655
+ ${stderr}` : ""}`,
6656
+ { cause: error }
6657
+ );
6595
6658
  }
6596
6659
  }
6597
6660
  async twoFactorAuthMode() {
@@ -7214,10 +7277,10 @@ var prerequisitesCheckTask = (options) => {
7214
7277
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter6 } from "@listr2/prompt-adapter-enquirer";
7215
7278
 
7216
7279
  // src/registry/custom-registry.ts
7217
- import { exec as exec7 } from "tinyexec";
7280
+ import { exec as exec8 } from "tinyexec";
7218
7281
  var CustomRegistry = class extends NpmRegistry {
7219
7282
  async npm(args) {
7220
- const { stdout } = await exec7(
7283
+ const { stdout } = await exec8(
7221
7284
  "npm",
7222
7285
  args.concat("--registry", this.registry),
7223
7286
  { throwOnError: true }
@@ -7526,55 +7589,14 @@ async function run(options) {
7526
7589
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7527
7590
  concurrent: true
7528
7591
  })
7529
- } : options.preflight ? [
7530
- {
7531
- skip: options.skipTests,
7532
- title: "Running tests",
7533
- task: async (ctx2) => {
7534
- const packageManager = await getPackageManager();
7535
- try {
7536
- await exec8(packageManager, ["run", ctx2.testScript], {
7537
- throwOnError: true
7538
- });
7539
- } catch (error) {
7540
- throw new AbstractError(
7541
- `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
7542
- { cause: error }
7543
- );
7544
- }
7545
- }
7546
- },
7547
- {
7548
- skip: options.skipBuild,
7549
- title: "Building the project",
7550
- task: async (ctx2) => {
7551
- const packageManager = await getPackageManager();
7552
- try {
7553
- await exec8(packageManager, ["run", ctx2.buildScript], {
7554
- throwOnError: true
7555
- });
7556
- } catch (error) {
7557
- throw new AbstractError(
7558
- `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
7559
- { cause: error }
7560
- );
7561
- }
7562
- }
7563
- },
7564
- {
7565
- title: "Validating publish (dry-run)",
7566
- task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7567
- concurrent: true
7568
- })
7569
- }
7570
- ] : [
7592
+ } : [
7571
7593
  {
7572
7594
  skip: options.skipTests,
7573
7595
  title: "Running tests",
7574
7596
  task: async (ctx2) => {
7575
7597
  const packageManager = await getPackageManager();
7576
7598
  try {
7577
- await exec8(packageManager, ["run", ctx2.testScript], {
7599
+ await exec9(packageManager, ["run", ctx2.testScript], {
7578
7600
  throwOnError: true
7579
7601
  });
7580
7602
  } catch (error) {
@@ -7591,7 +7613,7 @@ async function run(options) {
7591
7613
  task: async (ctx2) => {
7592
7614
  const packageManager = await getPackageManager();
7593
7615
  try {
7594
- await exec8(packageManager, ["run", ctx2.buildScript], {
7616
+ await exec9(packageManager, ["run", ctx2.buildScript], {
7595
7617
  throwOnError: true
7596
7618
  });
7597
7619
  } catch (error) {
@@ -7651,12 +7673,19 @@ async function run(options) {
7651
7673
  }
7652
7674
  },
7653
7675
  {
7654
- skip: (ctx2) => options.skipPublish || !!ctx2.preview,
7676
+ skip: (ctx2) => options.skipPublish || !!ctx2.preview || options.preflight,
7655
7677
  title: "Publishing",
7656
7678
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7657
7679
  concurrent: true
7658
7680
  })
7659
7681
  },
7682
+ {
7683
+ skip: !options.preflight,
7684
+ title: "Validating publish (dry-run)",
7685
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7686
+ concurrent: true
7687
+ })
7688
+ },
7660
7689
  {
7661
7690
  title: "Pushing tags to GitHub",
7662
7691
  skip: (ctx2) => !!ctx2.preview,
@@ -7751,7 +7780,7 @@ import micromatch from "micromatch";
7751
7780
 
7752
7781
  // src/monorepo/workspace.ts
7753
7782
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
7754
- import { join } from "node:path";
7783
+ import { join as join2 } from "node:path";
7755
7784
  import { parse as parse2 } from "yaml";
7756
7785
 
7757
7786
  // src/monorepo/groups.ts
@@ -7994,7 +8023,7 @@ defaultCmd.action(
7994
8023
  };
7995
8024
  try {
7996
8025
  if (options.preflight) {
7997
- context.version = nextVersion || "0.0.0-preflight";
8026
+ await requiredMissingInformationTasks().run(context);
7998
8027
  } else if (isCI3) {
7999
8028
  if (options.publishOnly) {
8000
8029
  const git = new Git();
package/dist/index.cjs CHANGED
@@ -4486,7 +4486,7 @@ var Listr = (_a23 = class {
4486
4486
  // src/tasks/runner.ts
4487
4487
  var import_semver3 = __toESM(require("semver"), 1);
4488
4488
  var import_std_env = require("std-env");
4489
- var import_tinyexec7 = require("tinyexec");
4489
+ var import_tinyexec8 = require("tinyexec");
4490
4490
 
4491
4491
  // src/error.ts
4492
4492
  var AbstractError = class extends Error {
@@ -4818,6 +4818,7 @@ function link2(text, url) {
4818
4818
  var import_promises2 = require("fs/promises");
4819
4819
  var import_node_path2 = __toESM(require("path"), 1);
4820
4820
  var import_smol_toml = require("smol-toml");
4821
+ var import_tinyexec2 = require("tinyexec");
4821
4822
 
4822
4823
  // src/ecosystem/ecosystem.ts
4823
4824
  var Ecosystem = class {
@@ -4860,6 +4861,28 @@ var RustEcosystem = class extends Ecosystem {
4860
4861
  pkg.version = newVersion;
4861
4862
  await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4862
4863
  }
4864
+ async syncLockfile() {
4865
+ const lockfilePath = await this.findLockfile();
4866
+ if (!lockfilePath) return void 0;
4867
+ const name = await this.packageName();
4868
+ await (0, import_tinyexec2.exec)("cargo", ["update", "--package", name], {
4869
+ nodeOptions: { cwd: import_node_path2.default.dirname(lockfilePath) }
4870
+ });
4871
+ return lockfilePath;
4872
+ }
4873
+ async findLockfile() {
4874
+ let dir = this.packagePath;
4875
+ const { root } = import_node_path2.default.parse(dir);
4876
+ while (dir !== root) {
4877
+ const candidate = import_node_path2.default.join(dir, "Cargo.lock");
4878
+ try {
4879
+ if ((await (0, import_promises2.stat)(candidate)).isFile()) return candidate;
4880
+ } catch {
4881
+ }
4882
+ dir = import_node_path2.default.dirname(dir);
4883
+ }
4884
+ return void 0;
4885
+ }
4863
4886
  async dependencies() {
4864
4887
  const cargo = await this.readCargoToml();
4865
4888
  const deps = [];
@@ -5167,10 +5190,19 @@ async function replaceVersion(version2, packages) {
5167
5190
  { cause: error }
5168
5191
  );
5169
5192
  }
5170
- return import_node_path3.default.join(pkg.path, "Cargo.toml");
5193
+ let lockfilePath;
5194
+ try {
5195
+ lockfilePath = await eco.syncLockfile();
5196
+ } catch (error) {
5197
+ throw new AbstractError(
5198
+ `Failed to sync Cargo.lock at ${pkg.path}: ${error instanceof Error ? error.message : error}`,
5199
+ { cause: error }
5200
+ );
5201
+ }
5202
+ return [import_node_path3.default.join(pkg.path, "Cargo.toml"), lockfilePath];
5171
5203
  })
5172
5204
  ]);
5173
- return results.filter((v) => v);
5205
+ return [...new Set(results.flat().filter((v) => !!v))];
5174
5206
  }
5175
5207
 
5176
5208
  // src/utils/package-manager.ts
@@ -5332,7 +5364,7 @@ function injectTokensToEnv(tokens) {
5332
5364
 
5333
5365
  // src/registry/crates.ts
5334
5366
  var import_node_path5 = __toESM(require("path"), 1);
5335
- var import_tinyexec2 = require("tinyexec");
5367
+ var import_tinyexec3 = require("tinyexec");
5336
5368
 
5337
5369
  // src/registry/registry.ts
5338
5370
  var Registry = class {
@@ -5372,7 +5404,7 @@ var CratesRegistry = class extends Registry {
5372
5404
  }
5373
5405
  async isInstalled() {
5374
5406
  try {
5375
- await (0, import_tinyexec2.exec)("cargo", ["--version"]);
5407
+ await (0, import_tinyexec3.exec)("cargo", ["--version"]);
5376
5408
  return true;
5377
5409
  } catch {
5378
5410
  return false;
@@ -5413,10 +5445,10 @@ var CratesRegistry = class extends Registry {
5413
5445
  if (manifestDir) {
5414
5446
  args.push("--manifest-path", import_node_path5.default.join(manifestDir, "Cargo.toml"));
5415
5447
  }
5416
- await (0, import_tinyexec2.exec)("cargo", args, { throwOnError: true });
5448
+ await (0, import_tinyexec3.exec)("cargo", args, { throwOnError: true });
5417
5449
  return true;
5418
5450
  } catch (error) {
5419
- const stderr = error instanceof import_tinyexec2.NonZeroExitError ? error.output?.stderr : void 0;
5451
+ const stderr = error instanceof import_tinyexec3.NonZeroExitError ? error.output?.stderr : void 0;
5420
5452
  const message = stderr ? `Failed to run \`cargo publish\`:
5421
5453
  ${stderr}` : "Failed to run `cargo publish`";
5422
5454
  throw new CratesError(message, { cause: error });
@@ -5428,9 +5460,9 @@ ${stderr}` : "Failed to run `cargo publish`";
5428
5460
  if (manifestDir) {
5429
5461
  args.push("--manifest-path", import_node_path5.default.join(manifestDir, "Cargo.toml"));
5430
5462
  }
5431
- await (0, import_tinyexec2.exec)("cargo", args, { throwOnError: true });
5463
+ await (0, import_tinyexec3.exec)("cargo", args, { throwOnError: true });
5432
5464
  } catch (error) {
5433
- const stderr = error instanceof import_tinyexec2.NonZeroExitError ? error.output?.stderr : void 0;
5465
+ const stderr = error instanceof import_tinyexec3.NonZeroExitError ? error.output?.stderr : void 0;
5434
5466
  const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
5435
5467
  ${stderr}` : "Failed to run `cargo publish --dry-run`";
5436
5468
  throw new CratesError(message, { cause: error });
@@ -5526,7 +5558,7 @@ var cratesPublishTasks = createCratesPublishTask();
5526
5558
  var import_prompt_adapter_enquirer = require("@listr2/prompt-adapter-enquirer");
5527
5559
 
5528
5560
  // src/registry/jsr.ts
5529
- var import_tinyexec3 = require("tinyexec");
5561
+ var import_tinyexec4 = require("tinyexec");
5530
5562
 
5531
5563
  // src/utils/package-name.ts
5532
5564
  var import_node_module = require("module");
@@ -5594,7 +5626,7 @@ var JsrRegisry = class extends Registry {
5594
5626
  this.client = new JsrClient(getApiEndpoint(this.registry));
5595
5627
  }
5596
5628
  async jsr(args) {
5597
- const { stdout } = await (0, import_tinyexec3.exec)("jsr", args, { throwOnError: true });
5629
+ const { stdout } = await (0, import_tinyexec4.exec)("jsr", args, { throwOnError: true });
5598
5630
  return stdout;
5599
5631
  }
5600
5632
  async isInstalled() {
@@ -5610,7 +5642,7 @@ var JsrRegisry = class extends Registry {
5610
5642
  }
5611
5643
  async ping() {
5612
5644
  try {
5613
- const { stdout } = await (0, import_tinyexec3.exec)(
5645
+ const { stdout } = await (0, import_tinyexec4.exec)(
5614
5646
  "ping",
5615
5647
  [new URL(this.registry).hostname, "-c", "1"],
5616
5648
  { throwOnError: true }
@@ -5625,7 +5657,7 @@ var JsrRegisry = class extends Registry {
5625
5657
  }
5626
5658
  async publish() {
5627
5659
  try {
5628
- await (0, import_tinyexec3.exec)(
5660
+ await (0, import_tinyexec4.exec)(
5629
5661
  "jsr",
5630
5662
  ["publish", "--allow-dirty", "--token", `${JsrClient.token}`],
5631
5663
  {
@@ -5635,7 +5667,7 @@ var JsrRegisry = class extends Registry {
5635
5667
  this.packageCreationUrls = void 0;
5636
5668
  return true;
5637
5669
  } catch (error) {
5638
- const stderr = error instanceof import_tinyexec3.NonZeroExitError ? error.output?.stderr : void 0;
5670
+ const stderr = error instanceof import_tinyexec4.NonZeroExitError ? error.output?.stderr : void 0;
5639
5671
  if (stderr?.includes("don't exist")) {
5640
5672
  const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5641
5673
  (m) => m[0]
@@ -5656,7 +5688,7 @@ ${stderr}` : ""}`,
5656
5688
  }
5657
5689
  async dryRunPublish() {
5658
5690
  try {
5659
- await (0, import_tinyexec3.exec)(
5691
+ await (0, import_tinyexec4.exec)(
5660
5692
  "jsr",
5661
5693
  [
5662
5694
  "publish",
@@ -5914,7 +5946,9 @@ async function jsrRegistry() {
5914
5946
  }
5915
5947
 
5916
5948
  // src/registry/npm.ts
5917
- var import_tinyexec4 = require("tinyexec");
5949
+ var import_node_os = require("os");
5950
+ var import_node_path6 = require("path");
5951
+ var import_tinyexec5 = require("tinyexec");
5918
5952
  var NpmError = class extends AbstractError {
5919
5953
  constructor() {
5920
5954
  super(...arguments);
@@ -5927,7 +5961,7 @@ var NpmRegistry = class extends Registry {
5927
5961
  __publicField(this, "registry", "https://registry.npmjs.org");
5928
5962
  }
5929
5963
  async npm(args) {
5930
- const { stdout } = await (0, import_tinyexec4.exec)("npm", args, { throwOnError: true });
5964
+ const { stdout } = await (0, import_tinyexec5.exec)("npm", args, { throwOnError: true });
5931
5965
  return stdout;
5932
5966
  }
5933
5967
  async isInstalled() {
@@ -5971,7 +6005,7 @@ var NpmRegistry = class extends Registry {
5971
6005
  await this.npm(["whoami"]);
5972
6006
  return true;
5973
6007
  } catch (error) {
5974
- if (error instanceof import_tinyexec4.NonZeroExitError) {
6008
+ if (error instanceof import_tinyexec5.NonZeroExitError) {
5975
6009
  return false;
5976
6010
  }
5977
6011
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -6038,7 +6072,7 @@ var NpmRegistry = class extends Registry {
6038
6072
  }
6039
6073
  async ping() {
6040
6074
  try {
6041
- await (0, import_tinyexec4.exec)("npm", ["ping"], { throwOnError: true });
6075
+ await (0, import_tinyexec5.exec)("npm", ["ping"], { throwOnError: true });
6042
6076
  return true;
6043
6077
  } catch (error) {
6044
6078
  throw new NpmError("Failed to run `npm ping`", { cause: error });
@@ -6049,7 +6083,7 @@ var NpmRegistry = class extends Registry {
6049
6083
  await this.npm(["publish", "--provenance", "--access", "public"]);
6050
6084
  return true;
6051
6085
  } catch (error) {
6052
- if (error instanceof import_tinyexec4.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
6086
+ if (error instanceof import_tinyexec5.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
6053
6087
  return false;
6054
6088
  }
6055
6089
  throw this.classifyPublishError(error);
@@ -6061,7 +6095,7 @@ var NpmRegistry = class extends Registry {
6061
6095
  await this.npm(args);
6062
6096
  return true;
6063
6097
  } catch (error) {
6064
- if (error instanceof import_tinyexec4.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
6098
+ if (error instanceof import_tinyexec5.NonZeroExitError && error.output?.stderr.includes("EOTP")) {
6065
6099
  return false;
6066
6100
  }
6067
6101
  throw this.classifyPublishError(error);
@@ -6069,11 +6103,22 @@ var NpmRegistry = class extends Registry {
6069
6103
  }
6070
6104
  async dryRunPublish() {
6071
6105
  try {
6072
- await this.npm(["publish", "--dry-run"]);
6073
- } catch (error) {
6074
- throw new NpmError("Failed to run `npm publish --dry-run`", {
6075
- cause: error
6106
+ await (0, import_tinyexec5.exec)("npm", ["publish", "--dry-run"], {
6107
+ throwOnError: true,
6108
+ nodeOptions: {
6109
+ env: {
6110
+ ...process.env,
6111
+ npm_config_cache: (0, import_node_path6.join)((0, import_node_os.tmpdir)(), "pubm-npm-cache")
6112
+ }
6113
+ }
6076
6114
  });
6115
+ } catch (error) {
6116
+ const stderr = error instanceof import_tinyexec5.NonZeroExitError ? error.output?.stderr : void 0;
6117
+ throw new NpmError(
6118
+ `Failed to run \`npm publish --dry-run\`${stderr ? `
6119
+ ${stderr}` : ""}`,
6120
+ { cause: error }
6121
+ );
6077
6122
  }
6078
6123
  }
6079
6124
  async twoFactorAuthMode() {
@@ -6095,7 +6140,7 @@ var NpmRegistry = class extends Registry {
6095
6140
  };
6096
6141
  }
6097
6142
  classifyPublishError(error) {
6098
- if (error instanceof import_tinyexec4.NonZeroExitError) {
6143
+ if (error instanceof import_tinyexec5.NonZeroExitError) {
6099
6144
  const stderr = error.output?.stderr ?? "";
6100
6145
  if (stderr.includes("EOTP")) {
6101
6146
  return new NpmError("OTP required for publishing", { cause: error });
@@ -6547,8 +6592,9 @@ var npmPublishTasks = {
6547
6592
  };
6548
6593
 
6549
6594
  // src/tasks/preflight.ts
6595
+ var import_node_crypto2 = require("crypto");
6550
6596
  var import_prompt_adapter_enquirer4 = require("@listr2/prompt-adapter-enquirer");
6551
- var import_tinyexec5 = require("tinyexec");
6597
+ var import_tinyexec6 = require("tinyexec");
6552
6598
  var PreflightError = class extends AbstractError {
6553
6599
  constructor() {
6554
6600
  super(...arguments);
@@ -6575,13 +6621,29 @@ async function syncGhSecrets(tokens) {
6575
6621
  for (const [registry, token] of Object.entries(tokens)) {
6576
6622
  const config = TOKEN_CONFIG[registry];
6577
6623
  if (!config) continue;
6578
- await (0, import_tinyexec5.exec)("gh", ["secret", "set", config.ghSecretName], {
6579
- throwOnError: true,
6580
- nodeOptions: { input: token }
6624
+ const result = (0, import_tinyexec6.exec)("gh", ["secret", "set", config.ghSecretName], {
6625
+ throwOnError: true
6581
6626
  });
6627
+ const proc = result.process;
6628
+ if (proc?.stdin) {
6629
+ proc.stdin.end(token);
6630
+ }
6631
+ await result;
6582
6632
  }
6583
6633
  }
6634
+ function tokensSyncHash(tokens) {
6635
+ const sorted = Object.entries(tokens).sort(([a2], [b]) => a2.localeCompare(b));
6636
+ return (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(sorted)).digest("hex").slice(0, 16);
6637
+ }
6638
+ var SYNC_HASH_DB_KEY = "gh-secrets-sync-hash";
6584
6639
  async function promptGhSecretsSync(tokens, task) {
6640
+ const db = new Db();
6641
+ const currentHash = tokensSyncHash(tokens);
6642
+ const storedHash = db.get(SYNC_HASH_DB_KEY);
6643
+ if (storedHash === currentHash) {
6644
+ task.output = "Tokens already synced to GitHub Secrets.";
6645
+ return;
6646
+ }
6585
6647
  const shouldSync = await task.prompt(import_prompt_adapter_enquirer4.ListrEnquirerPromptAdapter).run({
6586
6648
  type: "toggle",
6587
6649
  message: "Sync tokens to GitHub Secrets?",
@@ -6592,6 +6654,7 @@ async function promptGhSecretsSync(tokens, task) {
6592
6654
  task.output = "Syncing tokens to GitHub Secrets...";
6593
6655
  try {
6594
6656
  await syncGhSecrets(tokens);
6657
+ db.set(SYNC_HASH_DB_KEY, currentHash);
6595
6658
  task.output = "Tokens synced to GitHub Secrets.";
6596
6659
  } catch (error) {
6597
6660
  throw new PreflightError(
@@ -6752,10 +6815,10 @@ var prerequisitesCheckTask = (options) => {
6752
6815
  var import_prompt_adapter_enquirer6 = require("@listr2/prompt-adapter-enquirer");
6753
6816
 
6754
6817
  // src/registry/custom-registry.ts
6755
- var import_tinyexec6 = require("tinyexec");
6818
+ var import_tinyexec7 = require("tinyexec");
6756
6819
  var CustomRegistry = class extends NpmRegistry {
6757
6820
  async npm(args) {
6758
- const { stdout } = await (0, import_tinyexec6.exec)(
6821
+ const { stdout } = await (0, import_tinyexec7.exec)(
6759
6822
  "npm",
6760
6823
  args.concat("--registry", this.registry),
6761
6824
  { throwOnError: true }
@@ -7065,55 +7128,14 @@ async function run(options) {
7065
7128
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7066
7129
  concurrent: true
7067
7130
  })
7068
- } : options.preflight ? [
7069
- {
7070
- skip: options.skipTests,
7071
- title: "Running tests",
7072
- task: async (ctx2) => {
7073
- const packageManager = await getPackageManager();
7074
- try {
7075
- await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.testScript], {
7076
- throwOnError: true
7077
- });
7078
- } catch (error) {
7079
- throw new AbstractError(
7080
- `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
7081
- { cause: error }
7082
- );
7083
- }
7084
- }
7085
- },
7086
- {
7087
- skip: options.skipBuild,
7088
- title: "Building the project",
7089
- task: async (ctx2) => {
7090
- const packageManager = await getPackageManager();
7091
- try {
7092
- await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.buildScript], {
7093
- throwOnError: true
7094
- });
7095
- } catch (error) {
7096
- throw new AbstractError(
7097
- `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
7098
- { cause: error }
7099
- );
7100
- }
7101
- }
7102
- },
7103
- {
7104
- title: "Validating publish (dry-run)",
7105
- task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7106
- concurrent: true
7107
- })
7108
- }
7109
- ] : [
7131
+ } : [
7110
7132
  {
7111
7133
  skip: options.skipTests,
7112
7134
  title: "Running tests",
7113
7135
  task: async (ctx2) => {
7114
7136
  const packageManager = await getPackageManager();
7115
7137
  try {
7116
- await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.testScript], {
7138
+ await (0, import_tinyexec8.exec)(packageManager, ["run", ctx2.testScript], {
7117
7139
  throwOnError: true
7118
7140
  });
7119
7141
  } catch (error) {
@@ -7130,7 +7152,7 @@ async function run(options) {
7130
7152
  task: async (ctx2) => {
7131
7153
  const packageManager = await getPackageManager();
7132
7154
  try {
7133
- await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.buildScript], {
7155
+ await (0, import_tinyexec8.exec)(packageManager, ["run", ctx2.buildScript], {
7134
7156
  throwOnError: true
7135
7157
  });
7136
7158
  } catch (error) {
@@ -7190,12 +7212,19 @@ async function run(options) {
7190
7212
  }
7191
7213
  },
7192
7214
  {
7193
- skip: (ctx2) => options.skipPublish || !!ctx2.preview,
7215
+ skip: (ctx2) => options.skipPublish || !!ctx2.preview || options.preflight,
7194
7216
  title: "Publishing",
7195
7217
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7196
7218
  concurrent: true
7197
7219
  })
7198
7220
  },
7221
+ {
7222
+ skip: !options.preflight,
7223
+ title: "Validating publish (dry-run)",
7224
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7225
+ concurrent: true
7226
+ })
7227
+ },
7199
7228
  {
7200
7229
  title: "Pushing tags to GitHub",
7201
7230
  skip: (ctx2) => !!ctx2.preview,
@@ -7321,11 +7350,11 @@ function generateChangelog(version2, entries, depUpdates) {
7321
7350
 
7322
7351
  // src/changeset/migrate.ts
7323
7352
  var import_node_fs2 = require("fs");
7324
- var import_node_path6 = __toESM(require("path"), 1);
7353
+ var import_node_path7 = __toESM(require("path"), 1);
7325
7354
  var import_node_process9 = __toESM(require("process"), 1);
7326
7355
  var SKIPPED_FILES = /* @__PURE__ */ new Set(["config.json", "README.md"]);
7327
7356
  function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
7328
- const changesetDir = import_node_path6.default.join(cwd, ".changeset");
7357
+ const changesetDir = import_node_path7.default.join(cwd, ".changeset");
7329
7358
  if (!(0, import_node_fs2.existsSync)(changesetDir)) {
7330
7359
  return {
7331
7360
  success: false,
@@ -7334,7 +7363,7 @@ function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
7334
7363
  configMigrated: false
7335
7364
  };
7336
7365
  }
7337
- const pubmDir = import_node_path6.default.join(cwd, ".pubm", "changesets");
7366
+ const pubmDir = import_node_path7.default.join(cwd, ".pubm", "changesets");
7338
7367
  (0, import_node_fs2.mkdirSync)(pubmDir, { recursive: true });
7339
7368
  const files = (0, import_node_fs2.readdirSync)(changesetDir);
7340
7369
  const migratedFiles = [];
@@ -7349,15 +7378,15 @@ function migrateFromChangesets(cwd = import_node_process9.default.cwd()) {
7349
7378
  }
7350
7379
  if (file === "pre.json") {
7351
7380
  (0, import_node_fs2.copyFileSync)(
7352
- import_node_path6.default.join(changesetDir, file),
7353
- import_node_path6.default.resolve(cwd, ".pubm", "pre.json")
7381
+ import_node_path7.default.join(changesetDir, file),
7382
+ import_node_path7.default.resolve(cwd, ".pubm", "pre.json")
7354
7383
  );
7355
7384
  migratedFiles.push(file);
7356
7385
  continue;
7357
7386
  }
7358
7387
  if (file.endsWith(".md")) {
7359
- const src = import_node_path6.default.join(changesetDir, file);
7360
- const dest = import_node_path6.default.join(pubmDir, file);
7388
+ const src = import_node_path7.default.join(changesetDir, file);
7389
+ const dest = import_node_path7.default.join(pubmDir, file);
7361
7390
  (0, import_node_fs2.copyFileSync)(src, dest);
7362
7391
  migratedFiles.push(file);
7363
7392
  }
@@ -7404,10 +7433,10 @@ function parseChangeset(content, fileName) {
7404
7433
 
7405
7434
  // src/changeset/reader.ts
7406
7435
  var import_node_fs3 = require("fs");
7407
- var import_node_path7 = __toESM(require("path"), 1);
7436
+ var import_node_path8 = __toESM(require("path"), 1);
7408
7437
  var import_node_process10 = __toESM(require("process"), 1);
7409
7438
  function readChangesets(cwd = import_node_process10.default.cwd()) {
7410
- const changesetsDir = import_node_path7.default.join(cwd, ".pubm", "changesets");
7439
+ const changesetsDir = import_node_path8.default.join(cwd, ".pubm", "changesets");
7411
7440
  if (!(0, import_node_fs3.existsSync)(changesetsDir)) {
7412
7441
  return [];
7413
7442
  }
@@ -7417,7 +7446,7 @@ function readChangesets(cwd = import_node_process10.default.cwd()) {
7417
7446
  if (!file.endsWith(".md") || file === "README.md") {
7418
7447
  continue;
7419
7448
  }
7420
- const filePath = import_node_path7.default.join(changesetsDir, file);
7449
+ const filePath = import_node_path8.default.join(changesetsDir, file);
7421
7450
  const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
7422
7451
  changesets.push(parseChangeset(content, file));
7423
7452
  }
@@ -7487,7 +7516,7 @@ function calculateVersionBumps(currentVersions, cwd = import_node_process12.defa
7487
7516
 
7488
7517
  // src/changeset/writer.ts
7489
7518
  var import_node_fs4 = require("fs");
7490
- var import_node_path8 = __toESM(require("path"), 1);
7519
+ var import_node_path9 = __toESM(require("path"), 1);
7491
7520
  var import_node_process13 = __toESM(require("process"), 1);
7492
7521
  var import_yaml2 = require("yaml");
7493
7522
  var adjectives = [
@@ -7582,11 +7611,11 @@ ${summary}
7582
7611
  return content;
7583
7612
  }
7584
7613
  function writeChangeset(releases, summary, cwd = import_node_process13.default.cwd()) {
7585
- const changesetsDir = import_node_path8.default.join(cwd, ".pubm", "changesets");
7614
+ const changesetsDir = import_node_path9.default.join(cwd, ".pubm", "changesets");
7586
7615
  (0, import_node_fs4.mkdirSync)(changesetsDir, { recursive: true });
7587
7616
  const id = generateChangesetId();
7588
7617
  const fileName = `${id}.md`;
7589
- const filePath = import_node_path8.default.join(changesetsDir, fileName);
7618
+ const filePath = import_node_path9.default.join(changesetsDir, fileName);
7590
7619
  const content = generateChangesetContent(releases, summary);
7591
7620
  (0, import_node_fs4.writeFileSync)(filePath, content, "utf-8");
7592
7621
  return filePath;
@@ -7645,23 +7674,23 @@ function topologicalSort(graph) {
7645
7674
 
7646
7675
  // src/monorepo/discover.ts
7647
7676
  var import_node_fs6 = require("fs");
7648
- var import_node_path10 = __toESM(require("path"), 1);
7677
+ var import_node_path11 = __toESM(require("path"), 1);
7649
7678
  var import_micromatch = __toESM(require("micromatch"), 1);
7650
7679
 
7651
7680
  // src/monorepo/workspace.ts
7652
7681
  var import_node_fs5 = require("fs");
7653
- var import_node_path9 = require("path");
7682
+ var import_node_path10 = require("path");
7654
7683
  var import_yaml3 = require("yaml");
7655
7684
  function detectWorkspace(cwd) {
7656
7685
  const root = cwd ?? process.cwd();
7657
- const pnpmWorkspacePath = (0, import_node_path9.join)(root, "pnpm-workspace.yaml");
7686
+ const pnpmWorkspacePath = (0, import_node_path10.join)(root, "pnpm-workspace.yaml");
7658
7687
  if ((0, import_node_fs5.existsSync)(pnpmWorkspacePath)) {
7659
7688
  const content = (0, import_node_fs5.readFileSync)(pnpmWorkspacePath, "utf-8");
7660
7689
  const parsed = (0, import_yaml3.parse)(content);
7661
7690
  const packages = parsed?.packages ?? [];
7662
7691
  return { type: "pnpm", patterns: packages };
7663
7692
  }
7664
- const packageJsonPath = (0, import_node_path9.join)(root, "package.json");
7693
+ const packageJsonPath = (0, import_node_path10.join)(root, "package.json");
7665
7694
  if ((0, import_node_fs5.existsSync)(packageJsonPath)) {
7666
7695
  const content = (0, import_node_fs5.readFileSync)(packageJsonPath, "utf-8");
7667
7696
  const pkg = JSON.parse(content);
@@ -7722,9 +7751,9 @@ function applyLinkedGroup(bumps, group) {
7722
7751
 
7723
7752
  // src/prerelease/pre.ts
7724
7753
  var import_node_fs7 = require("fs");
7725
- var import_node_path11 = __toESM(require("path"), 1);
7754
+ var import_node_path12 = __toESM(require("path"), 1);
7726
7755
  function getPreStatePath(cwd) {
7727
- return import_node_path11.default.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
7756
+ return import_node_path12.default.resolve(cwd ?? process.cwd(), ".pubm", "pre.json");
7728
7757
  }
7729
7758
  function readPreState(cwd) {
7730
7759
  const filePath = getPreStatePath(cwd);
@@ -7741,7 +7770,7 @@ function enterPreMode(tag, cwd) {
7741
7770
  "Already in pre mode. Exit pre mode first before entering a new one."
7742
7771
  );
7743
7772
  }
7744
- const dir = import_node_path11.default.dirname(filePath);
7773
+ const dir = import_node_path12.default.dirname(filePath);
7745
7774
  if (!(0, import_node_fs7.existsSync)(dir)) {
7746
7775
  (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
7747
7776
  }
@@ -7783,10 +7812,10 @@ function generateSnapshotVersion(options) {
7783
7812
 
7784
7813
  // src/validate/entry-points.ts
7785
7814
  var import_node_fs8 = require("fs");
7786
- var import_node_path12 = __toESM(require("path"), 1);
7815
+ var import_node_path13 = __toESM(require("path"), 1);
7787
7816
  var SIMPLE_FIELDS = ["main", "module", "types", "typings"];
7788
7817
  function checkPath(filePath, cwd) {
7789
- return (0, import_node_fs8.existsSync)(import_node_path12.default.resolve(cwd, filePath));
7818
+ return (0, import_node_fs8.existsSync)(import_node_path13.default.resolve(cwd, filePath));
7790
7819
  }
7791
7820
  function validateExports(exports2, cwd, prefix = "exports") {
7792
7821
  const errors = [];
package/dist/index.js CHANGED
@@ -4453,7 +4453,7 @@ var Listr = (_a23 = class {
4453
4453
  // src/tasks/runner.ts
4454
4454
  import SemVer from "semver";
4455
4455
  import { isCI as isCI2 } from "std-env";
4456
- import { exec as exec8 } from "tinyexec";
4456
+ import { exec as exec9 } from "tinyexec";
4457
4457
 
4458
4458
  // src/error.ts
4459
4459
  var AbstractError = class extends Error {
@@ -4785,6 +4785,7 @@ function link2(text, url) {
4785
4785
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
4786
4786
  import path2 from "node:path";
4787
4787
  import { parse, stringify } from "smol-toml";
4788
+ import { exec as exec3 } from "tinyexec";
4788
4789
 
4789
4790
  // src/ecosystem/ecosystem.ts
4790
4791
  var Ecosystem = class {
@@ -4827,6 +4828,28 @@ var RustEcosystem = class extends Ecosystem {
4827
4828
  pkg.version = newVersion;
4828
4829
  await writeFile(filePath, stringify(cargo));
4829
4830
  }
4831
+ async syncLockfile() {
4832
+ const lockfilePath = await this.findLockfile();
4833
+ if (!lockfilePath) return void 0;
4834
+ const name = await this.packageName();
4835
+ await exec3("cargo", ["update", "--package", name], {
4836
+ nodeOptions: { cwd: path2.dirname(lockfilePath) }
4837
+ });
4838
+ return lockfilePath;
4839
+ }
4840
+ async findLockfile() {
4841
+ let dir = this.packagePath;
4842
+ const { root } = path2.parse(dir);
4843
+ while (dir !== root) {
4844
+ const candidate = path2.join(dir, "Cargo.lock");
4845
+ try {
4846
+ if ((await stat2(candidate)).isFile()) return candidate;
4847
+ } catch {
4848
+ }
4849
+ dir = path2.dirname(dir);
4850
+ }
4851
+ return void 0;
4852
+ }
4830
4853
  async dependencies() {
4831
4854
  const cargo = await this.readCargoToml();
4832
4855
  const deps = [];
@@ -5134,10 +5157,19 @@ async function replaceVersion(version2, packages) {
5134
5157
  { cause: error }
5135
5158
  );
5136
5159
  }
5137
- return path3.join(pkg.path, "Cargo.toml");
5160
+ let lockfilePath;
5161
+ try {
5162
+ lockfilePath = await eco.syncLockfile();
5163
+ } catch (error) {
5164
+ throw new AbstractError(
5165
+ `Failed to sync Cargo.lock at ${pkg.path}: ${error instanceof Error ? error.message : error}`,
5166
+ { cause: error }
5167
+ );
5168
+ }
5169
+ return [path3.join(pkg.path, "Cargo.toml"), lockfilePath];
5138
5170
  })
5139
5171
  ]);
5140
- return results.filter((v) => v);
5172
+ return [...new Set(results.flat().filter((v) => !!v))];
5141
5173
  }
5142
5174
 
5143
5175
  // src/utils/package-manager.ts
@@ -5298,7 +5330,7 @@ function injectTokensToEnv(tokens) {
5298
5330
 
5299
5331
  // src/registry/crates.ts
5300
5332
  import path5 from "node:path";
5301
- import { exec as exec3, NonZeroExitError } from "tinyexec";
5333
+ import { exec as exec4, NonZeroExitError } from "tinyexec";
5302
5334
 
5303
5335
  // src/registry/registry.ts
5304
5336
  var Registry = class {
@@ -5338,7 +5370,7 @@ var CratesRegistry = class extends Registry {
5338
5370
  }
5339
5371
  async isInstalled() {
5340
5372
  try {
5341
- await exec3("cargo", ["--version"]);
5373
+ await exec4("cargo", ["--version"]);
5342
5374
  return true;
5343
5375
  } catch {
5344
5376
  return false;
@@ -5379,7 +5411,7 @@ var CratesRegistry = class extends Registry {
5379
5411
  if (manifestDir) {
5380
5412
  args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
5381
5413
  }
5382
- await exec3("cargo", args, { throwOnError: true });
5414
+ await exec4("cargo", args, { throwOnError: true });
5383
5415
  return true;
5384
5416
  } catch (error) {
5385
5417
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
@@ -5394,7 +5426,7 @@ ${stderr}` : "Failed to run `cargo publish`";
5394
5426
  if (manifestDir) {
5395
5427
  args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
5396
5428
  }
5397
- await exec3("cargo", args, { throwOnError: true });
5429
+ await exec4("cargo", args, { throwOnError: true });
5398
5430
  } catch (error) {
5399
5431
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5400
5432
  const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
@@ -5492,7 +5524,7 @@ var cratesPublishTasks = createCratesPublishTask();
5492
5524
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5493
5525
 
5494
5526
  // src/registry/jsr.ts
5495
- import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5527
+ import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5496
5528
 
5497
5529
  // src/utils/package-name.ts
5498
5530
  import { builtinModules } from "node:module";
@@ -5559,7 +5591,7 @@ var JsrRegisry = class extends Registry {
5559
5591
  this.client = new JsrClient(getApiEndpoint(this.registry));
5560
5592
  }
5561
5593
  async jsr(args) {
5562
- const { stdout } = await exec4("jsr", args, { throwOnError: true });
5594
+ const { stdout } = await exec5("jsr", args, { throwOnError: true });
5563
5595
  return stdout;
5564
5596
  }
5565
5597
  async isInstalled() {
@@ -5575,7 +5607,7 @@ var JsrRegisry = class extends Registry {
5575
5607
  }
5576
5608
  async ping() {
5577
5609
  try {
5578
- const { stdout } = await exec4(
5610
+ const { stdout } = await exec5(
5579
5611
  "ping",
5580
5612
  [new URL(this.registry).hostname, "-c", "1"],
5581
5613
  { throwOnError: true }
@@ -5590,7 +5622,7 @@ var JsrRegisry = class extends Registry {
5590
5622
  }
5591
5623
  async publish() {
5592
5624
  try {
5593
- await exec4(
5625
+ await exec5(
5594
5626
  "jsr",
5595
5627
  ["publish", "--allow-dirty", "--token", `${JsrClient.token}`],
5596
5628
  {
@@ -5621,7 +5653,7 @@ ${stderr}` : ""}`,
5621
5653
  }
5622
5654
  async dryRunPublish() {
5623
5655
  try {
5624
- await exec4(
5656
+ await exec5(
5625
5657
  "jsr",
5626
5658
  [
5627
5659
  "publish",
@@ -5879,7 +5911,9 @@ async function jsrRegistry() {
5879
5911
  }
5880
5912
 
5881
5913
  // src/registry/npm.ts
5882
- import { exec as exec5, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
5914
+ import { tmpdir } from "node:os";
5915
+ import { join } from "node:path";
5916
+ import { exec as exec6, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
5883
5917
  var NpmError = class extends AbstractError {
5884
5918
  constructor() {
5885
5919
  super(...arguments);
@@ -5892,7 +5926,7 @@ var NpmRegistry = class extends Registry {
5892
5926
  __publicField(this, "registry", "https://registry.npmjs.org");
5893
5927
  }
5894
5928
  async npm(args) {
5895
- const { stdout } = await exec5("npm", args, { throwOnError: true });
5929
+ const { stdout } = await exec6("npm", args, { throwOnError: true });
5896
5930
  return stdout;
5897
5931
  }
5898
5932
  async isInstalled() {
@@ -6003,7 +6037,7 @@ var NpmRegistry = class extends Registry {
6003
6037
  }
6004
6038
  async ping() {
6005
6039
  try {
6006
- await exec5("npm", ["ping"], { throwOnError: true });
6040
+ await exec6("npm", ["ping"], { throwOnError: true });
6007
6041
  return true;
6008
6042
  } catch (error) {
6009
6043
  throw new NpmError("Failed to run `npm ping`", { cause: error });
@@ -6034,11 +6068,22 @@ var NpmRegistry = class extends Registry {
6034
6068
  }
6035
6069
  async dryRunPublish() {
6036
6070
  try {
6037
- await this.npm(["publish", "--dry-run"]);
6038
- } catch (error) {
6039
- throw new NpmError("Failed to run `npm publish --dry-run`", {
6040
- cause: error
6071
+ await exec6("npm", ["publish", "--dry-run"], {
6072
+ throwOnError: true,
6073
+ nodeOptions: {
6074
+ env: {
6075
+ ...process.env,
6076
+ npm_config_cache: join(tmpdir(), "pubm-npm-cache")
6077
+ }
6078
+ }
6041
6079
  });
6080
+ } catch (error) {
6081
+ const stderr = error instanceof NonZeroExitError3 ? error.output?.stderr : void 0;
6082
+ throw new NpmError(
6083
+ `Failed to run \`npm publish --dry-run\`${stderr ? `
6084
+ ${stderr}` : ""}`,
6085
+ { cause: error }
6086
+ );
6042
6087
  }
6043
6088
  }
6044
6089
  async twoFactorAuthMode() {
@@ -6512,8 +6557,9 @@ var npmPublishTasks = {
6512
6557
  };
6513
6558
 
6514
6559
  // src/tasks/preflight.ts
6560
+ import { createHash as createHash2 } from "node:crypto";
6515
6561
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter4 } from "@listr2/prompt-adapter-enquirer";
6516
- import { exec as exec6 } from "tinyexec";
6562
+ import { exec as exec7 } from "tinyexec";
6517
6563
  var PreflightError = class extends AbstractError {
6518
6564
  constructor() {
6519
6565
  super(...arguments);
@@ -6540,13 +6586,29 @@ async function syncGhSecrets(tokens) {
6540
6586
  for (const [registry, token] of Object.entries(tokens)) {
6541
6587
  const config = TOKEN_CONFIG[registry];
6542
6588
  if (!config) continue;
6543
- await exec6("gh", ["secret", "set", config.ghSecretName], {
6544
- throwOnError: true,
6545
- nodeOptions: { input: token }
6589
+ const result = exec7("gh", ["secret", "set", config.ghSecretName], {
6590
+ throwOnError: true
6546
6591
  });
6592
+ const proc = result.process;
6593
+ if (proc?.stdin) {
6594
+ proc.stdin.end(token);
6595
+ }
6596
+ await result;
6547
6597
  }
6548
6598
  }
6599
+ function tokensSyncHash(tokens) {
6600
+ const sorted = Object.entries(tokens).sort(([a2], [b]) => a2.localeCompare(b));
6601
+ return createHash2("sha256").update(JSON.stringify(sorted)).digest("hex").slice(0, 16);
6602
+ }
6603
+ var SYNC_HASH_DB_KEY = "gh-secrets-sync-hash";
6549
6604
  async function promptGhSecretsSync(tokens, task) {
6605
+ const db = new Db();
6606
+ const currentHash = tokensSyncHash(tokens);
6607
+ const storedHash = db.get(SYNC_HASH_DB_KEY);
6608
+ if (storedHash === currentHash) {
6609
+ task.output = "Tokens already synced to GitHub Secrets.";
6610
+ return;
6611
+ }
6550
6612
  const shouldSync = await task.prompt(ListrEnquirerPromptAdapter4).run({
6551
6613
  type: "toggle",
6552
6614
  message: "Sync tokens to GitHub Secrets?",
@@ -6557,6 +6619,7 @@ async function promptGhSecretsSync(tokens, task) {
6557
6619
  task.output = "Syncing tokens to GitHub Secrets...";
6558
6620
  try {
6559
6621
  await syncGhSecrets(tokens);
6622
+ db.set(SYNC_HASH_DB_KEY, currentHash);
6560
6623
  task.output = "Tokens synced to GitHub Secrets.";
6561
6624
  } catch (error) {
6562
6625
  throw new PreflightError(
@@ -6717,10 +6780,10 @@ var prerequisitesCheckTask = (options) => {
6717
6780
  import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter6 } from "@listr2/prompt-adapter-enquirer";
6718
6781
 
6719
6782
  // src/registry/custom-registry.ts
6720
- import { exec as exec7 } from "tinyexec";
6783
+ import { exec as exec8 } from "tinyexec";
6721
6784
  var CustomRegistry = class extends NpmRegistry {
6722
6785
  async npm(args) {
6723
- const { stdout } = await exec7(
6786
+ const { stdout } = await exec8(
6724
6787
  "npm",
6725
6788
  args.concat("--registry", this.registry),
6726
6789
  { throwOnError: true }
@@ -7029,14 +7092,14 @@ async function run(options) {
7029
7092
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7030
7093
  concurrent: true
7031
7094
  })
7032
- } : options.preflight ? [
7095
+ } : [
7033
7096
  {
7034
7097
  skip: options.skipTests,
7035
7098
  title: "Running tests",
7036
7099
  task: async (ctx2) => {
7037
7100
  const packageManager = await getPackageManager();
7038
7101
  try {
7039
- await exec8(packageManager, ["run", ctx2.testScript], {
7102
+ await exec9(packageManager, ["run", ctx2.testScript], {
7040
7103
  throwOnError: true
7041
7104
  });
7042
7105
  } catch (error) {
@@ -7053,48 +7116,7 @@ async function run(options) {
7053
7116
  task: async (ctx2) => {
7054
7117
  const packageManager = await getPackageManager();
7055
7118
  try {
7056
- await exec8(packageManager, ["run", ctx2.buildScript], {
7057
- throwOnError: true
7058
- });
7059
- } catch (error) {
7060
- throw new AbstractError(
7061
- `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
7062
- { cause: error }
7063
- );
7064
- }
7065
- }
7066
- },
7067
- {
7068
- title: "Validating publish (dry-run)",
7069
- task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7070
- concurrent: true
7071
- })
7072
- }
7073
- ] : [
7074
- {
7075
- skip: options.skipTests,
7076
- title: "Running tests",
7077
- task: async (ctx2) => {
7078
- const packageManager = await getPackageManager();
7079
- try {
7080
- await exec8(packageManager, ["run", ctx2.testScript], {
7081
- throwOnError: true
7082
- });
7083
- } catch (error) {
7084
- throw new AbstractError(
7085
- `Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
7086
- { cause: error }
7087
- );
7088
- }
7089
- }
7090
- },
7091
- {
7092
- skip: options.skipBuild,
7093
- title: "Building the project",
7094
- task: async (ctx2) => {
7095
- const packageManager = await getPackageManager();
7096
- try {
7097
- await exec8(packageManager, ["run", ctx2.buildScript], {
7119
+ await exec9(packageManager, ["run", ctx2.buildScript], {
7098
7120
  throwOnError: true
7099
7121
  });
7100
7122
  } catch (error) {
@@ -7154,12 +7176,19 @@ async function run(options) {
7154
7176
  }
7155
7177
  },
7156
7178
  {
7157
- skip: (ctx2) => options.skipPublish || !!ctx2.preview,
7179
+ skip: (ctx2) => options.skipPublish || !!ctx2.preview || options.preflight,
7158
7180
  title: "Publishing",
7159
7181
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
7160
7182
  concurrent: true
7161
7183
  })
7162
7184
  },
7185
+ {
7186
+ skip: !options.preflight,
7187
+ title: "Validating publish (dry-run)",
7188
+ task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
7189
+ concurrent: true
7190
+ })
7191
+ },
7163
7192
  {
7164
7193
  title: "Pushing tags to GitHub",
7165
7194
  skip: (ctx2) => !!ctx2.preview,
@@ -7614,18 +7643,18 @@ import micromatch from "micromatch";
7614
7643
 
7615
7644
  // src/monorepo/workspace.ts
7616
7645
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
7617
- import { join } from "node:path";
7646
+ import { join as join2 } from "node:path";
7618
7647
  import { parse as parse2 } from "yaml";
7619
7648
  function detectWorkspace(cwd) {
7620
7649
  const root = cwd ?? process.cwd();
7621
- const pnpmWorkspacePath = join(root, "pnpm-workspace.yaml");
7650
+ const pnpmWorkspacePath = join2(root, "pnpm-workspace.yaml");
7622
7651
  if (existsSync3(pnpmWorkspacePath)) {
7623
7652
  const content = readFileSync3(pnpmWorkspacePath, "utf-8");
7624
7653
  const parsed = parse2(content);
7625
7654
  const packages = parsed?.packages ?? [];
7626
7655
  return { type: "pnpm", patterns: packages };
7627
7656
  }
7628
- const packageJsonPath = join(root, "package.json");
7657
+ const packageJsonPath = join2(root, "package.json");
7629
7658
  if (existsSync3(packageJsonPath)) {
7630
7659
  const content = readFileSync3(packageJsonPath, "utf-8");
7631
7660
  const pkg = JSON.parse(content);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubm",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "engines": {
5
5
  "node": ">=18",
6
6
  "git": ">=2.11.0"
@@ -33,7 +33,7 @@
33
33
  "typecheck": "tsc --noEmit",
34
34
  "test": "vitest --run",
35
35
  "coverage": "vitest --run --coverage",
36
- "release": "pnpm build && node bin/cli.js --no-publish",
36
+ "release": "pnpm build && node bin/cli.js --preflight",
37
37
  "ci:release": "node bin/cli.js --publish-only"
38
38
  },
39
39
  "dependencies": {