pubm 0.2.1 → 0.2.2

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/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 exec7 } from "tinyexec";
4456
+ import { exec as exec8 } from "tinyexec";
4457
4457
 
4458
4458
  // src/error.ts
4459
4459
  var AbstractError = class extends Error {
@@ -5174,8 +5174,130 @@ function collectRegistries(ctx) {
5174
5174
  return ctx.registries;
5175
5175
  }
5176
5176
 
5177
- // src/registry/crates.ts
5177
+ // src/utils/db.ts
5178
+ import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
5179
+ import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
5178
5180
  import path4 from "node:path";
5181
+ var a = "aes-256-cbc";
5182
+ var n = statSync(import.meta.dirname);
5183
+ var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
5184
+ var l = createHash("md5").update(k).digest();
5185
+ function e(e2, f) {
5186
+ const c = createCipheriv(a, createHash("sha-256").update(f).digest(), l);
5187
+ return c.update(e2, "utf8", "hex") + c.final("hex");
5188
+ }
5189
+ function d(g, h) {
5190
+ const d2 = createDecipheriv(a, createHash("sha-256").update(h).digest(), l);
5191
+ return d2.update(g, "hex", "utf8") + d2.final("utf8");
5192
+ }
5193
+ var Db = class {
5194
+ constructor() {
5195
+ __publicField(this, "path", path4.resolve(import.meta.dirname, ".pubm"));
5196
+ try {
5197
+ if (!statSync(this.path).isDirectory()) {
5198
+ mkdirSync(this.path);
5199
+ }
5200
+ } catch {
5201
+ try {
5202
+ mkdirSync(this.path);
5203
+ } catch (error) {
5204
+ throw new Error(
5205
+ `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5206
+ );
5207
+ }
5208
+ }
5209
+ }
5210
+ set(field, value) {
5211
+ try {
5212
+ writeFileSync(
5213
+ path4.resolve(
5214
+ this.path,
5215
+ Buffer.from(e(field, field)).toString("base64")
5216
+ ),
5217
+ Buffer.from(e(`${value}`, field)),
5218
+ { encoding: "binary" }
5219
+ );
5220
+ } catch (error) {
5221
+ throw new Error(
5222
+ `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5223
+ );
5224
+ }
5225
+ }
5226
+ get(field) {
5227
+ const filePath = path4.resolve(
5228
+ this.path,
5229
+ Buffer.from(e(field, field)).toString("base64")
5230
+ );
5231
+ let raw;
5232
+ try {
5233
+ raw = readFileSync(filePath);
5234
+ } catch {
5235
+ return null;
5236
+ }
5237
+ try {
5238
+ return d(Buffer.from(raw).toString(), field);
5239
+ } catch {
5240
+ console.warn(
5241
+ `Stored token for '${field}' appears corrupted. It will be re-requested.`
5242
+ );
5243
+ return null;
5244
+ }
5245
+ }
5246
+ };
5247
+
5248
+ // src/utils/token.ts
5249
+ var TOKEN_CONFIG = {
5250
+ npm: {
5251
+ envVar: "NODE_AUTH_TOKEN",
5252
+ dbKey: "npm-token",
5253
+ ghSecretName: "NODE_AUTH_TOKEN",
5254
+ promptLabel: "npm access token"
5255
+ },
5256
+ jsr: {
5257
+ envVar: "JSR_TOKEN",
5258
+ dbKey: "jsr-token",
5259
+ ghSecretName: "JSR_TOKEN",
5260
+ promptLabel: "jsr API token"
5261
+ },
5262
+ crates: {
5263
+ envVar: "CARGO_REGISTRY_TOKEN",
5264
+ dbKey: "cargo-token",
5265
+ ghSecretName: "CARGO_REGISTRY_TOKEN",
5266
+ promptLabel: "crates.io API token"
5267
+ }
5268
+ };
5269
+ function loadTokensFromDb(registries) {
5270
+ const db = new Db();
5271
+ const tokens = {};
5272
+ for (const registry of registries) {
5273
+ const config = TOKEN_CONFIG[registry];
5274
+ if (!config) continue;
5275
+ const token = db.get(config.dbKey);
5276
+ if (token) tokens[registry] = token;
5277
+ }
5278
+ return tokens;
5279
+ }
5280
+ function injectTokensToEnv(tokens) {
5281
+ const originals = {};
5282
+ for (const [registry, token] of Object.entries(tokens)) {
5283
+ const config = TOKEN_CONFIG[registry];
5284
+ if (!config) continue;
5285
+ originals[config.envVar] = process.env[config.envVar];
5286
+ process.env[config.envVar] = token;
5287
+ }
5288
+ return () => {
5289
+ for (const [envVar, original] of Object.entries(originals)) {
5290
+ if (original === void 0) {
5291
+ delete process.env[envVar];
5292
+ } else {
5293
+ process.env[envVar] = original;
5294
+ }
5295
+ }
5296
+ };
5297
+ }
5298
+
5299
+ // src/registry/crates.ts
5300
+ import path5 from "node:path";
5179
5301
  import { exec as exec3, NonZeroExitError } from "tinyexec";
5180
5302
 
5181
5303
  // src/registry/registry.ts
@@ -5255,7 +5377,7 @@ var CratesRegistry = class extends Registry {
5255
5377
  try {
5256
5378
  const args = ["publish"];
5257
5379
  if (manifestDir) {
5258
- args.push("--manifest-path", path4.join(manifestDir, "Cargo.toml"));
5380
+ args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
5259
5381
  }
5260
5382
  await exec3("cargo", args, { throwOnError: true });
5261
5383
  return true;
@@ -5270,13 +5392,13 @@ ${stderr}` : "Failed to run `cargo publish`";
5270
5392
  try {
5271
5393
  const args = ["publish", "--dry-run"];
5272
5394
  if (manifestDir) {
5273
- args.push("--manifest-path", path4.join(manifestDir, "Cargo.toml"));
5395
+ args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
5274
5396
  }
5275
5397
  await exec3("cargo", args, { throwOnError: true });
5276
5398
  } catch (error) {
5277
5399
  const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
5278
- const message = stderr ? `Dry-run failed for \`cargo publish\`:
5279
- ${stderr}` : "Dry-run failed for `cargo publish`";
5400
+ const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
5401
+ ${stderr}` : "Failed to run `cargo publish --dry-run`";
5280
5402
  throw new CratesError(message, { cause: error });
5281
5403
  }
5282
5404
  }
@@ -5366,80 +5488,12 @@ function createCratesPublishTask(packagePath) {
5366
5488
  var cratesAvailableCheckTasks = createCratesAvailableCheckTask();
5367
5489
  var cratesPublishTasks = createCratesPublishTask();
5368
5490
 
5491
+ // src/tasks/dry-run-publish.ts
5492
+ import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
5493
+
5369
5494
  // src/registry/jsr.ts
5370
5495
  import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
5371
5496
 
5372
- // src/utils/db.ts
5373
- import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
5374
- import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
5375
- import path5 from "node:path";
5376
- var a = "aes-256-cbc";
5377
- var n = statSync(import.meta.dirname);
5378
- var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
5379
- var l = createHash("md5").update(k).digest();
5380
- function e(e2, f) {
5381
- const c = createCipheriv(a, createHash("sha-256").update(f).digest(), l);
5382
- return c.update(e2, "utf8", "hex") + c.final("hex");
5383
- }
5384
- function d(g, h) {
5385
- const d2 = createDecipheriv(a, createHash("sha-256").update(h).digest(), l);
5386
- return d2.update(g, "hex", "utf8") + d2.final("utf8");
5387
- }
5388
- var Db = class {
5389
- constructor() {
5390
- __publicField(this, "path", path5.resolve(import.meta.dirname, ".pubm"));
5391
- try {
5392
- if (!statSync(this.path).isDirectory()) {
5393
- mkdirSync(this.path);
5394
- }
5395
- } catch {
5396
- try {
5397
- mkdirSync(this.path);
5398
- } catch (error) {
5399
- throw new Error(
5400
- `Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
5401
- );
5402
- }
5403
- }
5404
- }
5405
- set(field, value) {
5406
- try {
5407
- writeFileSync(
5408
- path5.resolve(
5409
- this.path,
5410
- Buffer.from(e(field, field)).toString("base64")
5411
- ),
5412
- Buffer.from(e(`${value}`, field)),
5413
- { encoding: "binary" }
5414
- );
5415
- } catch (error) {
5416
- throw new Error(
5417
- `Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
5418
- );
5419
- }
5420
- }
5421
- get(field) {
5422
- const filePath = path5.resolve(
5423
- this.path,
5424
- Buffer.from(e(field, field)).toString("base64")
5425
- );
5426
- let raw;
5427
- try {
5428
- raw = readFileSync(filePath);
5429
- } catch {
5430
- return null;
5431
- }
5432
- try {
5433
- return d(Buffer.from(raw).toString(), field);
5434
- } catch {
5435
- console.warn(
5436
- `Stored token for '${field}' appears corrupted. It will be re-requested.`
5437
- );
5438
- return null;
5439
- }
5440
- }
5441
- };
5442
-
5443
5497
  // src/utils/package-name.ts
5444
5498
  import { builtinModules } from "node:module";
5445
5499
  function isScopedPackage(packageName) {
@@ -5579,12 +5633,9 @@ ${stderr}` : ""}`,
5579
5633
  { throwOnError: true }
5580
5634
  );
5581
5635
  } catch (error) {
5582
- const stderr = error instanceof NonZeroExitError2 ? error.output?.stderr : void 0;
5583
- throw new JsrError(
5584
- `Dry-run failed for \`jsr publish\`${stderr ? `
5585
- ${stderr}` : ""}`,
5586
- { cause: error }
5587
- );
5636
+ throw new JsrError("Failed to run `jsr publish --dry-run`", {
5637
+ cause: error
5638
+ });
5588
5639
  }
5589
5640
  }
5590
5641
  async version() {
@@ -5985,7 +6036,9 @@ var NpmRegistry = class extends Registry {
5985
6036
  try {
5986
6037
  await this.npm(["publish", "--dry-run"]);
5987
6038
  } catch (error) {
5988
- throw this.classifyPublishError(error);
6039
+ throw new NpmError("Failed to run `npm publish --dry-run`", {
6040
+ cause: error
6041
+ });
5989
6042
  }
5990
6043
  }
5991
6044
  async twoFactorAuthMode() {
@@ -6034,69 +6087,78 @@ async function npmRegistry() {
6034
6087
  }
6035
6088
 
6036
6089
  // src/tasks/dry-run-publish.ts
6090
+ var AUTH_ERROR_PATTERNS = [
6091
+ /401/i,
6092
+ /403/i,
6093
+ /unauthorized/i,
6094
+ /forbidden/i,
6095
+ /invalid.token/i,
6096
+ /eotp/i
6097
+ ];
6098
+ function isAuthError(error) {
6099
+ const message = error instanceof Error ? error.message : String(error);
6100
+ return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
6101
+ }
6102
+ async function withTokenRetry(registryKey, task, action) {
6103
+ try {
6104
+ await action();
6105
+ } catch (error) {
6106
+ if (!isAuthError(error)) throw error;
6107
+ const config = TOKEN_CONFIG[registryKey];
6108
+ if (!config) throw error;
6109
+ task.output = `Auth failed. Re-enter ${config.promptLabel}`;
6110
+ const newToken = await task.prompt(ListrEnquirerPromptAdapter).run({
6111
+ type: "password",
6112
+ message: `Re-enter ${config.promptLabel}`
6113
+ });
6114
+ new Db().set(config.dbKey, newToken);
6115
+ process.env[config.envVar] = newToken;
6116
+ await action();
6117
+ }
6118
+ }
6037
6119
  var npmDryRunPublishTask = {
6038
6120
  title: "Dry-run npm publish",
6039
6121
  task: async (_, task) => {
6040
- const npm = await npmRegistry();
6041
6122
  task.output = "Running npm publish --dry-run...";
6042
- await npm.dryRunPublish();
6123
+ await withTokenRetry("npm", task, async () => {
6124
+ const npm = await npmRegistry();
6125
+ await npm.dryRunPublish();
6126
+ });
6043
6127
  }
6044
6128
  };
6045
6129
  var jsrDryRunPublishTask = {
6046
6130
  title: "Dry-run jsr publish",
6047
- skip: () => !JsrClient.token,
6048
6131
  task: async (_, task) => {
6049
- const jsr = await jsrRegistry();
6050
6132
  task.output = "Running jsr publish --dry-run...";
6051
- await jsr.dryRunPublish();
6133
+ await withTokenRetry("jsr", task, async () => {
6134
+ const jsr = await jsrRegistry();
6135
+ await jsr.dryRunPublish();
6136
+ });
6052
6137
  }
6053
6138
  };
6139
+ async function getCrateName2(packagePath) {
6140
+ const eco = new RustEcosystem(packagePath ?? process.cwd());
6141
+ return await eco.packageName();
6142
+ }
6054
6143
  function createCratesDryRunPublishTask(packagePath) {
6055
6144
  const label = packagePath ? ` (${packagePath})` : "";
6056
6145
  return {
6057
- title: `Dry-run cargo publish${label}`,
6146
+ title: `Dry-run crates.io publish${label}`,
6058
6147
  task: async (_, task) => {
6059
- const eco = new RustEcosystem(packagePath ?? process.cwd());
6060
- const packageName = await eco.packageName();
6061
- const registry = new CratesRegistry(packageName);
6062
6148
  task.output = "Running cargo publish --dry-run...";
6063
- await registry.dryRunPublish(packagePath);
6149
+ await withTokenRetry("crates", task, async () => {
6150
+ const packageName = await getCrateName2(packagePath);
6151
+ const registry = new CratesRegistry(packageName);
6152
+ await registry.dryRunPublish(packagePath);
6153
+ });
6064
6154
  }
6065
6155
  };
6066
6156
  }
6067
6157
  var cratesDryRunPublishTask = createCratesDryRunPublishTask();
6068
- function registryDryRunTask(registryKey) {
6069
- switch (registryKey) {
6070
- case "npm":
6071
- return npmDryRunPublishTask;
6072
- case "jsr":
6073
- return jsrDryRunPublishTask;
6074
- case "crates":
6075
- return cratesDryRunPublishTask;
6076
- default:
6077
- return npmDryRunPublishTask;
6078
- }
6079
- }
6080
- var dryRunPublishTask = {
6081
- title: "Validating publish (dry-run)",
6082
- task: (ctx, parentTask) => {
6083
- if (ctx.packages?.length) {
6084
- const tasks = ctx.packages.flatMap(
6085
- (pkg) => pkg.registries.map(
6086
- (registryKey) => registryKey === "crates" ? createCratesDryRunPublishTask(pkg.path) : registryDryRunTask(registryKey)
6087
- )
6088
- );
6089
- return parentTask.newListr(tasks, { concurrent: true });
6090
- }
6091
- return parentTask.newListr(collectRegistries(ctx).map(registryDryRunTask), {
6092
- concurrent: true
6093
- });
6094
- }
6095
- };
6096
6158
 
6097
6159
  // src/tasks/jsr.ts
6098
6160
  import process8 from "node:process";
6099
- import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
6161
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6100
6162
  import npmCli from "@npmcli/promise-spawn";
6101
6163
  var { open } = npmCli;
6102
6164
  var JsrAvailableError = class extends AbstractError {
@@ -6125,7 +6187,7 @@ var jsrAvailableCheckTasks = {
6125
6187
  if (ctx.promptEnabled) {
6126
6188
  const maxAttempts = 3;
6127
6189
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6128
- JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter).run({
6190
+ JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter2).run({
6129
6191
  type: "password",
6130
6192
  message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
6131
6193
  footer: `
@@ -6175,7 +6237,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6175
6237
  )
6176
6238
  )).filter((v) => v !== null);
6177
6239
  if (searchResults.length > 0) {
6178
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6240
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
6179
6241
  type: "select",
6180
6242
  message: "Is there a scoped package you want to publish in the already published list?",
6181
6243
  choices: [
@@ -6193,7 +6255,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6193
6255
  }
6194
6256
  const userName = await new Git().userName();
6195
6257
  task.output = "Select the scope of the package to publish";
6196
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6258
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
6197
6259
  type: "select",
6198
6260
  message: "jsr.json does not exist, and the package name is not scoped. Please select a scope for the 'jsr' package",
6199
6261
  choices: [
@@ -6221,7 +6283,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
6221
6283
  });
6222
6284
  if (jsrName === "specify") {
6223
6285
  while (!isScopedPackage(jsrName)) {
6224
- jsrName = await task.prompt(ListrEnquirerPromptAdapter).run({
6286
+ jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
6225
6287
  type: "input",
6226
6288
  message: "Package name"
6227
6289
  });
@@ -6289,7 +6351,7 @@ var jsrPublishTasks = {
6289
6351
  ${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
6290
6352
  open(urls[0]);
6291
6353
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6292
- await task.prompt(ListrEnquirerPromptAdapter).run({
6354
+ await task.prompt(ListrEnquirerPromptAdapter2).run({
6293
6355
  type: "input",
6294
6356
  message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6295
6357
  });
@@ -6318,7 +6380,7 @@ ${jsr.packageCreationUrls.join("\n")}`
6318
6380
  // src/tasks/npm.ts
6319
6381
  import { spawn } from "node:child_process";
6320
6382
  import process9 from "node:process";
6321
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
6383
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter3 } from "@listr2/prompt-adapter-enquirer";
6322
6384
  import npmCli2 from "@npmcli/promise-spawn";
6323
6385
  var { open: open2 } = npmCli2;
6324
6386
  var NpmAvailableError = class extends AbstractError {
@@ -6415,7 +6477,7 @@ var npmPublishTasks = {
6415
6477
  const maxAttempts = 3;
6416
6478
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
6417
6479
  result = await npm.publish(
6418
- await task.prompt(ListrEnquirerPromptAdapter2).run({
6480
+ await task.prompt(ListrEnquirerPromptAdapter3).run({
6419
6481
  type: "password",
6420
6482
  message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
6421
6483
  })
@@ -6449,8 +6511,64 @@ var npmPublishTasks = {
6449
6511
  }
6450
6512
  };
6451
6513
 
6514
+ // src/tasks/preflight.ts
6515
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter4 } from "@listr2/prompt-adapter-enquirer";
6516
+ import { exec as exec6 } from "tinyexec";
6517
+ var PreflightError = class extends AbstractError {
6518
+ constructor() {
6519
+ super(...arguments);
6520
+ __publicField(this, "name", "Preflight Error");
6521
+ }
6522
+ };
6523
+ async function collectTokens(registries, task) {
6524
+ const existing = loadTokensFromDb(registries);
6525
+ const tokens = { ...existing };
6526
+ for (const registry of registries) {
6527
+ const config = TOKEN_CONFIG[registry];
6528
+ if (!config || tokens[registry]) continue;
6529
+ task.output = `Enter ${config.promptLabel}`;
6530
+ const token = await task.prompt(ListrEnquirerPromptAdapter4).run({
6531
+ type: "password",
6532
+ message: `Enter ${config.promptLabel}`
6533
+ });
6534
+ tokens[registry] = token;
6535
+ new Db().set(config.dbKey, token);
6536
+ }
6537
+ return tokens;
6538
+ }
6539
+ async function syncGhSecrets(tokens) {
6540
+ for (const [registry, token] of Object.entries(tokens)) {
6541
+ const config = TOKEN_CONFIG[registry];
6542
+ if (!config) continue;
6543
+ await exec6("gh", ["secret", "set", config.ghSecretName], {
6544
+ throwOnError: true,
6545
+ nodeOptions: { input: token }
6546
+ });
6547
+ }
6548
+ }
6549
+ async function promptGhSecretsSync(tokens, task) {
6550
+ const shouldSync = await task.prompt(ListrEnquirerPromptAdapter4).run({
6551
+ type: "toggle",
6552
+ message: "Sync tokens to GitHub Secrets?",
6553
+ enabled: "Yes",
6554
+ disabled: "No"
6555
+ });
6556
+ if (shouldSync) {
6557
+ task.output = "Syncing tokens to GitHub Secrets...";
6558
+ try {
6559
+ await syncGhSecrets(tokens);
6560
+ task.output = "Tokens synced to GitHub Secrets.";
6561
+ } catch (error) {
6562
+ throw new PreflightError(
6563
+ "Failed to sync tokens to GitHub Secrets. Ensure `gh` CLI is installed and authenticated (`gh auth login`).",
6564
+ { cause: error }
6565
+ );
6566
+ }
6567
+ }
6568
+ }
6569
+
6452
6570
  // src/tasks/prerequisites-check.ts
6453
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter3 } from "@listr2/prompt-adapter-enquirer";
6571
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter5 } from "@listr2/prompt-adapter-enquirer";
6454
6572
  var PrerequisitesCheckError = class extends AbstractError {
6455
6573
  constructor(message, { cause } = {}) {
6456
6574
  super(message, { cause });
@@ -6470,7 +6588,7 @@ var prerequisitesCheckTask = (options) => {
6470
6588
  title: "Verifying current branch is a release branch",
6471
6589
  task: async (ctx, task) => {
6472
6590
  if (await git.branch() !== ctx.branch) {
6473
- const swtichBranch = await task.prompt(ListrEnquirerPromptAdapter3).run({
6591
+ const swtichBranch = await task.prompt(ListrEnquirerPromptAdapter5).run({
6474
6592
  type: "toggle",
6475
6593
  message: `${warningBadge} The current HEAD branch is not the release target branch. Do you want to switch branch to ${ctx.branch}?`,
6476
6594
  enabled: "Yes",
@@ -6492,7 +6610,7 @@ var prerequisitesCheckTask = (options) => {
6492
6610
  task: async (_2, task) => {
6493
6611
  task.output = "Checking for updates with `git fetch`";
6494
6612
  if ((await git.dryFetch()).trim()) {
6495
- const fetch2 = await task.prompt(ListrEnquirerPromptAdapter3).run({
6613
+ const fetch2 = await task.prompt(ListrEnquirerPromptAdapter5).run({
6496
6614
  type: "toggle",
6497
6615
  message: `${warningBadge} Local history is outdated. Do you want to run \`git fetch\`?`,
6498
6616
  enabled: "Yes",
@@ -6509,7 +6627,7 @@ var prerequisitesCheckTask = (options) => {
6509
6627
  }
6510
6628
  task.output = "Checking for updates with `git pull`";
6511
6629
  if (await git.revisionDiffsCount()) {
6512
- const pull = await task.prompt(ListrEnquirerPromptAdapter3).run({
6630
+ const pull = await task.prompt(ListrEnquirerPromptAdapter5).run({
6513
6631
  type: "toggle",
6514
6632
  message: `${warningBadge} Local history is outdated. Do you want to run \`git pull\`?`,
6515
6633
  enabled: "Yes",
@@ -6531,7 +6649,7 @@ var prerequisitesCheckTask = (options) => {
6531
6649
  task: async (ctx, task) => {
6532
6650
  if (await git.status()) {
6533
6651
  task.output = "Local working tree is not clean.";
6534
- if (!await task.prompt(ListrEnquirerPromptAdapter3).run({
6652
+ if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
6535
6653
  type: "toggle",
6536
6654
  message: `${warningBadge} Local working tree is not clean. Do you want to skip?`,
6537
6655
  enabled: "Yes",
@@ -6556,7 +6674,7 @@ var prerequisitesCheckTask = (options) => {
6556
6674
  return void 0;
6557
6675
  }
6558
6676
  if ((await git.commits(latestTag, "HEAD")).length <= 0) {
6559
- if (!await task.prompt(ListrEnquirerPromptAdapter3).run({
6677
+ if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
6560
6678
  type: "toggle",
6561
6679
  message: `${warningBadge} No commits exist from the latest tag. Do you want to skip?`,
6562
6680
  enabled: "Yes",
@@ -6574,7 +6692,7 @@ var prerequisitesCheckTask = (options) => {
6574
6692
  task: async (ctx, task) => {
6575
6693
  const gitTag = `v${ctx.version}`;
6576
6694
  if (await git.checkTagExist(gitTag)) {
6577
- const deleteTag = await task.prompt(ListrEnquirerPromptAdapter3).run({
6695
+ const deleteTag = await task.prompt(ListrEnquirerPromptAdapter5).run({
6578
6696
  type: "toggle",
6579
6697
  message: `${warningBadge} The Git tag '${gitTag}' already exists. Do you want to delete tag?`,
6580
6698
  enabled: "Yes",
@@ -6596,13 +6714,13 @@ var prerequisitesCheckTask = (options) => {
6596
6714
  };
6597
6715
 
6598
6716
  // src/tasks/required-conditions-check.ts
6599
- import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter4 } from "@listr2/prompt-adapter-enquirer";
6717
+ import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter6 } from "@listr2/prompt-adapter-enquirer";
6600
6718
 
6601
6719
  // src/registry/custom-registry.ts
6602
- import { exec as exec6 } from "tinyexec";
6720
+ import { exec as exec7 } from "tinyexec";
6603
6721
  var CustomRegistry = class extends NpmRegistry {
6604
6722
  async npm(args) {
6605
- const { stdout } = await exec6(
6723
+ const { stdout } = await exec7(
6606
6724
  "npm",
6607
6725
  args.concat("--registry", this.registry),
6608
6726
  { throwOnError: true }
@@ -6704,7 +6822,7 @@ var requiredConditionsCheckTask = (options) => createListr({
6704
6822
  task: async (_3, task) => {
6705
6823
  const jsr = await jsrRegistry();
6706
6824
  if (!await jsr.isInstalled()) {
6707
- const install = await task.prompt(ListrEnquirerPromptAdapter4).run({
6825
+ const install = await task.prompt(ListrEnquirerPromptAdapter6).run({
6708
6826
  type: "toggle",
6709
6827
  message: `${warningBadge} jsr is not installed. Do you want to install jsr?`,
6710
6828
  enabled: "Yes",
@@ -6838,14 +6956,66 @@ async function collectPublishTasks(ctx) {
6838
6956
  }
6839
6957
  return collectRegistries(ctx).map(registryTask);
6840
6958
  }
6959
+ function dryRunRegistryTask(registry) {
6960
+ switch (registry) {
6961
+ case "npm":
6962
+ return npmDryRunPublishTask;
6963
+ case "jsr":
6964
+ return jsrDryRunPublishTask;
6965
+ case "crates":
6966
+ return cratesDryRunPublishTask;
6967
+ default:
6968
+ return npmDryRunPublishTask;
6969
+ }
6970
+ }
6971
+ async function collectDryRunPublishTasks(ctx) {
6972
+ if (ctx.packages?.length) {
6973
+ const nonCratesTasks = ctx.packages.flatMap(
6974
+ (pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => dryRunRegistryTask(reg))
6975
+ );
6976
+ const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
6977
+ if (cratesPaths.length === 0) {
6978
+ return nonCratesTasks;
6979
+ }
6980
+ const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
6981
+ const sequentialCratesTask = {
6982
+ title: "Dry-run crates.io publish (sequential)",
6983
+ task: (_ctx, task) => task.newListr(
6984
+ sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
6985
+ { concurrent: false }
6986
+ )
6987
+ };
6988
+ return [...nonCratesTasks, sequentialCratesTask];
6989
+ }
6990
+ return collectRegistries(ctx).map(dryRunRegistryTask);
6991
+ }
6841
6992
  async function run(options) {
6842
6993
  const ctx = {
6843
6994
  ...options,
6844
6995
  promptEnabled: !isCI2 && process10.stdin.isTTY
6845
6996
  };
6997
+ let cleanupEnv;
6846
6998
  try {
6847
6999
  if (options.contents) process10.chdir(options.contents);
6848
- if (!options.publishOnly) {
7000
+ if (options.preflight) {
7001
+ await createListr({
7002
+ title: "Collecting registry tokens",
7003
+ task: async (ctx2, task) => {
7004
+ const registries2 = collectRegistries(ctx2);
7005
+ const tokens = await collectTokens(registries2, task);
7006
+ await promptGhSecretsSync(tokens, task);
7007
+ cleanupEnv = injectTokensToEnv(tokens);
7008
+ ctx2.promptEnabled = false;
7009
+ }
7010
+ }).run(ctx);
7011
+ await prerequisitesCheckTask({
7012
+ skip: options.skipPrerequisitesCheck
7013
+ }).run(ctx);
7014
+ await requiredConditionsCheckTask({
7015
+ skip: options.skipConditionsCheck
7016
+ }).run(ctx);
7017
+ }
7018
+ if (!options.publishOnly && !options.preflight) {
6849
7019
  await prerequisitesCheckTask({
6850
7020
  skip: options.skipPrerequisitesCheck
6851
7021
  }).run(ctx);
@@ -6859,14 +7029,14 @@ async function run(options) {
6859
7029
  task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
6860
7030
  concurrent: true
6861
7031
  })
6862
- } : [
7032
+ } : options.preflight ? [
6863
7033
  {
6864
7034
  skip: options.skipTests,
6865
7035
  title: "Running tests",
6866
7036
  task: async (ctx2) => {
6867
7037
  const packageManager = await getPackageManager();
6868
7038
  try {
6869
- await exec7(packageManager, ["run", ctx2.testScript], {
7039
+ await exec8(packageManager, ["run", ctx2.testScript], {
6870
7040
  throwOnError: true
6871
7041
  });
6872
7042
  } catch (error) {
@@ -6883,7 +7053,7 @@ async function run(options) {
6883
7053
  task: async (ctx2) => {
6884
7054
  const packageManager = await getPackageManager();
6885
7055
  try {
6886
- await exec7(packageManager, ["run", ctx2.buildScript], {
7056
+ await exec8(packageManager, ["run", ctx2.buildScript], {
6887
7057
  throwOnError: true
6888
7058
  });
6889
7059
  } catch (error) {
@@ -6895,8 +7065,45 @@ async function run(options) {
6895
7065
  }
6896
7066
  },
6897
7067
  {
6898
- ...dryRunPublishTask,
6899
- skip: (ctx2) => options.skipPublish || !!ctx2.preview
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], {
7098
+ throwOnError: true
7099
+ });
7100
+ } catch (error) {
7101
+ throw new AbstractError(
7102
+ `Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
7103
+ { cause: error }
7104
+ );
7105
+ }
7106
+ }
6900
7107
  },
6901
7108
  {
6902
7109
  title: "Bumping version",
@@ -7012,13 +7219,24 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
7012
7219
  parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
7013
7220
  }
7014
7221
  }
7015
- console.log(
7016
- `
7222
+ if (options.preflight) {
7223
+ cleanupEnv?.();
7224
+ console.log(
7225
+ `
7226
+
7227
+ \u2705 Preflight check passed. CI publish should succeed for ${parts.join(", ")}.
7228
+ `
7229
+ );
7230
+ } else {
7231
+ console.log(
7232
+ `
7017
7233
 
7018
7234
  \u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
7019
7235
  `
7020
- );
7236
+ );
7237
+ }
7021
7238
  } catch (e2) {
7239
+ cleanupEnv?.();
7022
7240
  consoleError(e2);
7023
7241
  await rollback();
7024
7242
  process10.exit(1);