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/bin/cli.js CHANGED
@@ -5468,12 +5468,6 @@ import SemVer from "semver";
5468
5468
  import { isCI as isCI2 } from "std-env";
5469
5469
  import { exec as exec9 } from "tinyexec";
5470
5470
 
5471
- // src/utils/cli.ts
5472
- var warningBadge = color.bgYellow(" Warning ");
5473
- function link2(text, url) {
5474
- return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
5475
- }
5476
-
5477
5471
  // src/ecosystem/rust.ts
5478
5472
  import { readFile, stat as stat2, writeFile } from "node:fs/promises";
5479
5473
  import path8 from "node:path";
@@ -5593,6 +5587,12 @@ var RustEcosystem = class extends Ecosystem {
5593
5587
  }
5594
5588
  };
5595
5589
 
5590
+ // src/utils/cli.ts
5591
+ var warningBadge = color.bgYellow(" Warning ");
5592
+ function link2(text, url) {
5593
+ return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
5594
+ }
5595
+
5596
5596
  // src/utils/crate-graph.ts
5597
5597
  async function sortCratesByDependencyOrder(cratePaths) {
5598
5598
  if (cratePaths.length <= 1) return cratePaths;
@@ -6044,7 +6044,7 @@ ${stderr}` : "Failed to run `cargo publish`";
6044
6044
  }
6045
6045
  async dryRunPublish(manifestDir) {
6046
6046
  try {
6047
- const args = ["publish", "--dry-run", "--no-verify"];
6047
+ const args = ["publish", "--dry-run"];
6048
6048
  if (manifestDir) {
6049
6049
  args.push("--manifest-path", path10.join(manifestDir, "Cargo.toml"));
6050
6050
  }
@@ -6056,6 +6056,20 @@ ${stderr}` : "Failed to run `cargo publish --dry-run`";
6056
6056
  throw new CratesError(message, { cause: error });
6057
6057
  }
6058
6058
  }
6059
+ async isVersionPublished(version2) {
6060
+ try {
6061
+ const response = await fetch(
6062
+ `${this.registry}/api/v1/crates/${this.packageName}/${version2}`,
6063
+ { headers: this.headers }
6064
+ );
6065
+ return response.ok;
6066
+ } catch (error) {
6067
+ throw new CratesError(
6068
+ `Failed to check version ${version2} for '${this.packageName}' on crates.io`,
6069
+ { cause: error }
6070
+ );
6071
+ }
6072
+ }
6059
6073
  async isPublished() {
6060
6074
  try {
6061
6075
  const response = await fetch(
@@ -6132,10 +6146,24 @@ function createCratesPublishTask(packagePath) {
6132
6146
  const label = packagePath ? ` (${packagePath})` : "";
6133
6147
  return {
6134
6148
  title: `Publishing to crates.io${label}`,
6135
- task: async () => {
6149
+ task: async (ctx, task) => {
6136
6150
  const packageName = await getCrateName(packagePath);
6137
6151
  const registry = new CratesRegistry(packageName);
6138
- await registry.publish(packagePath);
6152
+ if (await registry.isVersionPublished(ctx.version)) {
6153
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
6154
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
6155
+ return task.skip();
6156
+ }
6157
+ try {
6158
+ await registry.publish(packagePath);
6159
+ } catch (error) {
6160
+ if (error instanceof Error && error.message.includes("is already uploaded")) {
6161
+ task.title = `[SKIPPED] crates.io${label}: v${ctx.version} already published`;
6162
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
6163
+ return task.skip();
6164
+ }
6165
+ throw error;
6166
+ }
6139
6167
  }
6140
6168
  };
6141
6169
  }
@@ -6306,6 +6334,20 @@ ${stderr}` : ""}`,
6306
6334
  );
6307
6335
  }
6308
6336
  }
6337
+ async isVersionPublished(version2) {
6338
+ try {
6339
+ const [scope, name] = getScopeAndName(this.packageName);
6340
+ const response = await fetch(
6341
+ `${this.registry}/@${scope}/${name}/${version2}`
6342
+ );
6343
+ return response.status === 200;
6344
+ } catch (error) {
6345
+ throw new JsrError(
6346
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
6347
+ { cause: error }
6348
+ );
6349
+ }
6350
+ }
6309
6351
  async hasPermission() {
6310
6352
  return this.client.scopePermission(`${getScope(this.packageName)}`) !== null;
6311
6353
  }
@@ -6580,6 +6622,19 @@ var NpmRegistry = class extends Registry {
6580
6622
  );
6581
6623
  }
6582
6624
  }
6625
+ async isVersionPublished(version2) {
6626
+ try {
6627
+ const response = await fetch(
6628
+ `${this.registry}/${this.packageName}/${version2}`
6629
+ );
6630
+ return response.status === 200;
6631
+ } catch (error) {
6632
+ throw new NpmError(
6633
+ `Failed to fetch \`${this.registry}/${this.packageName}/${version2}\``,
6634
+ { cause: error }
6635
+ );
6636
+ }
6637
+ }
6583
6638
  async userName() {
6584
6639
  try {
6585
6640
  return (await this.npm(["whoami"])).trim();
@@ -6785,20 +6840,30 @@ async function withTokenRetry(registryKey, task, action) {
6785
6840
  }
6786
6841
  var npmDryRunPublishTask = {
6787
6842
  title: "Dry-run npm publish",
6788
- task: async (_, task) => {
6843
+ task: async (ctx, task) => {
6844
+ const npm = await npmRegistry();
6845
+ if (await npm.isVersionPublished(ctx.version)) {
6846
+ task.title = `[SKIPPED] Dry-run npm publish: v${ctx.version} already published`;
6847
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
6848
+ return task.skip();
6849
+ }
6789
6850
  task.output = "Running npm publish --dry-run...";
6790
6851
  await withTokenRetry("npm", task, async () => {
6791
- const npm = await npmRegistry();
6792
6852
  await npm.dryRunPublish();
6793
6853
  });
6794
6854
  }
6795
6855
  };
6796
6856
  var jsrDryRunPublishTask = {
6797
6857
  title: "Dry-run jsr publish",
6798
- task: async (_, task) => {
6858
+ task: async (ctx, task) => {
6859
+ const jsr = await jsrRegistry();
6860
+ if (await jsr.isVersionPublished(ctx.version)) {
6861
+ task.title = `[SKIPPED] Dry-run jsr publish: v${ctx.version} already published`;
6862
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
6863
+ return task.skip();
6864
+ }
6799
6865
  task.output = "Running jsr publish --dry-run...";
6800
6866
  await withTokenRetry("jsr", task, async () => {
6801
- const jsr = await jsrRegistry();
6802
6867
  await jsr.dryRunPublish();
6803
6868
  });
6804
6869
  }
@@ -6807,17 +6872,58 @@ async function getCrateName2(packagePath) {
6807
6872
  const eco = new RustEcosystem(packagePath ?? process.cwd());
6808
6873
  return await eco.packageName();
6809
6874
  }
6810
- function createCratesDryRunPublishTask(packagePath) {
6875
+ var MISSING_CRATE_PATTERN = /no matching package named `([^`]+)` found/;
6876
+ async function findUnpublishedSiblingDeps(packagePath, siblingCrateNames) {
6877
+ const eco = new RustEcosystem(packagePath ?? process.cwd());
6878
+ const deps = await eco.dependencies();
6879
+ const siblingDeps = deps.filter((d2) => siblingCrateNames.includes(d2));
6880
+ const results = await Promise.all(
6881
+ siblingDeps.map(async (name) => {
6882
+ const registry = new CratesRegistry(name);
6883
+ const published = await registry.isPublished();
6884
+ return published ? null : name;
6885
+ })
6886
+ );
6887
+ return results.filter((name) => name !== null);
6888
+ }
6889
+ function createCratesDryRunPublishTask(packagePath, siblingCrateNames) {
6811
6890
  const label = packagePath ? ` (${packagePath})` : "";
6812
6891
  return {
6813
6892
  title: `Dry-run crates.io publish${label}`,
6814
- task: async (_, task) => {
6893
+ task: async (ctx, task) => {
6894
+ const packageName = await getCrateName2(packagePath);
6895
+ const registry = new CratesRegistry(packageName);
6896
+ if (await registry.isVersionPublished(ctx.version)) {
6897
+ task.title = `[SKIPPED] Dry-run crates.io publish${label}: v${ctx.version} already published`;
6898
+ task.output = `\u26A0 ${packageName}@${ctx.version} is already published on crates.io`;
6899
+ return task.skip();
6900
+ }
6901
+ if (siblingCrateNames?.length) {
6902
+ const unpublished = await findUnpublishedSiblingDeps(
6903
+ packagePath,
6904
+ siblingCrateNames
6905
+ );
6906
+ if (unpublished.length > 0) {
6907
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${unpublished.join("`, `")}\` not yet published]`;
6908
+ return;
6909
+ }
6910
+ }
6815
6911
  task.output = "Running cargo publish --dry-run...";
6816
- await withTokenRetry("crates", task, async () => {
6817
- const packageName = await getCrateName2(packagePath);
6818
- const registry = new CratesRegistry(packageName);
6819
- await registry.dryRunPublish(packagePath);
6820
- });
6912
+ try {
6913
+ await withTokenRetry("crates", task, async () => {
6914
+ const packageName2 = await getCrateName2(packagePath);
6915
+ const registry2 = new CratesRegistry(packageName2);
6916
+ await registry2.dryRunPublish(packagePath);
6917
+ });
6918
+ } catch (error) {
6919
+ const message = error instanceof Error ? error.message : String(error);
6920
+ const match = message.match(MISSING_CRATE_PATTERN);
6921
+ if (match && siblingCrateNames?.includes(match[1])) {
6922
+ task.title = `Dry-run crates.io publish${label} [skipped: sibling crate \`${match[1]}\` not yet published]`;
6923
+ return;
6924
+ }
6925
+ throw error;
6926
+ }
6821
6927
  }
6822
6928
  };
6823
6929
  }
@@ -6998,48 +7104,62 @@ var jsrPublishTasks = {
6998
7104
  title: "Running jsr publish",
6999
7105
  task: async (ctx, task) => {
7000
7106
  const jsr = await jsrRegistry();
7107
+ if (await jsr.isVersionPublished(ctx.version)) {
7108
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
7109
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
7110
+ return task.skip();
7111
+ }
7001
7112
  task.output = "Publishing on jsr...";
7002
- if (!JsrClient.token && !ctx.promptEnabled) {
7003
- const jsrTokenEnv = process12.env.JSR_TOKEN;
7004
- if (!jsrTokenEnv) {
7005
- throw new JsrAvailableError(
7006
- "JSR_TOKEN not found in the environment variables. Please set the token and try again."
7007
- );
7113
+ try {
7114
+ if (!JsrClient.token && !ctx.promptEnabled) {
7115
+ const jsrTokenEnv = process12.env.JSR_TOKEN;
7116
+ if (!jsrTokenEnv) {
7117
+ throw new JsrAvailableError(
7118
+ "JSR_TOKEN not found in the environment variables. Please set the token and try again."
7119
+ );
7120
+ }
7121
+ JsrClient.token = jsrTokenEnv;
7008
7122
  }
7009
- JsrClient.token = jsrTokenEnv;
7010
- }
7011
- let result = await jsr.publish();
7012
- if (!result && jsr.packageCreationUrls) {
7013
- if (ctx.promptEnabled) {
7014
- task.title = "Running jsr publish (package creation needed)";
7015
- const urls = jsr.packageCreationUrls;
7016
- const maxAttempts = 3;
7017
- task.output = `Package doesn't exist on jsr. Create it at:
7123
+ let result = await jsr.publish();
7124
+ if (!result && jsr.packageCreationUrls) {
7125
+ if (ctx.promptEnabled) {
7126
+ task.title = "Running jsr publish (package creation needed)";
7127
+ const urls = jsr.packageCreationUrls;
7128
+ const maxAttempts = 3;
7129
+ task.output = `Package doesn't exist on jsr. Create it at:
7018
7130
  ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
7019
- open(urls[0]);
7020
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7021
- await task.prompt(ListrEnquirerPromptAdapter3).run({
7022
- type: "input",
7023
- message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
7024
- });
7025
- result = await jsr.publish();
7026
- if (result) break;
7027
- if (attempt < maxAttempts) {
7028
- task.output = "Package still doesn't exist. Please create it and try again.";
7131
+ open(urls[0]);
7132
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7133
+ await task.prompt(ListrEnquirerPromptAdapter3).run({
7134
+ type: "input",
7135
+ message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
7136
+ });
7137
+ result = await jsr.publish();
7138
+ if (result) break;
7139
+ if (attempt < maxAttempts) {
7140
+ task.output = "Package still doesn't exist. Please create it and try again.";
7141
+ }
7029
7142
  }
7030
- }
7031
- if (!result) {
7143
+ if (!result) {
7144
+ throw new JsrAvailableError(
7145
+ "Package creation not completed after 3 attempts."
7146
+ );
7147
+ }
7148
+ task.title = "Running jsr publish (package created)";
7149
+ } else {
7032
7150
  throw new JsrAvailableError(
7033
- "Package creation not completed after 3 attempts."
7151
+ `Package doesn't exist on jsr. Create it at:
7152
+ ${jsr.packageCreationUrls.join("\n")}`
7034
7153
  );
7035
7154
  }
7036
- task.title = "Running jsr publish (package created)";
7037
- } else {
7038
- throw new JsrAvailableError(
7039
- `Package doesn't exist on jsr. Create it at:
7040
- ${jsr.packageCreationUrls.join("\n")}`
7041
- );
7042
7155
  }
7156
+ } catch (error) {
7157
+ if (error instanceof Error && error.message.includes("already published")) {
7158
+ task.title = `[SKIPPED] jsr: v${ctx.version} already published`;
7159
+ task.output = `\u26A0 ${jsr.packageName}@${ctx.version} is already published on jsr`;
7160
+ return task.skip();
7161
+ }
7162
+ throw error;
7043
7163
  }
7044
7164
  }
7045
7165
  };
@@ -7136,44 +7256,62 @@ var npmPublishTasks = {
7136
7256
  skip: (ctx) => !!ctx.preview,
7137
7257
  task: async (ctx, task) => {
7138
7258
  const npm = await npmRegistry();
7259
+ if (await npm.isVersionPublished(ctx.version)) {
7260
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
7261
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
7262
+ return task.skip();
7263
+ }
7139
7264
  task.output = "Publishing on npm...";
7140
- if (ctx.promptEnabled) {
7141
- let result = await npm.publish();
7142
- if (!result) {
7143
- task.title = "Running npm publish (OTP code needed)";
7144
- const maxAttempts = 3;
7145
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7146
- result = await npm.publish(
7147
- await task.prompt(ListrEnquirerPromptAdapter4).run({
7148
- type: "password",
7149
- message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
7150
- })
7151
- );
7152
- if (result) break;
7153
- if (attempt < maxAttempts) {
7154
- task.output = "2FA failed. Please try again.";
7265
+ try {
7266
+ if (ctx.promptEnabled) {
7267
+ let result = await npm.publish();
7268
+ if (!result) {
7269
+ task.title = "Running npm publish (OTP code needed)";
7270
+ const maxAttempts = 3;
7271
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7272
+ result = await npm.publish(
7273
+ await task.prompt(ListrEnquirerPromptAdapter4).run({
7274
+ type: "password",
7275
+ message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
7276
+ })
7277
+ );
7278
+ if (result) break;
7279
+ if (attempt < maxAttempts) {
7280
+ task.output = "2FA failed. Please try again.";
7281
+ }
7282
+ }
7283
+ if (!result) {
7284
+ throw new NpmAvailableError(
7285
+ "OTP verification failed after 3 attempts."
7286
+ );
7155
7287
  }
7288
+ task.title = "Running npm publish (2FA passed)";
7289
+ }
7290
+ } else {
7291
+ const npmTokenEnv = process13.env.NODE_AUTH_TOKEN;
7292
+ if (!npmTokenEnv) {
7293
+ throw new NpmAvailableError(
7294
+ "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"
7295
+ );
7156
7296
  }
7297
+ const result = await npm.publishProvenance();
7157
7298
  if (!result) {
7158
7299
  throw new NpmAvailableError(
7159
- "OTP verification failed after 3 attempts."
7300
+ `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 `
7160
7301
  );
7161
7302
  }
7162
- task.title = "Running npm publish (2FA passed)";
7163
- }
7164
- } else {
7165
- const npmTokenEnv = process13.env.NODE_AUTH_TOKEN;
7166
- if (!npmTokenEnv) {
7167
- throw new NpmAvailableError(
7168
- "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"
7169
- );
7170
7303
  }
7171
- const result = await npm.publishProvenance();
7172
- if (!result) {
7173
- throw new NpmAvailableError(
7174
- `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 `
7175
- );
7304
+ } catch (error) {
7305
+ if (error instanceof Error && (error.message.includes(
7306
+ "cannot publish over the previously published"
7307
+ ) || error.message.includes(
7308
+ "You cannot publish over the previously published"
7309
+ ))) {
7310
+ task.title = `[SKIPPED] npm: v${ctx.version} already published`;
7311
+ task.output = `\u26A0 ${npm.packageName}@${ctx.version} is already published on npm`;
7312
+ return task.skip();
7176
7313
  }
7314
+ throw error;
7177
7315
  }
7178
7316
  }
7179
7317
  };
@@ -7589,10 +7727,15 @@ async function collectDryRunPublishTasks(ctx) {
7589
7727
  return nonCratesTasks;
7590
7728
  }
7591
7729
  const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
7730
+ const siblingCrateNames = await Promise.all(
7731
+ cratesPaths.map((p) => new RustEcosystem(p).packageName())
7732
+ );
7592
7733
  const sequentialCratesTask = {
7593
7734
  title: "Dry-run crates.io publish (sequential)",
7594
7735
  task: (_ctx, task) => task.newListr(
7595
- sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
7736
+ sortedPaths.map(
7737
+ (p) => createCratesDryRunPublishTask(p, siblingCrateNames)
7738
+ ),
7596
7739
  { concurrent: false }
7597
7740
  )
7598
7741
  };