pubm 0.2.7 → 0.2.8

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 +227 -84
  2. package/dist/index.cjs +343 -200
  3. package/dist/index.js +343 -200
  4. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -4488,6 +4488,125 @@ var import_semver3 = __toESM(require("semver"), 1);
4488
4488
  var import_std_env = require("std-env");
4489
4489
  var import_tinyexec8 = require("tinyexec");
4490
4490
 
4491
+ // src/ecosystem/rust.ts
4492
+ var import_promises2 = require("fs/promises");
4493
+ var import_node_path2 = __toESM(require("path"), 1);
4494
+ var import_smol_toml = require("smol-toml");
4495
+ var import_tinyexec = require("tinyexec");
4496
+
4497
+ // src/ecosystem/ecosystem.ts
4498
+ var Ecosystem = class {
4499
+ constructor(packagePath) {
4500
+ this.packagePath = packagePath;
4501
+ }
4502
+ };
4503
+
4504
+ // src/ecosystem/rust.ts
4505
+ var RustEcosystem = class extends Ecosystem {
4506
+ static async detect(packagePath) {
4507
+ try {
4508
+ return (await (0, import_promises2.stat)(import_node_path2.default.join(packagePath, "Cargo.toml"))).isFile();
4509
+ } catch {
4510
+ return false;
4511
+ }
4512
+ }
4513
+ async readCargoToml() {
4514
+ const raw = await (0, import_promises2.readFile)(
4515
+ import_node_path2.default.join(this.packagePath, "Cargo.toml"),
4516
+ "utf-8"
4517
+ );
4518
+ return (0, import_smol_toml.parse)(raw);
4519
+ }
4520
+ async packageName() {
4521
+ const cargo = await this.readCargoToml();
4522
+ const pkg = cargo.package;
4523
+ return pkg.name;
4524
+ }
4525
+ async readVersion() {
4526
+ const cargo = await this.readCargoToml();
4527
+ const pkg = cargo.package;
4528
+ return pkg.version;
4529
+ }
4530
+ async writeVersion(newVersion) {
4531
+ const filePath = import_node_path2.default.join(this.packagePath, "Cargo.toml");
4532
+ const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
4533
+ const cargo = (0, import_smol_toml.parse)(raw);
4534
+ const pkg = cargo.package;
4535
+ pkg.version = newVersion;
4536
+ await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4537
+ }
4538
+ /**
4539
+ * Update the `version` field of dependencies that match sibling crate names.
4540
+ * This ensures `cargo publish` works when crates depend on each other via path.
4541
+ */
4542
+ async updateSiblingDependencyVersions(siblingVersions) {
4543
+ const filePath = import_node_path2.default.join(this.packagePath, "Cargo.toml");
4544
+ const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
4545
+ const cargo = (0, import_smol_toml.parse)(raw);
4546
+ let modified = false;
4547
+ for (const section of ["dependencies", "build-dependencies"]) {
4548
+ const sectionData = cargo[section];
4549
+ if (!sectionData) continue;
4550
+ for (const [depName, depValue] of Object.entries(sectionData)) {
4551
+ if (typeof depValue === "object" && depValue !== null && "path" in depValue && siblingVersions.has(depName)) {
4552
+ const dep = depValue;
4553
+ dep.version = siblingVersions.get(depName);
4554
+ modified = true;
4555
+ }
4556
+ }
4557
+ }
4558
+ if (modified) {
4559
+ await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4560
+ }
4561
+ return modified;
4562
+ }
4563
+ async syncLockfile() {
4564
+ const lockfilePath = await this.findLockfile();
4565
+ if (!lockfilePath) return void 0;
4566
+ const name = await this.packageName();
4567
+ await (0, import_tinyexec.exec)("cargo", ["update", "--package", name], {
4568
+ nodeOptions: { cwd: import_node_path2.default.dirname(lockfilePath) }
4569
+ });
4570
+ return lockfilePath;
4571
+ }
4572
+ async findLockfile() {
4573
+ let dir = this.packagePath;
4574
+ const { root } = import_node_path2.default.parse(dir);
4575
+ while (dir !== root) {
4576
+ const candidate = import_node_path2.default.join(dir, "Cargo.lock");
4577
+ try {
4578
+ if ((await (0, import_promises2.stat)(candidate)).isFile()) return candidate;
4579
+ } catch {
4580
+ }
4581
+ dir = import_node_path2.default.dirname(dir);
4582
+ }
4583
+ return void 0;
4584
+ }
4585
+ async dependencies() {
4586
+ const cargo = await this.readCargoToml();
4587
+ const deps = [];
4588
+ for (const section of ["dependencies", "build-dependencies"]) {
4589
+ const sectionData = cargo[section];
4590
+ if (sectionData) {
4591
+ deps.push(...Object.keys(sectionData));
4592
+ }
4593
+ }
4594
+ return deps;
4595
+ }
4596
+ manifestFiles() {
4597
+ return ["Cargo.toml"];
4598
+ }
4599
+ defaultTestCommand() {
4600
+ return "cargo test";
4601
+ }
4602
+ defaultBuildCommand() {
4603
+ return "cargo build --release";
4604
+ }
4605
+ supportedRegistries() {
4606
+ return ["crates"];
4607
+ }
4608
+ };
4609
+
4491
4610
  // src/error.ts
4492
4611
  var AbstractError = class extends Error {
4493
4612
  constructor(message, { cause } = {}) {
@@ -4529,7 +4648,7 @@ function consoleError(error) {
4529
4648
 
4530
4649
  // src/git.ts
4531
4650
  var import_semver = __toESM(require("semver"), 1);
4532
- var import_tinyexec = require("tinyexec");
4651
+ var import_tinyexec2 = require("tinyexec");
4533
4652
  var GitError = class extends AbstractError {
4534
4653
  constructor() {
4535
4654
  super(...arguments);
@@ -4538,7 +4657,7 @@ var GitError = class extends AbstractError {
4538
4657
  };
4539
4658
  var Git = class {
4540
4659
  async git(args) {
4541
- const { stdout } = await (0, import_tinyexec.exec)("git", args, { throwOnError: true });
4660
+ const { stdout } = await (0, import_tinyexec2.exec)("git", args, { throwOnError: true });
4542
4661
  return stdout;
4543
4662
  }
4544
4663
  async userName() {
@@ -4792,7 +4911,7 @@ var Git = class {
4792
4911
  async push(options) {
4793
4912
  const args = ["push", options].filter((v) => v);
4794
4913
  try {
4795
- const { stderr } = await (0, import_tinyexec.exec)("git", args, { throwOnError: true });
4914
+ const { stderr } = await (0, import_tinyexec2.exec)("git", args, { throwOnError: true });
4796
4915
  if (`${stderr}`.includes("GH006")) {
4797
4916
  return false;
4798
4917
  }
@@ -4814,125 +4933,6 @@ function link2(text, url) {
4814
4933
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
4815
4934
  }
4816
4935
 
4817
- // src/ecosystem/rust.ts
4818
- var import_promises2 = require("fs/promises");
4819
- var import_node_path2 = __toESM(require("path"), 1);
4820
- var import_smol_toml = require("smol-toml");
4821
- var import_tinyexec2 = require("tinyexec");
4822
-
4823
- // src/ecosystem/ecosystem.ts
4824
- var Ecosystem = class {
4825
- constructor(packagePath) {
4826
- this.packagePath = packagePath;
4827
- }
4828
- };
4829
-
4830
- // src/ecosystem/rust.ts
4831
- var RustEcosystem = class extends Ecosystem {
4832
- static async detect(packagePath) {
4833
- try {
4834
- return (await (0, import_promises2.stat)(import_node_path2.default.join(packagePath, "Cargo.toml"))).isFile();
4835
- } catch {
4836
- return false;
4837
- }
4838
- }
4839
- async readCargoToml() {
4840
- const raw = await (0, import_promises2.readFile)(
4841
- import_node_path2.default.join(this.packagePath, "Cargo.toml"),
4842
- "utf-8"
4843
- );
4844
- return (0, import_smol_toml.parse)(raw);
4845
- }
4846
- async packageName() {
4847
- const cargo = await this.readCargoToml();
4848
- const pkg = cargo.package;
4849
- return pkg.name;
4850
- }
4851
- async readVersion() {
4852
- const cargo = await this.readCargoToml();
4853
- const pkg = cargo.package;
4854
- return pkg.version;
4855
- }
4856
- async writeVersion(newVersion) {
4857
- const filePath = import_node_path2.default.join(this.packagePath, "Cargo.toml");
4858
- const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
4859
- const cargo = (0, import_smol_toml.parse)(raw);
4860
- const pkg = cargo.package;
4861
- pkg.version = newVersion;
4862
- await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4863
- }
4864
- /**
4865
- * Update the `version` field of dependencies that match sibling crate names.
4866
- * This ensures `cargo publish` works when crates depend on each other via path.
4867
- */
4868
- async updateSiblingDependencyVersions(siblingVersions) {
4869
- const filePath = import_node_path2.default.join(this.packagePath, "Cargo.toml");
4870
- const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
4871
- const cargo = (0, import_smol_toml.parse)(raw);
4872
- let modified = false;
4873
- for (const section of ["dependencies", "build-dependencies"]) {
4874
- const sectionData = cargo[section];
4875
- if (!sectionData) continue;
4876
- for (const [depName, depValue] of Object.entries(sectionData)) {
4877
- if (typeof depValue === "object" && depValue !== null && "path" in depValue && siblingVersions.has(depName)) {
4878
- const dep = depValue;
4879
- dep.version = siblingVersions.get(depName);
4880
- modified = true;
4881
- }
4882
- }
4883
- }
4884
- if (modified) {
4885
- await (0, import_promises2.writeFile)(filePath, (0, import_smol_toml.stringify)(cargo));
4886
- }
4887
- return modified;
4888
- }
4889
- async syncLockfile() {
4890
- const lockfilePath = await this.findLockfile();
4891
- if (!lockfilePath) return void 0;
4892
- const name = await this.packageName();
4893
- await (0, import_tinyexec2.exec)("cargo", ["update", "--package", name], {
4894
- nodeOptions: { cwd: import_node_path2.default.dirname(lockfilePath) }
4895
- });
4896
- return lockfilePath;
4897
- }
4898
- async findLockfile() {
4899
- let dir = this.packagePath;
4900
- const { root } = import_node_path2.default.parse(dir);
4901
- while (dir !== root) {
4902
- const candidate = import_node_path2.default.join(dir, "Cargo.lock");
4903
- try {
4904
- if ((await (0, import_promises2.stat)(candidate)).isFile()) return candidate;
4905
- } catch {
4906
- }
4907
- dir = import_node_path2.default.dirname(dir);
4908
- }
4909
- return void 0;
4910
- }
4911
- async dependencies() {
4912
- const cargo = await this.readCargoToml();
4913
- const deps = [];
4914
- for (const section of ["dependencies", "build-dependencies"]) {
4915
- const sectionData = cargo[section];
4916
- if (sectionData) {
4917
- deps.push(...Object.keys(sectionData));
4918
- }
4919
- }
4920
- return deps;
4921
- }
4922
- manifestFiles() {
4923
- return ["Cargo.toml"];
4924
- }
4925
- defaultTestCommand() {
4926
- return "cargo test";
4927
- }
4928
- defaultBuildCommand() {
4929
- return "cargo build --release";
4930
- }
4931
- supportedRegistries() {
4932
- return ["crates"];
4933
- }
4934
- };
4935
-
4936
4936
  // src/utils/crate-graph.ts
4937
4937
  async function sortCratesByDependencyOrder(cratePaths) {
4938
4938
  if (cratePaths.length <= 1) return cratePaths;
@@ -5507,7 +5507,7 @@ ${stderr}` : "Failed to run `cargo publish`";
5507
5507
  }
5508
5508
  async dryRunPublish(manifestDir) {
5509
5509
  try {
5510
- const args = ["publish", "--dry-run", "--no-verify"];
5510
+ const args = ["publish", "--dry-run"];
5511
5511
  if (manifestDir) {
5512
5512
  args.push("--manifest-path", import_node_path5.default.join(manifestDir, "Cargo.toml"));
5513
5513
  }
@@ -5519,6 +5519,20 @@ ${stderr}` : "Failed to run `cargo publish --dry-run`";
5519
5519
  throw new CratesError(message, { cause: error });
5520
5520
  }
5521
5521
  }
5522
+ async isVersionPublished(version2) {
5523
+ try {
5524
+ const response = await fetch(
5525
+ `${this.registry}/api/v1/crates/${this.packageName}/${version2}`,
5526
+ { headers: this.headers }
5527
+ );
5528
+ return response.ok;
5529
+ } catch (error) {
5530
+ throw new CratesError(
5531
+ `Failed to check version ${version2} for '${this.packageName}' on crates.io`,
5532
+ { cause: error }
5533
+ );
5534
+ }
5535
+ }
5522
5536
  async isPublished() {
5523
5537
  try {
5524
5538
  const response = await fetch(
@@ -5595,10 +5609,24 @@ function createCratesPublishTask(packagePath) {
5595
5609
  const label = packagePath ? ` (${packagePath})` : "";
5596
5610
  return {
5597
5611
  title: `Publishing to crates.io${label}`,
5598
- task: async () => {
5612
+ task: async (ctx, task) => {
5599
5613
  const packageName = await getCrateName(packagePath);
5600
5614
  const registry = new CratesRegistry(packageName);
5601
- await registry.publish(packagePath);
5615
+ if (await registry.isVersionPublished(ctx.version)) {
5616
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
5617
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
5618
+ return task.skip();
5619
+ }
5620
+ try {
5621
+ await registry.publish(packagePath);
5622
+ } catch (error) {
5623
+ if (error instanceof Error && error.message.includes("is already uploaded")) {
5624
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
5625
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
5626
+ return task.skip();
5627
+ }
5628
+ throw error;
5629
+ }
5602
5630
  }
5603
5631
  };
5604
5632
  }
@@ -5770,6 +5798,20 @@ ${stderr}` : ""}`,
5770
5798
  );
5771
5799
  }
5772
5800
  }
5801
+ async isVersionPublished(version2) {
5802
+ try {
5803
+ const [scope, name] = getScopeAndName(this.packageName);
5804
+ const response = await fetch(
5805
+ `${this.registry}/@${scope}/${name}/${version2}`
5806
+ );
5807
+ return response.status === 200;
5808
+ } catch (error) {
5809
+ throw new JsrError(
5810
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
5811
+ { cause: error }
5812
+ );
5813
+ }
5814
+ }
5773
5815
  async hasPermission() {
5774
5816
  return this.client.scopePermission(`${getScope(this.packageName)}`) !== null;
5775
5817
  }
@@ -6044,6 +6086,19 @@ var NpmRegistry = class extends Registry {
6044
6086
  );
6045
6087
  }
6046
6088
  }
6089
+ async isVersionPublished(version2) {
6090
+ try {
6091
+ const response = await fetch(
6092
+ `${this.registry}/${this.packageName}/${version2}`
6093
+ );
6094
+ return response.status === 200;
6095
+ } catch (error) {
6096
+ throw new NpmError(
6097
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
6098
+ { cause: error }
6099
+ );
6100
+ }
6101
+ }
6047
6102
  async userName() {
6048
6103
  try {
6049
6104
  return (await this.npm(["whoami"])).trim();
@@ -6249,20 +6304,30 @@ async function withTokenRetry(registryKey, task, action) {
6249
6304
  }
6250
6305
  var npmDryRunPublishTask = {
6251
6306
  title: "Dry-run npm publish",
6252
- task: async (_, task) => {
6307
+ task: async (ctx, task) => {
6308
+ const npm = await npmRegistry();
6309
+ if (await npm.isVersionPublished(ctx.version)) {
6310
+ task.title = `[SKIPPED] Dry-run npm publish: v${ctx.version} already published`;
6311
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6312
+ return task.skip();
6313
+ }
6253
6314
  task.output = "Running npm publish --dry-run...";
6254
6315
  await withTokenRetry("npm", task, async () => {
6255
- const npm = await npmRegistry();
6256
6316
  await npm.dryRunPublish();
6257
6317
  });
6258
6318
  }
6259
6319
  };
6260
6320
  var jsrDryRunPublishTask = {
6261
6321
  title: "Dry-run jsr publish",
6262
- task: async (_, task) => {
6322
+ task: async (ctx, task) => {
6323
+ const jsr = await jsrRegistry();
6324
+ if (await jsr.isVersionPublished(ctx.version)) {
6325
+ task.title = `[SKIPPED] Dry-run jsr publish: v${ctx.version} already published`;
6326
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6327
+ return task.skip();
6328
+ }
6263
6329
  task.output = "Running jsr publish --dry-run...";
6264
6330
  await withTokenRetry("jsr", task, async () => {
6265
- const jsr = await jsrRegistry();
6266
6331
  await jsr.dryRunPublish();
6267
6332
  });
6268
6333
  }
@@ -6271,17 +6336,58 @@ async function getCrateName2(packagePath) {
6271
6336
  const eco = new RustEcosystem(packagePath ?? process.cwd());
6272
6337
  return await eco.packageName();
6273
6338
  }
6274
- function createCratesDryRunPublishTask(packagePath) {
6339
+ var MISSING_CRATE_PATTERN = /no matching package named `([^`]+)` found/;
6340
+ async function findUnpublishedSiblingDeps(packagePath, siblingCrateNames) {
6341
+ const eco = new RustEcosystem(packagePath ?? process.cwd());
6342
+ const deps = await eco.dependencies();
6343
+ const siblingDeps = deps.filter((d2) => siblingCrateNames.includes(d2));
6344
+ const results = await Promise.all(
6345
+ siblingDeps.map(async (name) => {
6346
+ const registry = new CratesRegistry(name);
6347
+ const published = await registry.isPublished();
6348
+ return published ? null : name;
6349
+ })
6350
+ );
6351
+ return results.filter((name) => name !== null);
6352
+ }
6353
+ function createCratesDryRunPublishTask(packagePath, siblingCrateNames) {
6275
6354
  const label = packagePath ? ` (${packagePath})` : "";
6276
6355
  return {
6277
6356
  title: `Dry-run crates.io publish${label}`,
6278
- task: async (_, task) => {
6357
+ task: async (ctx, task) => {
6358
+ const packageName = await getCrateName2(packagePath);
6359
+ const registry = new CratesRegistry(packageName);
6360
+ if (await registry.isVersionPublished(ctx.version)) {
6361
+ task.title = `[SKIPPED] Dry-run crates.io publish${label}: v${ctx.version} already published`;
6362
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
6363
+ return task.skip();
6364
+ }
6365
+ if (siblingCrateNames?.length) {
6366
+ const unpublished = await findUnpublishedSiblingDeps(
6367
+ packagePath,
6368
+ siblingCrateNames
6369
+ );
6370
+ if (unpublished.length > 0) {
6371
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${unpublished.join("`, `")}\` not yet published]`;
6372
+ return;
6373
+ }
6374
+ }
6279
6375
  task.output = "Running cargo publish --dry-run...";
6280
- await withTokenRetry("crates", task, async () => {
6281
- const packageName = await getCrateName2(packagePath);
6282
- const registry = new CratesRegistry(packageName);
6283
- await registry.dryRunPublish(packagePath);
6284
- });
6376
+ try {
6377
+ await withTokenRetry("crates", task, async () => {
6378
+ const packageName2 = await getCrateName2(packagePath);
6379
+ const registry2 = new CratesRegistry(packageName2);
6380
+ await registry2.dryRunPublish(packagePath);
6381
+ });
6382
+ } catch (error) {
6383
+ const message = error instanceof Error ? error.message : String(error);
6384
+ const match = message.match(MISSING_CRATE_PATTERN);
6385
+ if (match && siblingCrateNames?.includes(match[1])) {
6386
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${match[1]}\` not yet published]`;
6387
+ return;
6388
+ }
6389
+ throw error;
6390
+ }
6285
6391
  }
6286
6392
  };
6287
6393
  }
@@ -6462,48 +6568,62 @@ var jsrPublishTasks = {
6462
6568
  title: "Running jsr publish",
6463
6569
  task: async (ctx, task) => {
6464
6570
  const jsr = await jsrRegistry();
6571
+ if (await jsr.isVersionPublished(ctx.version)) {
6572
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
6573
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6574
+ return task.skip();
6575
+ }
6465
6576
  task.output = "Publishing on jsr...";
6466
- if (!JsrClient.token && !ctx.promptEnabled) {
6467
- const jsrTokenEnv = import_node_process6.default.env.JSR_TOKEN;
6468
- if (!jsrTokenEnv) {
6469
- throw new JsrAvailableError(
6470
- "JSR_TOKEN not found in the environment variables. Please set the token and try again."
6471
- );
6577
+ try {
6578
+ if (!JsrClient.token && !ctx.promptEnabled) {
6579
+ const jsrTokenEnv = import_node_process6.default.env.JSR_TOKEN;
6580
+ if (!jsrTokenEnv) {
6581
+ throw new JsrAvailableError(
6582
+ "JSR_TOKEN not found in the environment variables. Please set the token and try again."
6583
+ );
6584
+ }
6585
+ JsrClient.token = jsrTokenEnv;
6472
6586
  }
6473
- JsrClient.token = jsrTokenEnv;
6474
- }
6475
- let result = await jsr.publish();
6476
- if (!result && jsr.packageCreationUrls) {
6477
- if (ctx.promptEnabled) {
6478
- task.title = "Running jsr publish (package creation needed)";
6479
- const urls = jsr.packageCreationUrls;
6480
- const maxAttempts = 3;
6481
- task.output = `Package doesn't exist on jsr. Create it at:
6587
+ let result = await jsr.publish();
6588
+ if (!result && jsr.packageCreationUrls) {
6589
+ if (ctx.promptEnabled) {
6590
+ task.title = "Running jsr publish (package creation needed)";
6591
+ const urls = jsr.packageCreationUrls;
6592
+ const maxAttempts = 3;
6593
+ task.output = `Package doesn't exist on jsr. Create it at:
6482
6594
  ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6483
- open(urls[0]);
6484
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6485
- await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
6486
- type: "input",
6487
- message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6488
- });
6489
- result = await jsr.publish();
6490
- if (result) break;
6491
- if (attempt < maxAttempts) {
6492
- task.output = "Package still doesn't exist. Please create it and try again.";
6595
+ open(urls[0]);
6596
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6597
+ await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
6598
+ type: "input",
6599
+ message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6600
+ });
6601
+ result = await jsr.publish();
6602
+ if (result) break;
6603
+ if (attempt < maxAttempts) {
6604
+ task.output = "Package still doesn't exist. Please create it and try again.";
6605
+ }
6493
6606
  }
6494
- }
6495
- if (!result) {
6607
+ if (!result) {
6608
+ throw new JsrAvailableError(
6609
+ "Package creation not completed after 3 attempts."
6610
+ );
6611
+ }
6612
+ task.title = "Running jsr publish (package created)";
6613
+ } else {
6496
6614
  throw new JsrAvailableError(
6497
- "Package creation not completed after 3 attempts."
6615
+ `Package doesn't exist on jsr. Create it at:
6616
+ ${jsr.packageCreationUrls.join("\n")}`
6498
6617
  );
6499
6618
  }
6500
- task.title = "Running jsr publish (package created)";
6501
- } else {
6502
- throw new JsrAvailableError(
6503
- `Package doesn't exist on jsr. Create it at:
6504
- ${jsr.packageCreationUrls.join("\n")}`
6505
- );
6506
6619
  }
6620
+ } catch (error) {
6621
+ if (error instanceof Error && error.message.includes("already published")) {
6622
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
6623
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6624
+ return task.skip();
6625
+ }
6626
+ throw error;
6507
6627
  }
6508
6628
  }
6509
6629
  };
@@ -6600,44 +6720,62 @@ var npmPublishTasks = {
6600
6720
  skip: (ctx) => !!ctx.preview,
6601
6721
  task: async (ctx, task) => {
6602
6722
  const npm = await npmRegistry();
6723
+ if (await npm.isVersionPublished(ctx.version)) {
6724
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
6725
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6726
+ return task.skip();
6727
+ }
6603
6728
  task.output = "Publishing on npm...";
6604
- if (ctx.promptEnabled) {
6605
- let result = await npm.publish();
6606
- if (!result) {
6607
- task.title = "Running npm publish (OTP code needed)";
6608
- const maxAttempts = 3;
6609
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6610
- result = await npm.publish(
6611
- await task.prompt(import_prompt_adapter_enquirer3.ListrEnquirerPromptAdapter).run({
6612
- type: "password",
6613
- message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6614
- })
6615
- );
6616
- if (result) break;
6617
- if (attempt < maxAttempts) {
6618
- task.output = "2FA failed. Please try again.";
6729
+ try {
6730
+ if (ctx.promptEnabled) {
6731
+ let result = await npm.publish();
6732
+ if (!result) {
6733
+ task.title = "Running npm publish (OTP code needed)";
6734
+ const maxAttempts = 3;
6735
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6736
+ result = await npm.publish(
6737
+ await task.prompt(import_prompt_adapter_enquirer3.ListrEnquirerPromptAdapter).run({
6738
+ type: "password",
6739
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6740
+ })
6741
+ );
6742
+ if (result) break;
6743
+ if (attempt < maxAttempts) {
6744
+ task.output = "2FA failed. Please try again.";
6745
+ }
6746
+ }
6747
+ if (!result) {
6748
+ throw new NpmAvailableError(
6749
+ "OTP verification failed after 3 attempts."
6750
+ );
6619
6751
  }
6752
+ task.title = "Running npm publish (2FA passed)";
6753
+ }
6754
+ } else {
6755
+ const npmTokenEnv = import_node_process7.default.env.NODE_AUTH_TOKEN;
6756
+ if (!npmTokenEnv) {
6757
+ throw new NpmAvailableError(
6758
+ "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"
6759
+ );
6620
6760
  }
6761
+ const result = await npm.publishProvenance();
6621
6762
  if (!result) {
6622
6763
  throw new NpmAvailableError(
6623
- "OTP verification failed after 3 attempts."
6764
+ `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 `
6624
6765
  );
6625
6766
  }
6626
- task.title = "Running npm publish (2FA passed)";
6627
- }
6628
- } else {
6629
- const npmTokenEnv = import_node_process7.default.env.NODE_AUTH_TOKEN;
6630
- if (!npmTokenEnv) {
6631
- throw new NpmAvailableError(
6632
- "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"
6633
- );
6634
6767
  }
6635
- const result = await npm.publishProvenance();
6636
- if (!result) {
6637
- throw new NpmAvailableError(
6638
- `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 `
6639
- );
6768
+ } catch (error) {
6769
+ if (error instanceof Error && (error.message.includes(
6770
+ "cannot publish over the previously published"
6771
+ ) || error.message.includes(
6772
+ "You cannot publish over the previously published"
6773
+ ))) {
6774
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
6775
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6776
+ return task.skip();
6640
6777
  }
6778
+ throw error;
6641
6779
  }
6642
6780
  }
6643
6781
  };
@@ -7128,10 +7266,15 @@ async function collectDryRunPublishTasks(ctx) {
7128
7266
  return nonCratesTasks;
7129
7267
  }
7130
7268
  const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
7269
+ const siblingCrateNames = await Promise.all(
7270
+ cratesPaths.map((p) => new RustEcosystem(p).packageName())
7271
+ );
7131
7272
  const sequentialCratesTask = {
7132
7273
  title: "Dry-run crates.io publish (sequential)",
7133
7274
  task: (_ctx, task) => task.newListr(
7134
- sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
7275
+ sortedPaths.map(
7276
+ (p) => createCratesDryRunPublishTask(p, siblingCrateNames)
7277
+ ),
7135
7278
  { concurrent: false }
7136
7279
  )
7137
7280
  };