pubm 0.2.7 → 0.2.9

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.
Files changed (4) hide show
  1. package/bin/cli.js +302 -114
  2. package/dist/index.cjs +435 -247
  3. package/dist/index.js +418 -230
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4455,7 +4455,127 @@ import SemVer from "semver";
4455
4455
  import { isCI as isCI2 } from "std-env";
4456
4456
  import { exec as exec9 } from "tinyexec";
4457
4457
 
4458
+ // src/ecosystem/rust.ts
4459
+ import { readFile, stat as stat2, writeFile } from "node:fs/promises";
4460
+ import path2 from "node:path";
4461
+ import { parse, stringify } from "smol-toml";
4462
+ import { exec as exec2 } from "tinyexec";
4463
+
4464
+ // src/ecosystem/ecosystem.ts
4465
+ var Ecosystem = class {
4466
+ constructor(packagePath) {
4467
+ this.packagePath = packagePath;
4468
+ }
4469
+ };
4470
+
4471
+ // src/ecosystem/rust.ts
4472
+ var RustEcosystem = class extends Ecosystem {
4473
+ static async detect(packagePath) {
4474
+ try {
4475
+ return (await stat2(path2.join(packagePath, "Cargo.toml"))).isFile();
4476
+ } catch {
4477
+ return false;
4478
+ }
4479
+ }
4480
+ async readCargoToml() {
4481
+ const raw = await readFile(
4482
+ path2.join(this.packagePath, "Cargo.toml"),
4483
+ "utf-8"
4484
+ );
4485
+ return parse(raw);
4486
+ }
4487
+ async packageName() {
4488
+ const cargo = await this.readCargoToml();
4489
+ const pkg = cargo.package;
4490
+ return pkg.name;
4491
+ }
4492
+ async readVersion() {
4493
+ const cargo = await this.readCargoToml();
4494
+ const pkg = cargo.package;
4495
+ return pkg.version;
4496
+ }
4497
+ async writeVersion(newVersion) {
4498
+ const filePath = path2.join(this.packagePath, "Cargo.toml");
4499
+ const raw = await readFile(filePath, "utf-8");
4500
+ const cargo = parse(raw);
4501
+ const pkg = cargo.package;
4502
+ pkg.version = newVersion;
4503
+ await writeFile(filePath, stringify(cargo));
4504
+ }
4505
+ /**
4506
+ * Update the `version` field of dependencies that match sibling crate names.
4507
+ * This ensures `cargo publish` works when crates depend on each other via path.
4508
+ */
4509
+ async updateSiblingDependencyVersions(siblingVersions) {
4510
+ const filePath = path2.join(this.packagePath, "Cargo.toml");
4511
+ const raw = await readFile(filePath, "utf-8");
4512
+ const cargo = parse(raw);
4513
+ let modified = false;
4514
+ for (const section of ["dependencies", "build-dependencies"]) {
4515
+ const sectionData = cargo[section];
4516
+ if (!sectionData) continue;
4517
+ for (const [depName, depValue] of Object.entries(sectionData)) {
4518
+ if (typeof depValue === "object" && depValue !== null && "path" in depValue && siblingVersions.has(depName)) {
4519
+ const dep = depValue;
4520
+ dep.version = siblingVersions.get(depName);
4521
+ modified = true;
4522
+ }
4523
+ }
4524
+ }
4525
+ if (modified) {
4526
+ await writeFile(filePath, stringify(cargo));
4527
+ }
4528
+ return modified;
4529
+ }
4530
+ async syncLockfile() {
4531
+ const lockfilePath = await this.findLockfile();
4532
+ if (!lockfilePath) return void 0;
4533
+ const name = await this.packageName();
4534
+ await exec2("cargo", ["update", "--package", name], {
4535
+ nodeOptions: { cwd: path2.dirname(lockfilePath) }
4536
+ });
4537
+ return lockfilePath;
4538
+ }
4539
+ async findLockfile() {
4540
+ let dir = this.packagePath;
4541
+ const { root } = path2.parse(dir);
4542
+ while (dir !== root) {
4543
+ const candidate = path2.join(dir, "Cargo.lock");
4544
+ try {
4545
+ if ((await stat2(candidate)).isFile()) return candidate;
4546
+ } catch {
4547
+ }
4548
+ dir = path2.dirname(dir);
4549
+ }
4550
+ return void 0;
4551
+ }
4552
+ async dependencies() {
4553
+ const cargo = await this.readCargoToml();
4554
+ const deps = [];
4555
+ for (const section of ["dependencies", "build-dependencies"]) {
4556
+ const sectionData = cargo[section];
4557
+ if (sectionData) {
4558
+ deps.push(...Object.keys(sectionData));
4559
+ }
4560
+ }
4561
+ return deps;
4562
+ }
4563
+ manifestFiles() {
4564
+ return ["Cargo.toml"];
4565
+ }
4566
+ defaultTestCommand() {
4567
+ return "cargo test";
4568
+ }
4569
+ defaultBuildCommand() {
4570
+ return "cargo build --release";
4571
+ }
4572
+ supportedRegistries() {
4573
+ return ["crates"];
4574
+ }
4575
+ };
4576
+
4458
4577
  // src/error.ts
4578
+ import { NonZeroExitError } from "tinyexec";
4459
4579
  var AbstractError = class extends Error {
4460
4580
  constructor(message, { cause } = {}) {
4461
4581
  super(message, { cause });
@@ -4466,20 +4586,49 @@ var AbstractError = class extends Error {
4466
4586
  function replaceCode(code) {
4467
4587
  return code.replace(/`([^`].+)`/g, color.bold(color.underline("$1")));
4468
4588
  }
4589
+ function formatStderr(stderr) {
4590
+ return stderr.split("\n").map((line) => ` ${color.dim("\u2502")} ${line}`).join("\n");
4591
+ }
4592
+ function isNoisyCause(cause) {
4593
+ if (cause instanceof NonZeroExitError) return true;
4594
+ if (cause instanceof Error && /Process exited with non-zero status/i.test(cause.message))
4595
+ return true;
4596
+ return false;
4597
+ }
4469
4598
  function formatError(error) {
4470
4599
  if (!(error instanceof Error)) return `${error}`;
4471
- const message = typeof error.message === "string" ? replaceCode(error.message) : (
4600
+ const rawMessage = typeof error.message === "string" ? error.message : (
4472
4601
  /* v8 ignore next */
4473
- formatError(error)
4602
+ String(error)
4474
4603
  );
4475
- let stringifyError = `${color.bgRed(` ${error.name} `)}${color.reset("")} ${message}
4604
+ const newlineIndex = rawMessage.indexOf("\n");
4605
+ let summary;
4606
+ let detail;
4607
+ if (newlineIndex !== -1) {
4608
+ summary = rawMessage.slice(0, newlineIndex);
4609
+ detail = rawMessage.slice(newlineIndex + 1);
4610
+ } else {
4611
+ summary = rawMessage;
4612
+ }
4613
+ let result = `${color.bgRed(` ${error.name} `)}${color.reset("")} ${replaceCode(summary)}
4614
+ `;
4615
+ if (detail) {
4616
+ result += `
4617
+ ${formatStderr(detail)}
4476
4618
  `;
4477
- stringifyError += error.stack?.split("\n").slice(1).join("\n").replace(/at/g, color.dim("at")).replace(/\(([^(].+)\)/g, `(${color.blue("$1")})`);
4478
- if (error.cause) {
4479
- stringifyError += "\n\nCaused: ";
4480
- stringifyError += formatError(error.cause);
4481
4619
  }
4482
- return stringifyError;
4620
+ if (process.env.DEBUG === "pubm" && error.stack) {
4621
+ result += error.stack.split("\n").slice(1).join("\n").replace(/at/g, color.dim("at")).replace(/\(([^(].+)\)/g, `(${color.blue("$1")})`);
4622
+ }
4623
+ if (error.cause && !isNoisyCause(error.cause)) {
4624
+ const causeMsg = error.cause instanceof Error ? error.cause.message : String(error.cause);
4625
+ if (causeMsg !== summary) {
4626
+ result += `
4627
+ ${color.dim("Caused by:")} `;
4628
+ result += formatError(error.cause);
4629
+ }
4630
+ }
4631
+ return result;
4483
4632
  }
4484
4633
  function consoleError(error) {
4485
4634
  let errorText = "\n";
@@ -4496,7 +4645,7 @@ function consoleError(error) {
4496
4645
 
4497
4646
  // src/git.ts
4498
4647
  import semver from "semver";
4499
- import { exec as exec2 } from "tinyexec";
4648
+ import { exec as exec3 } from "tinyexec";
4500
4649
  var GitError = class extends AbstractError {
4501
4650
  constructor() {
4502
4651
  super(...arguments);
@@ -4505,7 +4654,7 @@ var GitError = class extends AbstractError {
4505
4654
  };
4506
4655
  var Git = class {
4507
4656
  async git(args) {
4508
- const { stdout } = await exec2("git", args, { throwOnError: true });
4657
+ const { stdout } = await exec3("git", args, { throwOnError: true });
4509
4658
  return stdout;
4510
4659
  }
4511
4660
  async userName() {
@@ -4759,7 +4908,7 @@ var Git = class {
4759
4908
  async push(options) {
4760
4909
  const args = ["push", options].filter((v) => v);
4761
4910
  try {
4762
- const { stderr } = await exec2("git", args, { throwOnError: true });
4911
+ const { stderr } = await exec3("git", args, { throwOnError: true });
4763
4912
  if (`${stderr}`.includes("GH006")) {
4764
4913
  return false;
4765
4914
  }
@@ -4781,125 +4930,6 @@ function link2(text, url) {
4781
4930
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
4782
4931
  }
4783
4932
 
4784
- // src/ecosystem/rust.ts
4785
- import { readFile, stat as stat2, writeFile } from "node:fs/promises";
4786
- import path2 from "node:path";
4787
- import { parse, stringify } from "smol-toml";
4788
- import { exec as exec3 } from "tinyexec";
4789
-
4790
- // src/ecosystem/ecosystem.ts
4791
- var Ecosystem = class {
4792
- constructor(packagePath) {
4793
- this.packagePath = packagePath;
4794
- }
4795
- };
4796
-
4797
- // src/ecosystem/rust.ts
4798
- var RustEcosystem = class extends Ecosystem {
4799
- static async detect(packagePath) {
4800
- try {
4801
- return (await stat2(path2.join(packagePath, "Cargo.toml"))).isFile();
4802
- } catch {
4803
- return false;
4804
- }
4805
- }
4806
- async readCargoToml() {
4807
- const raw = await readFile(
4808
- path2.join(this.packagePath, "Cargo.toml"),
4809
- "utf-8"
4810
- );
4811
- return parse(raw);
4812
- }
4813
- async packageName() {
4814
- const cargo = await this.readCargoToml();
4815
- const pkg = cargo.package;
4816
- return pkg.name;
4817
- }
4818
- async readVersion() {
4819
- const cargo = await this.readCargoToml();
4820
- const pkg = cargo.package;
4821
- return pkg.version;
4822
- }
4823
- async writeVersion(newVersion) {
4824
- const filePath = path2.join(this.packagePath, "Cargo.toml");
4825
- const raw = await readFile(filePath, "utf-8");
4826
- const cargo = parse(raw);
4827
- const pkg = cargo.package;
4828
- pkg.version = newVersion;
4829
- await writeFile(filePath, stringify(cargo));
4830
- }
4831
- /**
4832
- * Update the `version` field of dependencies that match sibling crate names.
4833
- * This ensures `cargo publish` works when crates depend on each other via path.
4834
- */
4835
- async updateSiblingDependencyVersions(siblingVersions) {
4836
- const filePath = path2.join(this.packagePath, "Cargo.toml");
4837
- const raw = await readFile(filePath, "utf-8");
4838
- const cargo = parse(raw);
4839
- let modified = false;
4840
- for (const section of ["dependencies", "build-dependencies"]) {
4841
- const sectionData = cargo[section];
4842
- if (!sectionData) continue;
4843
- for (const [depName, depValue] of Object.entries(sectionData)) {
4844
- if (typeof depValue === "object" && depValue !== null && "path" in depValue && siblingVersions.has(depName)) {
4845
- const dep = depValue;
4846
- dep.version = siblingVersions.get(depName);
4847
- modified = true;
4848
- }
4849
- }
4850
- }
4851
- if (modified) {
4852
- await writeFile(filePath, stringify(cargo));
4853
- }
4854
- return modified;
4855
- }
4856
- async syncLockfile() {
4857
- const lockfilePath = await this.findLockfile();
4858
- if (!lockfilePath) return void 0;
4859
- const name = await this.packageName();
4860
- await exec3("cargo", ["update", "--package", name], {
4861
- nodeOptions: { cwd: path2.dirname(lockfilePath) }
4862
- });
4863
- return lockfilePath;
4864
- }
4865
- async findLockfile() {
4866
- let dir = this.packagePath;
4867
- const { root } = path2.parse(dir);
4868
- while (dir !== root) {
4869
- const candidate = path2.join(dir, "Cargo.lock");
4870
- try {
4871
- if ((await stat2(candidate)).isFile()) return candidate;
4872
- } catch {
4873
- }
4874
- dir = path2.dirname(dir);
4875
- }
4876
- return void 0;
4877
- }
4878
- async dependencies() {
4879
- const cargo = await this.readCargoToml();
4880
- const deps = [];
4881
- for (const section of ["dependencies", "build-dependencies"]) {
4882
- const sectionData = cargo[section];
4883
- if (sectionData) {
4884
- deps.push(...Object.keys(sectionData));
4885
- }
4886
- }
4887
- return deps;
4888
- }
4889
- manifestFiles() {
4890
- return ["Cargo.toml"];
4891
- }
4892
- defaultTestCommand() {
4893
- return "cargo test";
4894
- }
4895
- defaultBuildCommand() {
4896
- return "cargo build --release";
4897
- }
4898
- supportedRegistries() {
4899
- return ["crates"];
4900
- }
4901
- };
4902
-
4903
4933
  // src/utils/crate-graph.ts
4904
4934
  async function sortCratesByDependencyOrder(cratePaths) {
4905
4935
  if (cratePaths.length <= 1) return cratePaths;
@@ -4952,12 +4982,19 @@ var rollbacks = [];
4952
4982
  function addRollback(rollback2, context) {
4953
4983
  rollbacks.push({ fn: rollback2, ctx: context });
4954
4984
  }
4985
+ function rollbackLog(message) {
4986
+ console.log(` ${color.yellow("\u21A9")} ${message}`);
4987
+ }
4988
+ function rollbackError(message) {
4989
+ console.error(` ${color.red("\u2717")} ${message}`);
4990
+ }
4955
4991
  var called = false;
4956
4992
  async function rollback() {
4957
4993
  if (called) return void 0;
4958
4994
  called = true;
4959
4995
  if (rollbacks.length <= 0) return void 0;
4960
- console.log("Rollback...");
4996
+ console.log(`
4997
+ ${color.yellow("\u27F2")} ${color.yellow("Rolling back...")}`);
4961
4998
  const results = await Promise.allSettled(
4962
4999
  rollbacks.map(({ fn, ctx }) => fn(ctx))
4963
5000
  );
@@ -4966,15 +5003,15 @@ async function rollback() {
4966
5003
  );
4967
5004
  if (failures.length > 0) {
4968
5005
  for (const failure of failures) {
4969
- console.error(
4970
- `Rollback operation failed: ${failure.reason instanceof Error ? failure.reason.message : failure.reason}`
5006
+ rollbackError(
5007
+ failure.reason instanceof Error ? failure.reason.message : failure.reason
4971
5008
  );
4972
5009
  }
4973
5010
  console.log(
4974
- "Rollback completed with errors. Some operations may require manual recovery."
5011
+ `${color.red("\u2717")} ${color.red("Rollback completed with errors.")} Some operations may require manual recovery.`
4975
5012
  );
4976
5013
  } else {
4977
- console.log("Rollback completed");
5014
+ console.log(`${color.green("\u2713")} Rollback completed`);
4978
5015
  }
4979
5016
  }
4980
5017
 
@@ -5381,7 +5418,7 @@ function injectTokensToEnv(tokens) {
5381
5418
 
5382
5419
  // src/registry/crates.ts
5383
5420
  import path5 from "node:path";
5384
- import { exec as exec4, NonZeroExitError } from "tinyexec";
5421
+ import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5385
5422
 
5386
5423
  // src/registry/registry.ts
5387
5424
  var Registry = class {
@@ -5401,6 +5438,14 @@ var CratesError = class extends AbstractError {
5401
5438
  }
5402
5439
  };
5403
5440
  var USER_AGENT = "pubm (https://github.com/syi0808/pubm)";
5441
+ function cleanCargoStderr(stderr) {
5442
+ return stderr.split("\n").filter((line) => {
5443
+ const trimmed = line.trim();
5444
+ if (trimmed === "Updating crates.io index") return false;
5445
+ if (trimmed === "") return false;
5446
+ return true;
5447
+ }).join("\n");
5448
+ }
5404
5449
  var CratesRegistry = class extends Registry {
5405
5450
  constructor() {
5406
5451
  super(...arguments);
@@ -5465,26 +5510,40 @@ var CratesRegistry = class extends Registry {
5465
5510
  await exec4("cargo", args, { throwOnError: true });
5466
5511
  return true;
5467
5512
  } catch (error) {
5468
- const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5513
+ const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5469
5514
  const message = stderr ? `Failed to run \`cargo publish\`:
5470
- ${stderr}` : "Failed to run `cargo publish`";
5515
+ ${cleanCargoStderr(stderr)}` : "Failed to run `cargo publish`";
5471
5516
  throw new CratesError(message, { cause: error });
5472
5517
  }
5473
5518
  }
5474
5519
  async dryRunPublish(manifestDir) {
5475
5520
  try {
5476
- const args = ["publish", "--dry-run", "--no-verify"];
5521
+ const args = ["publish", "--dry-run"];
5477
5522
  if (manifestDir) {
5478
5523
  args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
5479
5524
  }
5480
5525
  await exec4("cargo", args, { throwOnError: true });
5481
5526
  } catch (error) {
5482
- const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5527
+ const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5483
5528
  const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
5484
- ${stderr}` : "Failed to run `cargo publish --dry-run`";
5529
+ ${cleanCargoStderr(stderr)}` : "Failed to run `cargo publish --dry-run`";
5485
5530
  throw new CratesError(message, { cause: error });
5486
5531
  }
5487
5532
  }
5533
+ async isVersionPublished(version2) {
5534
+ try {
5535
+ const response = await fetch(
5536
+ `${this.registry}/api/v1/crates/${this.packageName}/${version2}`,
5537
+ { headers: this.headers }
5538
+ );
5539
+ return response.ok;
5540
+ } catch (error) {
5541
+ throw new CratesError(
5542
+ `Failed to check version ${version2} for '${this.packageName}' on crates.io`,
5543
+ { cause: error }
5544
+ );
5545
+ }
5546
+ }
5488
5547
  async isPublished() {
5489
5548
  try {
5490
5549
  const response = await fetch(
@@ -5561,10 +5620,24 @@ function createCratesPublishTask(packagePath) {
5561
5620
  const label = packagePath ? ` (${packagePath})` : "";
5562
5621
  return {
5563
5622
  title: `Publishing to crates.io${label}`,
5564
- task: async () => {
5623
+ task: async (ctx, task) => {
5565
5624
  const packageName = await getCrateName(packagePath);
5566
5625
  const registry = new CratesRegistry(packageName);
5567
- await registry.publish(packagePath);
5626
+ if (await registry.isVersionPublished(ctx.version)) {
5627
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
5628
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
5629
+ return task.skip();
5630
+ }
5631
+ try {
5632
+ await registry.publish(packagePath);
5633
+ } catch (error) {
5634
+ if (error instanceof Error && error.message.includes("is already uploaded")) {
5635
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
5636
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
5637
+ return task.skip();
5638
+ }
5639
+ throw error;
5640
+ }
5568
5641
  }
5569
5642
  };
5570
5643
  }
@@ -5575,7 +5648,7 @@ var cratesPublishTasks = createCratesPublishTask();
5575
5648
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5576
5649
 
5577
5650
  // src/registry/jsr.ts
5578
- import { exec as exec5, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5651
+ import { exec as exec5, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
5579
5652
 
5580
5653
  // src/utils/package-name.ts
5581
5654
  import { builtinModules } from "node:module";
@@ -5683,7 +5756,7 @@ var JsrRegisry = class extends Registry {
5683
5756
  this.packageCreationUrls = void 0;
5684
5757
  return true;
5685
5758
  } catch (error) {
5686
- const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5759
+ const stderr = error instanceof NonZeroExitError3 ? error.output?.stderr : void 0;
5687
5760
  if (stderr?.includes("don't exist")) {
5688
5761
  const urls = [...stderr.matchAll(/https:\/\/jsr\.io\/new\S+/g)].map(
5689
5762
  (m) => m[0]
@@ -5735,6 +5808,20 @@ ${stderr}` : ""}`,
5735
5808
  );
5736
5809
  }
5737
5810
  }
5811
+ async isVersionPublished(version2) {
5812
+ try {
5813
+ const [scope, name] = getScopeAndName(this.packageName);
5814
+ const response = await fetch(
5815
+ `${this.registry}/@${scope}/${name}/${version2}`
5816
+ );
5817
+ return response.status === 200;
5818
+ } catch (error) {
5819
+ throw new JsrError(
5820
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
5821
+ { cause: error }
5822
+ );
5823
+ }
5824
+ }
5738
5825
  async hasPermission() {
5739
5826
  return this.client.scopePermission(`${getScope(this.packageName)}`) !== null;
5740
5827
  }
@@ -5964,7 +6051,7 @@ async function jsrRegistry() {
5964
6051
  // src/registry/npm.ts
5965
6052
  import { tmpdir } from "node:os";
5966
6053
  import { join } from "node:path";
5967
- import { exec as exec6, NonZeroExitError as NonZeroExitError3 } from "tinyexec";
6054
+ import { exec as exec6, NonZeroExitError as NonZeroExitError4 } from "tinyexec";
5968
6055
  var NpmError = class extends AbstractError {
5969
6056
  constructor() {
5970
6057
  super(...arguments);
@@ -6009,6 +6096,19 @@ var NpmRegistry = class extends Registry {
6009
6096
  );
6010
6097
  }
6011
6098
  }
6099
+ async isVersionPublished(version2) {
6100
+ try {
6101
+ const response = await fetch(
6102
+ `${this.registry}/${this.packageName}/${version2}`
6103
+ );
6104
+ return response.status === 200;
6105
+ } catch (error) {
6106
+ throw new NpmError(
6107
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
6108
+ { cause: error }
6109
+ );
6110
+ }
6111
+ }
6012
6112
  async userName() {
6013
6113
  try {
6014
6114
  return (await this.npm(["whoami"])).trim();
@@ -6021,7 +6121,7 @@ var NpmRegistry = class extends Registry {
6021
6121
  await this.npm(["whoami"]);
6022
6122
  return true;
6023
6123
  } catch (error) {
6024
- if (error instanceof NonZeroExitError3) {
6124
+ if (error instanceof NonZeroExitError4) {
6025
6125
  return false;
6026
6126
  }
6027
6127
  throw new NpmError("Failed to run `npm whoami`", { cause: error });
@@ -6099,7 +6199,7 @@ var NpmRegistry = class extends Registry {
6099
6199
  await this.npm(["publish", "--provenance", "--access", "public"]);
6100
6200
  return true;
6101
6201
  } catch (error) {
6102
- if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
6202
+ if (error instanceof NonZeroExitError4 && error.output?.stderr.includes("EOTP")) {
6103
6203
  return false;
6104
6204
  }
6105
6205
  throw this.classifyPublishError(error);
@@ -6111,7 +6211,7 @@ var NpmRegistry = class extends Registry {
6111
6211
  await this.npm(args);
6112
6212
  return true;
6113
6213
  } catch (error) {
6114
- if (error instanceof NonZeroExitError3 && error.output?.stderr.includes("EOTP")) {
6214
+ if (error instanceof NonZeroExitError4 && error.output?.stderr.includes("EOTP")) {
6115
6215
  return false;
6116
6216
  }
6117
6217
  throw this.classifyPublishError(error);
@@ -6129,7 +6229,7 @@ var NpmRegistry = class extends Registry {
6129
6229
  }
6130
6230
  });
6131
6231
  } catch (error) {
6132
- const stderr = error instanceof NonZeroExitError3 ? error.output?.stderr : void 0;
6232
+ const stderr = error instanceof NonZeroExitError4 ? error.output?.stderr : void 0;
6133
6233
  throw new NpmError(
6134
6234
  `Failed to run \`npm publish --dry-run\`${stderr ? `
6135
6235
  ${stderr}` : ""}`,
@@ -6156,7 +6256,7 @@ ${stderr}` : ""}`,
6156
6256
  };
6157
6257
  }
6158
6258
  classifyPublishError(error) {
6159
- if (error instanceof NonZeroExitError3) {
6259
+ if (error instanceof NonZeroExitError4) {
6160
6260
  const stderr = error.output?.stderr ?? "";
6161
6261
  if (stderr.includes("EOTP")) {
6162
6262
  return new NpmError("OTP required for publishing", { cause: error });
@@ -6214,20 +6314,30 @@ async function withTokenRetry(registryKey, task, action) {
6214
6314
  }
6215
6315
  var npmDryRunPublishTask = {
6216
6316
  title: "Dry-run npm publish",
6217
- task: async (_, task) => {
6317
+ task: async (ctx, task) => {
6318
+ const npm = await npmRegistry();
6319
+ if (await npm.isVersionPublished(ctx.version)) {
6320
+ task.title = `[SKIPPED] Dry-run npm publish: v${ctx.version} already published`;
6321
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6322
+ return task.skip();
6323
+ }
6218
6324
  task.output = "Running npm publish --dry-run...";
6219
6325
  await withTokenRetry("npm", task, async () => {
6220
- const npm = await npmRegistry();
6221
6326
  await npm.dryRunPublish();
6222
6327
  });
6223
6328
  }
6224
6329
  };
6225
6330
  var jsrDryRunPublishTask = {
6226
6331
  title: "Dry-run jsr publish",
6227
- task: async (_, task) => {
6332
+ task: async (ctx, task) => {
6333
+ const jsr = await jsrRegistry();
6334
+ if (await jsr.isVersionPublished(ctx.version)) {
6335
+ task.title = `[SKIPPED] Dry-run jsr publish: v${ctx.version} already published`;
6336
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6337
+ return task.skip();
6338
+ }
6228
6339
  task.output = "Running jsr publish --dry-run...";
6229
6340
  await withTokenRetry("jsr", task, async () => {
6230
- const jsr = await jsrRegistry();
6231
6341
  await jsr.dryRunPublish();
6232
6342
  });
6233
6343
  }
@@ -6236,17 +6346,58 @@ async function getCrateName2(packagePath) {
6236
6346
  const eco = new RustEcosystem(packagePath ?? process.cwd());
6237
6347
  return await eco.packageName();
6238
6348
  }
6239
- function createCratesDryRunPublishTask(packagePath) {
6349
+ var MISSING_CRATE_PATTERN = /no matching package named `([^`]+)` found/;
6350
+ async function findUnpublishedSiblingDeps(packagePath, siblingCrateNames) {
6351
+ const eco = new RustEcosystem(packagePath ?? process.cwd());
6352
+ const deps = await eco.dependencies();
6353
+ const siblingDeps = deps.filter((d2) => siblingCrateNames.includes(d2));
6354
+ const results = await Promise.all(
6355
+ siblingDeps.map(async (name) => {
6356
+ const registry = new CratesRegistry(name);
6357
+ const published = await registry.isPublished();
6358
+ return published ? null : name;
6359
+ })
6360
+ );
6361
+ return results.filter((name) => name !== null);
6362
+ }
6363
+ function createCratesDryRunPublishTask(packagePath, siblingCrateNames) {
6240
6364
  const label = packagePath ? ` (${packagePath})` : "";
6241
6365
  return {
6242
6366
  title: `Dry-run crates.io publish${label}`,
6243
- task: async (_, task) => {
6367
+ task: async (ctx, task) => {
6368
+ const packageName = await getCrateName2(packagePath);
6369
+ const registry = new CratesRegistry(packageName);
6370
+ if (await registry.isVersionPublished(ctx.version)) {
6371
+ task.title = `[SKIPPED] Dry-run crates.io publish${label}: v${ctx.version} already published`;
6372
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
6373
+ return task.skip();
6374
+ }
6375
+ if (siblingCrateNames?.length) {
6376
+ const unpublished = await findUnpublishedSiblingDeps(
6377
+ packagePath,
6378
+ siblingCrateNames
6379
+ );
6380
+ if (unpublished.length > 0) {
6381
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${unpublished.join("`, `")}\` not yet published]`;
6382
+ return;
6383
+ }
6384
+ }
6244
6385
  task.output = "Running cargo publish --dry-run...";
6245
- await withTokenRetry("crates", task, async () => {
6246
- const packageName = await getCrateName2(packagePath);
6247
- const registry = new CratesRegistry(packageName);
6248
- await registry.dryRunPublish(packagePath);
6249
- });
6386
+ try {
6387
+ await withTokenRetry("crates", task, async () => {
6388
+ const packageName2 = await getCrateName2(packagePath);
6389
+ const registry2 = new CratesRegistry(packageName2);
6390
+ await registry2.dryRunPublish(packagePath);
6391
+ });
6392
+ } catch (error) {
6393
+ const message = error instanceof Error ? error.message : String(error);
6394
+ const match = message.match(MISSING_CRATE_PATTERN);
6395
+ if (match && siblingCrateNames?.includes(match[1])) {
6396
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${match[1]}\` not yet published]`;
6397
+ return;
6398
+ }
6399
+ throw error;
6400
+ }
6250
6401
  }
6251
6402
  };
6252
6403
  }
@@ -6427,48 +6578,62 @@ var jsrPublishTasks = {
6427
6578
  title: "Running jsr publish",
6428
6579
  task: async (ctx, task) => {
6429
6580
  const jsr = await jsrRegistry();
6581
+ if (await jsr.isVersionPublished(ctx.version)) {
6582
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
6583
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6584
+ return task.skip();
6585
+ }
6430
6586
  task.output = "Publishing on jsr...";
6431
- if (!JsrClient.token && !ctx.promptEnabled) {
6432
- const jsrTokenEnv = process8.env.JSR_TOKEN;
6433
- if (!jsrTokenEnv) {
6434
- throw new JsrAvailableError(
6435
- "JSR_TOKEN not found in the environment variables. Please set the token and try again."
6436
- );
6587
+ try {
6588
+ if (!JsrClient.token && !ctx.promptEnabled) {
6589
+ const jsrTokenEnv = process8.env.JSR_TOKEN;
6590
+ if (!jsrTokenEnv) {
6591
+ throw new JsrAvailableError(
6592
+ "JSR_TOKEN not found in the environment variables. Please set the token and try again."
6593
+ );
6594
+ }
6595
+ JsrClient.token = jsrTokenEnv;
6437
6596
  }
6438
- JsrClient.token = jsrTokenEnv;
6439
- }
6440
- let result = await jsr.publish();
6441
- if (!result && jsr.packageCreationUrls) {
6442
- if (ctx.promptEnabled) {
6443
- task.title = "Running jsr publish (package creation needed)";
6444
- const urls = jsr.packageCreationUrls;
6445
- const maxAttempts = 3;
6446
- task.output = `Package doesn't exist on jsr. Create it at:
6597
+ let result = await jsr.publish();
6598
+ if (!result && jsr.packageCreationUrls) {
6599
+ if (ctx.promptEnabled) {
6600
+ task.title = "Running jsr publish (package creation needed)";
6601
+ const urls = jsr.packageCreationUrls;
6602
+ const maxAttempts = 3;
6603
+ task.output = `Package doesn't exist on jsr. Create it at:
6447
6604
  ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6448
- open(urls[0]);
6449
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6450
- await task.prompt(ListrEnquirerPromptAdapter2).run({
6451
- type: "input",
6452
- message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6453
- });
6454
- result = await jsr.publish();
6455
- if (result) break;
6456
- if (attempt < maxAttempts) {
6457
- task.output = "Package still doesn't exist. Please create it and try again.";
6605
+ open(urls[0]);
6606
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6607
+ await task.prompt(ListrEnquirerPromptAdapter2).run({
6608
+ type: "input",
6609
+ message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6610
+ });
6611
+ result = await jsr.publish();
6612
+ if (result) break;
6613
+ if (attempt < maxAttempts) {
6614
+ task.output = "Package still doesn't exist. Please create it and try again.";
6615
+ }
6458
6616
  }
6459
- }
6460
- if (!result) {
6617
+ if (!result) {
6618
+ throw new JsrAvailableError(
6619
+ "Package creation not completed after 3 attempts."
6620
+ );
6621
+ }
6622
+ task.title = "Running jsr publish (package created)";
6623
+ } else {
6461
6624
  throw new JsrAvailableError(
6462
- "Package creation not completed after 3 attempts."
6625
+ `Package doesn't exist on jsr. Create it at:
6626
+ ${jsr.packageCreationUrls.join("\n")}`
6463
6627
  );
6464
6628
  }
6465
- task.title = "Running jsr publish (package created)";
6466
- } else {
6467
- throw new JsrAvailableError(
6468
- `Package doesn't exist on jsr. Create it at:
6469
- ${jsr.packageCreationUrls.join("\n")}`
6470
- );
6471
6629
  }
6630
+ } catch (error) {
6631
+ if (error instanceof Error && error.message.includes("already published")) {
6632
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
6633
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6634
+ return task.skip();
6635
+ }
6636
+ throw error;
6472
6637
  }
6473
6638
  }
6474
6639
  };
@@ -6565,44 +6730,62 @@ var npmPublishTasks = {
6565
6730
  skip: (ctx) => !!ctx.preview,
6566
6731
  task: async (ctx, task) => {
6567
6732
  const npm = await npmRegistry();
6733
+ if (await npm.isVersionPublished(ctx.version)) {
6734
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
6735
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6736
+ return task.skip();
6737
+ }
6568
6738
  task.output = "Publishing on npm...";
6569
- if (ctx.promptEnabled) {
6570
- let result = await npm.publish();
6571
- if (!result) {
6572
- task.title = "Running npm publish (OTP code needed)";
6573
- const maxAttempts = 3;
6574
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6575
- result = await npm.publish(
6576
- await task.prompt(ListrEnquirerPromptAdapter3).run({
6577
- type: "password",
6578
- message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6579
- })
6580
- );
6581
- if (result) break;
6582
- if (attempt < maxAttempts) {
6583
- task.output = "2FA failed. Please try again.";
6739
+ try {
6740
+ if (ctx.promptEnabled) {
6741
+ let result = await npm.publish();
6742
+ if (!result) {
6743
+ task.title = "Running npm publish (OTP code needed)";
6744
+ const maxAttempts = 3;
6745
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6746
+ result = await npm.publish(
6747
+ await task.prompt(ListrEnquirerPromptAdapter3).run({
6748
+ type: "password",
6749
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6750
+ })
6751
+ );
6752
+ if (result) break;
6753
+ if (attempt < maxAttempts) {
6754
+ task.output = "2FA failed. Please try again.";
6755
+ }
6756
+ }
6757
+ if (!result) {
6758
+ throw new NpmAvailableError(
6759
+ "OTP verification failed after 3 attempts."
6760
+ );
6584
6761
  }
6762
+ task.title = "Running npm publish (2FA passed)";
6585
6763
  }
6764
+ } else {
6765
+ const npmTokenEnv = process9.env.NODE_AUTH_TOKEN;
6766
+ if (!npmTokenEnv) {
6767
+ throw new NpmAvailableError(
6768
+ "NODE_AUTH_TOKEN not found in environment variables. Set it in your CI configuration:\n GitHub Actions: Add NODE_AUTH_TOKEN as a repository secret\n Other CI: Export NODE_AUTH_TOKEN with your npm access token"
6769
+ );
6770
+ }
6771
+ const result = await npm.publishProvenance();
6586
6772
  if (!result) {
6587
6773
  throw new NpmAvailableError(
6588
- "OTP verification failed after 3 attempts."
6774
+ `In CI environment, publishing with 2FA is not allowed. Please disable 2FA when accessing with a token from https://www.npmjs.com/package/${npm.packageName}/access `
6589
6775
  );
6590
6776
  }
6591
- task.title = "Running npm publish (2FA passed)";
6592
- }
6593
- } else {
6594
- const npmTokenEnv = process9.env.NODE_AUTH_TOKEN;
6595
- if (!npmTokenEnv) {
6596
- throw new NpmAvailableError(
6597
- "NODE_AUTH_TOKEN not found in environment variables. Set it in your CI configuration:\n GitHub Actions: Add NODE_AUTH_TOKEN as a repository secret\n Other CI: Export NODE_AUTH_TOKEN with your npm access token"
6598
- );
6599
6777
  }
6600
- const result = await npm.publishProvenance();
6601
- if (!result) {
6602
- throw new NpmAvailableError(
6603
- `In CI environment, publishing with 2FA is not allowed. Please disable 2FA when accessing with a token from https://www.npmjs.com/package/${npm.packageName}/access `
6604
- );
6778
+ } catch (error) {
6779
+ if (error instanceof Error && (error.message.includes(
6780
+ "cannot publish over the previously published"
6781
+ ) || error.message.includes(
6782
+ "You cannot publish over the previously published"
6783
+ ))) {
6784
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
6785
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6786
+ return task.skip();
6605
6787
  }
6788
+ throw error;
6606
6789
  }
6607
6790
  }
6608
6791
  };
@@ -7092,10 +7275,15 @@ async function collectDryRunPublishTasks(ctx) {
7092
7275
  return nonCratesTasks;
7093
7276
  }
7094
7277
  const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
7278
+ const siblingCrateNames = await Promise.all(
7279
+ cratesPaths.map((p) => new RustEcosystem(p).packageName())
7280
+ );
7095
7281
  const sequentialCratesTask = {
7096
7282
  title: "Dry-run crates.io publish (sequential)",
7097
7283
  task: (_ctx, task) => task.newListr(
7098
- sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
7284
+ sortedPaths.map(
7285
+ (p) => createCratesDryRunPublishTask(p, siblingCrateNames)
7286
+ ),
7099
7287
  { concurrent: false }
7100
7288
  )
7101
7289
  };
@@ -7188,23 +7376,23 @@ async function run(options) {
7188
7376
  addRollback(async () => {
7189
7377
  if (tagCreated) {
7190
7378
  try {
7191
- console.log("Deleting tag...");
7379
+ rollbackLog("Deleting tag");
7192
7380
  await git.deleteTag(`${await git.latestTag()}`);
7193
7381
  } catch (error) {
7194
- console.error(
7382
+ rollbackError(
7195
7383
  `Failed to delete tag: ${error instanceof Error ? error.message : error}`
7196
7384
  );
7197
7385
  }
7198
7386
  }
7199
7387
  if (commited) {
7200
7388
  try {
7201
- console.log("Reset commits...");
7389
+ rollbackLog("Resetting commits");
7202
7390
  await git.reset();
7203
7391
  await git.stash();
7204
7392
  await git.reset("HEAD^", "--hard");
7205
7393
  await git.popStash();
7206
7394
  } catch (error) {
7207
- console.error(
7395
+ rollbackError(
7208
7396
  `Failed to reset commits: ${error instanceof Error ? error.message : error}`
7209
7397
  );
7210
7398
  }