pubm 0.2.0 → 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/bin/cli.js +736 -356
- package/dist/index.cjs +446 -104
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +445 -103
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4486,7 +4486,7 @@ var Listr = (_a23 = class {
|
|
|
4486
4486
|
// src/tasks/runner.ts
|
|
4487
4487
|
var import_semver3 = __toESM(require("semver"), 1);
|
|
4488
4488
|
var import_std_env = require("std-env");
|
|
4489
|
-
var
|
|
4489
|
+
var import_tinyexec7 = require("tinyexec");
|
|
4490
4490
|
|
|
4491
4491
|
// src/error.ts
|
|
4492
4492
|
var AbstractError = class extends Error {
|
|
@@ -5207,8 +5207,131 @@ function collectRegistries(ctx) {
|
|
|
5207
5207
|
return ctx.registries;
|
|
5208
5208
|
}
|
|
5209
5209
|
|
|
5210
|
-
// src/
|
|
5210
|
+
// src/utils/db.ts
|
|
5211
|
+
var import_node_crypto = require("crypto");
|
|
5212
|
+
var import_node_fs = require("fs");
|
|
5211
5213
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
5214
|
+
var import_meta = {};
|
|
5215
|
+
var a = "aes-256-cbc";
|
|
5216
|
+
var n = (0, import_node_fs.statSync)(import_meta.dirname);
|
|
5217
|
+
var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
|
|
5218
|
+
var l = (0, import_node_crypto.createHash)("md5").update(k).digest();
|
|
5219
|
+
function e(e2, f) {
|
|
5220
|
+
const c = (0, import_node_crypto.createCipheriv)(a, (0, import_node_crypto.createHash)("sha-256").update(f).digest(), l);
|
|
5221
|
+
return c.update(e2, "utf8", "hex") + c.final("hex");
|
|
5222
|
+
}
|
|
5223
|
+
function d(g, h) {
|
|
5224
|
+
const d2 = (0, import_node_crypto.createDecipheriv)(a, (0, import_node_crypto.createHash)("sha-256").update(h).digest(), l);
|
|
5225
|
+
return d2.update(g, "hex", "utf8") + d2.final("utf8");
|
|
5226
|
+
}
|
|
5227
|
+
var Db = class {
|
|
5228
|
+
constructor() {
|
|
5229
|
+
__publicField(this, "path", import_node_path4.default.resolve(import_meta.dirname, ".pubm"));
|
|
5230
|
+
try {
|
|
5231
|
+
if (!(0, import_node_fs.statSync)(this.path).isDirectory()) {
|
|
5232
|
+
(0, import_node_fs.mkdirSync)(this.path);
|
|
5233
|
+
}
|
|
5234
|
+
} catch {
|
|
5235
|
+
try {
|
|
5236
|
+
(0, import_node_fs.mkdirSync)(this.path);
|
|
5237
|
+
} catch (error) {
|
|
5238
|
+
throw new Error(
|
|
5239
|
+
`Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
|
|
5240
|
+
);
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
set(field, value) {
|
|
5245
|
+
try {
|
|
5246
|
+
(0, import_node_fs.writeFileSync)(
|
|
5247
|
+
import_node_path4.default.resolve(
|
|
5248
|
+
this.path,
|
|
5249
|
+
Buffer.from(e(field, field)).toString("base64")
|
|
5250
|
+
),
|
|
5251
|
+
Buffer.from(e(`${value}`, field)),
|
|
5252
|
+
{ encoding: "binary" }
|
|
5253
|
+
);
|
|
5254
|
+
} catch (error) {
|
|
5255
|
+
throw new Error(
|
|
5256
|
+
`Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
|
|
5257
|
+
);
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
get(field) {
|
|
5261
|
+
const filePath = import_node_path4.default.resolve(
|
|
5262
|
+
this.path,
|
|
5263
|
+
Buffer.from(e(field, field)).toString("base64")
|
|
5264
|
+
);
|
|
5265
|
+
let raw;
|
|
5266
|
+
try {
|
|
5267
|
+
raw = (0, import_node_fs.readFileSync)(filePath);
|
|
5268
|
+
} catch {
|
|
5269
|
+
return null;
|
|
5270
|
+
}
|
|
5271
|
+
try {
|
|
5272
|
+
return d(Buffer.from(raw).toString(), field);
|
|
5273
|
+
} catch {
|
|
5274
|
+
console.warn(
|
|
5275
|
+
`Stored token for '${field}' appears corrupted. It will be re-requested.`
|
|
5276
|
+
);
|
|
5277
|
+
return null;
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
};
|
|
5281
|
+
|
|
5282
|
+
// src/utils/token.ts
|
|
5283
|
+
var TOKEN_CONFIG = {
|
|
5284
|
+
npm: {
|
|
5285
|
+
envVar: "NODE_AUTH_TOKEN",
|
|
5286
|
+
dbKey: "npm-token",
|
|
5287
|
+
ghSecretName: "NODE_AUTH_TOKEN",
|
|
5288
|
+
promptLabel: "npm access token"
|
|
5289
|
+
},
|
|
5290
|
+
jsr: {
|
|
5291
|
+
envVar: "JSR_TOKEN",
|
|
5292
|
+
dbKey: "jsr-token",
|
|
5293
|
+
ghSecretName: "JSR_TOKEN",
|
|
5294
|
+
promptLabel: "jsr API token"
|
|
5295
|
+
},
|
|
5296
|
+
crates: {
|
|
5297
|
+
envVar: "CARGO_REGISTRY_TOKEN",
|
|
5298
|
+
dbKey: "cargo-token",
|
|
5299
|
+
ghSecretName: "CARGO_REGISTRY_TOKEN",
|
|
5300
|
+
promptLabel: "crates.io API token"
|
|
5301
|
+
}
|
|
5302
|
+
};
|
|
5303
|
+
function loadTokensFromDb(registries) {
|
|
5304
|
+
const db = new Db();
|
|
5305
|
+
const tokens = {};
|
|
5306
|
+
for (const registry of registries) {
|
|
5307
|
+
const config = TOKEN_CONFIG[registry];
|
|
5308
|
+
if (!config) continue;
|
|
5309
|
+
const token = db.get(config.dbKey);
|
|
5310
|
+
if (token) tokens[registry] = token;
|
|
5311
|
+
}
|
|
5312
|
+
return tokens;
|
|
5313
|
+
}
|
|
5314
|
+
function injectTokensToEnv(tokens) {
|
|
5315
|
+
const originals = {};
|
|
5316
|
+
for (const [registry, token] of Object.entries(tokens)) {
|
|
5317
|
+
const config = TOKEN_CONFIG[registry];
|
|
5318
|
+
if (!config) continue;
|
|
5319
|
+
originals[config.envVar] = process.env[config.envVar];
|
|
5320
|
+
process.env[config.envVar] = token;
|
|
5321
|
+
}
|
|
5322
|
+
return () => {
|
|
5323
|
+
for (const [envVar, original] of Object.entries(originals)) {
|
|
5324
|
+
if (original === void 0) {
|
|
5325
|
+
delete process.env[envVar];
|
|
5326
|
+
} else {
|
|
5327
|
+
process.env[envVar] = original;
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
|
|
5333
|
+
// src/registry/crates.ts
|
|
5334
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
5212
5335
|
var import_tinyexec2 = require("tinyexec");
|
|
5213
5336
|
|
|
5214
5337
|
// src/registry/registry.ts
|
|
@@ -5217,6 +5340,8 @@ var Registry = class {
|
|
|
5217
5340
|
this.packageName = packageName;
|
|
5218
5341
|
this.registry = registry;
|
|
5219
5342
|
}
|
|
5343
|
+
async dryRunPublish(_manifestDir) {
|
|
5344
|
+
}
|
|
5220
5345
|
};
|
|
5221
5346
|
|
|
5222
5347
|
// src/registry/crates.ts
|
|
@@ -5286,7 +5411,7 @@ var CratesRegistry = class extends Registry {
|
|
|
5286
5411
|
try {
|
|
5287
5412
|
const args = ["publish"];
|
|
5288
5413
|
if (manifestDir) {
|
|
5289
|
-
args.push("--manifest-path",
|
|
5414
|
+
args.push("--manifest-path", import_node_path5.default.join(manifestDir, "Cargo.toml"));
|
|
5290
5415
|
}
|
|
5291
5416
|
await (0, import_tinyexec2.exec)("cargo", args, { throwOnError: true });
|
|
5292
5417
|
return true;
|
|
@@ -5297,6 +5422,20 @@ ${stderr}` : "Failed to run `cargo publish`";
|
|
|
5297
5422
|
throw new CratesError(message, { cause: error });
|
|
5298
5423
|
}
|
|
5299
5424
|
}
|
|
5425
|
+
async dryRunPublish(manifestDir) {
|
|
5426
|
+
try {
|
|
5427
|
+
const args = ["publish", "--dry-run"];
|
|
5428
|
+
if (manifestDir) {
|
|
5429
|
+
args.push("--manifest-path", import_node_path5.default.join(manifestDir, "Cargo.toml"));
|
|
5430
|
+
}
|
|
5431
|
+
await (0, import_tinyexec2.exec)("cargo", args, { throwOnError: true });
|
|
5432
|
+
} catch (error) {
|
|
5433
|
+
const stderr = error instanceof import_tinyexec2.NonZeroExitError ? error.output?.stderr : void 0;
|
|
5434
|
+
const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
|
|
5435
|
+
${stderr}` : "Failed to run `cargo publish --dry-run`";
|
|
5436
|
+
throw new CratesError(message, { cause: error });
|
|
5437
|
+
}
|
|
5438
|
+
}
|
|
5300
5439
|
async isPublished() {
|
|
5301
5440
|
try {
|
|
5302
5441
|
const response = await fetch(
|
|
@@ -5383,86 +5522,12 @@ function createCratesPublishTask(packagePath) {
|
|
|
5383
5522
|
var cratesAvailableCheckTasks = createCratesAvailableCheckTask();
|
|
5384
5523
|
var cratesPublishTasks = createCratesPublishTask();
|
|
5385
5524
|
|
|
5386
|
-
// src/tasks/
|
|
5387
|
-
var import_node_process6 = __toESM(require("process"), 1);
|
|
5525
|
+
// src/tasks/dry-run-publish.ts
|
|
5388
5526
|
var import_prompt_adapter_enquirer = require("@listr2/prompt-adapter-enquirer");
|
|
5389
|
-
var import_promise_spawn = __toESM(require("@npmcli/promise-spawn"), 1);
|
|
5390
5527
|
|
|
5391
5528
|
// src/registry/jsr.ts
|
|
5392
5529
|
var import_tinyexec3 = require("tinyexec");
|
|
5393
5530
|
|
|
5394
|
-
// src/utils/db.ts
|
|
5395
|
-
var import_node_crypto = require("crypto");
|
|
5396
|
-
var import_node_fs = require("fs");
|
|
5397
|
-
var import_node_path5 = __toESM(require("path"), 1);
|
|
5398
|
-
var import_meta = {};
|
|
5399
|
-
var a = "aes-256-cbc";
|
|
5400
|
-
var n = (0, import_node_fs.statSync)(import_meta.dirname);
|
|
5401
|
-
var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
|
|
5402
|
-
var l = (0, import_node_crypto.createHash)("md5").update(k).digest();
|
|
5403
|
-
function e(e2, f) {
|
|
5404
|
-
const c = (0, import_node_crypto.createCipheriv)(a, (0, import_node_crypto.createHash)("sha-256").update(f).digest(), l);
|
|
5405
|
-
return c.update(e2, "utf8", "hex") + c.final("hex");
|
|
5406
|
-
}
|
|
5407
|
-
function d(g, h) {
|
|
5408
|
-
const d2 = (0, import_node_crypto.createDecipheriv)(a, (0, import_node_crypto.createHash)("sha-256").update(h).digest(), l);
|
|
5409
|
-
return d2.update(g, "hex", "utf8") + d2.final("utf8");
|
|
5410
|
-
}
|
|
5411
|
-
var Db = class {
|
|
5412
|
-
constructor() {
|
|
5413
|
-
__publicField(this, "path", import_node_path5.default.resolve(import_meta.dirname, ".pubm"));
|
|
5414
|
-
try {
|
|
5415
|
-
if (!(0, import_node_fs.statSync)(this.path).isDirectory()) {
|
|
5416
|
-
(0, import_node_fs.mkdirSync)(this.path);
|
|
5417
|
-
}
|
|
5418
|
-
} catch {
|
|
5419
|
-
try {
|
|
5420
|
-
(0, import_node_fs.mkdirSync)(this.path);
|
|
5421
|
-
} catch (error) {
|
|
5422
|
-
throw new Error(
|
|
5423
|
-
`Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
|
|
5424
|
-
);
|
|
5425
|
-
}
|
|
5426
|
-
}
|
|
5427
|
-
}
|
|
5428
|
-
set(field, value) {
|
|
5429
|
-
try {
|
|
5430
|
-
(0, import_node_fs.writeFileSync)(
|
|
5431
|
-
import_node_path5.default.resolve(
|
|
5432
|
-
this.path,
|
|
5433
|
-
Buffer.from(e(field, field)).toString("base64")
|
|
5434
|
-
),
|
|
5435
|
-
Buffer.from(e(`${value}`, field)),
|
|
5436
|
-
{ encoding: "binary" }
|
|
5437
|
-
);
|
|
5438
|
-
} catch (error) {
|
|
5439
|
-
throw new Error(
|
|
5440
|
-
`Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
|
|
5441
|
-
);
|
|
5442
|
-
}
|
|
5443
|
-
}
|
|
5444
|
-
get(field) {
|
|
5445
|
-
const filePath = import_node_path5.default.resolve(
|
|
5446
|
-
this.path,
|
|
5447
|
-
Buffer.from(e(field, field)).toString("base64")
|
|
5448
|
-
);
|
|
5449
|
-
let raw;
|
|
5450
|
-
try {
|
|
5451
|
-
raw = (0, import_node_fs.readFileSync)(filePath);
|
|
5452
|
-
} catch {
|
|
5453
|
-
return null;
|
|
5454
|
-
}
|
|
5455
|
-
try {
|
|
5456
|
-
return d(Buffer.from(raw).toString(), field);
|
|
5457
|
-
} catch {
|
|
5458
|
-
console.warn(
|
|
5459
|
-
`Stored token for '${field}' appears corrupted. It will be re-requested.`
|
|
5460
|
-
);
|
|
5461
|
-
return null;
|
|
5462
|
-
}
|
|
5463
|
-
}
|
|
5464
|
-
};
|
|
5465
|
-
|
|
5466
5531
|
// src/utils/package-name.ts
|
|
5467
5532
|
var import_node_module = require("module");
|
|
5468
5533
|
function isScopedPackage(packageName) {
|
|
@@ -5589,6 +5654,25 @@ ${stderr}` : ""}`,
|
|
|
5589
5654
|
);
|
|
5590
5655
|
}
|
|
5591
5656
|
}
|
|
5657
|
+
async dryRunPublish() {
|
|
5658
|
+
try {
|
|
5659
|
+
await (0, import_tinyexec3.exec)(
|
|
5660
|
+
"jsr",
|
|
5661
|
+
[
|
|
5662
|
+
"publish",
|
|
5663
|
+
"--dry-run",
|
|
5664
|
+
"--allow-dirty",
|
|
5665
|
+
"--token",
|
|
5666
|
+
`${JsrClient.token}`
|
|
5667
|
+
],
|
|
5668
|
+
{ throwOnError: true }
|
|
5669
|
+
);
|
|
5670
|
+
} catch (error) {
|
|
5671
|
+
throw new JsrError("Failed to run `jsr publish --dry-run`", {
|
|
5672
|
+
cause: error
|
|
5673
|
+
});
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5592
5676
|
async version() {
|
|
5593
5677
|
return await this.jsr(["--version"]);
|
|
5594
5678
|
}
|
|
@@ -5983,6 +6067,24 @@ var NpmRegistry = class extends Registry {
|
|
|
5983
6067
|
throw this.classifyPublishError(error);
|
|
5984
6068
|
}
|
|
5985
6069
|
}
|
|
6070
|
+
async dryRunPublish() {
|
|
6071
|
+
try {
|
|
6072
|
+
await this.npm(["publish", "--dry-run"]);
|
|
6073
|
+
} catch (error) {
|
|
6074
|
+
throw new NpmError("Failed to run `npm publish --dry-run`", {
|
|
6075
|
+
cause: error
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
async twoFactorAuthMode() {
|
|
6080
|
+
try {
|
|
6081
|
+
const output = await this.npm(["profile", "get", "--json"]);
|
|
6082
|
+
const profile = JSON.parse(output);
|
|
6083
|
+
return profile?.tfa?.mode ?? null;
|
|
6084
|
+
} catch {
|
|
6085
|
+
return null;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
5986
6088
|
async isPackageNameAvaliable() {
|
|
5987
6089
|
return isValidPackageName(this.packageName);
|
|
5988
6090
|
}
|
|
@@ -6019,7 +6121,80 @@ async function npmRegistry() {
|
|
|
6019
6121
|
return new NpmRegistry(packageJson.name);
|
|
6020
6122
|
}
|
|
6021
6123
|
|
|
6124
|
+
// src/tasks/dry-run-publish.ts
|
|
6125
|
+
var AUTH_ERROR_PATTERNS = [
|
|
6126
|
+
/401/i,
|
|
6127
|
+
/403/i,
|
|
6128
|
+
/unauthorized/i,
|
|
6129
|
+
/forbidden/i,
|
|
6130
|
+
/invalid.token/i,
|
|
6131
|
+
/eotp/i
|
|
6132
|
+
];
|
|
6133
|
+
function isAuthError(error) {
|
|
6134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6135
|
+
return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
6136
|
+
}
|
|
6137
|
+
async function withTokenRetry(registryKey, task, action) {
|
|
6138
|
+
try {
|
|
6139
|
+
await action();
|
|
6140
|
+
} catch (error) {
|
|
6141
|
+
if (!isAuthError(error)) throw error;
|
|
6142
|
+
const config = TOKEN_CONFIG[registryKey];
|
|
6143
|
+
if (!config) throw error;
|
|
6144
|
+
task.output = `Auth failed. Re-enter ${config.promptLabel}`;
|
|
6145
|
+
const newToken = await task.prompt(import_prompt_adapter_enquirer.ListrEnquirerPromptAdapter).run({
|
|
6146
|
+
type: "password",
|
|
6147
|
+
message: `Re-enter ${config.promptLabel}`
|
|
6148
|
+
});
|
|
6149
|
+
new Db().set(config.dbKey, newToken);
|
|
6150
|
+
process.env[config.envVar] = newToken;
|
|
6151
|
+
await action();
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
var npmDryRunPublishTask = {
|
|
6155
|
+
title: "Dry-run npm publish",
|
|
6156
|
+
task: async (_, task) => {
|
|
6157
|
+
task.output = "Running npm publish --dry-run...";
|
|
6158
|
+
await withTokenRetry("npm", task, async () => {
|
|
6159
|
+
const npm = await npmRegistry();
|
|
6160
|
+
await npm.dryRunPublish();
|
|
6161
|
+
});
|
|
6162
|
+
}
|
|
6163
|
+
};
|
|
6164
|
+
var jsrDryRunPublishTask = {
|
|
6165
|
+
title: "Dry-run jsr publish",
|
|
6166
|
+
task: async (_, task) => {
|
|
6167
|
+
task.output = "Running jsr publish --dry-run...";
|
|
6168
|
+
await withTokenRetry("jsr", task, async () => {
|
|
6169
|
+
const jsr = await jsrRegistry();
|
|
6170
|
+
await jsr.dryRunPublish();
|
|
6171
|
+
});
|
|
6172
|
+
}
|
|
6173
|
+
};
|
|
6174
|
+
async function getCrateName2(packagePath) {
|
|
6175
|
+
const eco = new RustEcosystem(packagePath ?? process.cwd());
|
|
6176
|
+
return await eco.packageName();
|
|
6177
|
+
}
|
|
6178
|
+
function createCratesDryRunPublishTask(packagePath) {
|
|
6179
|
+
const label = packagePath ? ` (${packagePath})` : "";
|
|
6180
|
+
return {
|
|
6181
|
+
title: `Dry-run crates.io publish${label}`,
|
|
6182
|
+
task: async (_, task) => {
|
|
6183
|
+
task.output = "Running cargo publish --dry-run...";
|
|
6184
|
+
await withTokenRetry("crates", task, async () => {
|
|
6185
|
+
const packageName = await getCrateName2(packagePath);
|
|
6186
|
+
const registry = new CratesRegistry(packageName);
|
|
6187
|
+
await registry.dryRunPublish(packagePath);
|
|
6188
|
+
});
|
|
6189
|
+
}
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6192
|
+
var cratesDryRunPublishTask = createCratesDryRunPublishTask();
|
|
6193
|
+
|
|
6022
6194
|
// src/tasks/jsr.ts
|
|
6195
|
+
var import_node_process6 = __toESM(require("process"), 1);
|
|
6196
|
+
var import_prompt_adapter_enquirer2 = require("@listr2/prompt-adapter-enquirer");
|
|
6197
|
+
var import_promise_spawn = __toESM(require("@npmcli/promise-spawn"), 1);
|
|
6023
6198
|
var { open } = import_promise_spawn.default;
|
|
6024
6199
|
var JsrAvailableError = class extends AbstractError {
|
|
6025
6200
|
constructor(message, { cause } = {}) {
|
|
@@ -6047,7 +6222,7 @@ var jsrAvailableCheckTasks = {
|
|
|
6047
6222
|
if (ctx.promptEnabled) {
|
|
6048
6223
|
const maxAttempts = 3;
|
|
6049
6224
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6050
|
-
JsrClient.token = await task.prompt(
|
|
6225
|
+
JsrClient.token = await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
|
|
6051
6226
|
type: "password",
|
|
6052
6227
|
message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
|
|
6053
6228
|
footer: `
|
|
@@ -6097,7 +6272,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6097
6272
|
)
|
|
6098
6273
|
)).filter((v) => v !== null);
|
|
6099
6274
|
if (searchResults.length > 0) {
|
|
6100
|
-
jsrName = await task.prompt(
|
|
6275
|
+
jsrName = await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
|
|
6101
6276
|
type: "select",
|
|
6102
6277
|
message: "Is there a scoped package you want to publish in the already published list?",
|
|
6103
6278
|
choices: [
|
|
@@ -6115,7 +6290,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6115
6290
|
}
|
|
6116
6291
|
const userName = await new Git().userName();
|
|
6117
6292
|
task.output = "Select the scope of the package to publish";
|
|
6118
|
-
jsrName = await task.prompt(
|
|
6293
|
+
jsrName = await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
|
|
6119
6294
|
type: "select",
|
|
6120
6295
|
message: "jsr.json does not exist, and the package name is not scoped. Please select a scope for the 'jsr' package",
|
|
6121
6296
|
choices: [
|
|
@@ -6143,7 +6318,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6143
6318
|
});
|
|
6144
6319
|
if (jsrName === "specify") {
|
|
6145
6320
|
while (!isScopedPackage(jsrName)) {
|
|
6146
|
-
jsrName = await task.prompt(
|
|
6321
|
+
jsrName = await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
|
|
6147
6322
|
type: "input",
|
|
6148
6323
|
message: "Package name"
|
|
6149
6324
|
});
|
|
@@ -6211,7 +6386,7 @@ var jsrPublishTasks = {
|
|
|
6211
6386
|
${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
|
|
6212
6387
|
open(urls[0]);
|
|
6213
6388
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6214
|
-
await task.prompt(
|
|
6389
|
+
await task.prompt(import_prompt_adapter_enquirer2.ListrEnquirerPromptAdapter).run({
|
|
6215
6390
|
type: "input",
|
|
6216
6391
|
message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
|
|
6217
6392
|
});
|
|
@@ -6240,7 +6415,7 @@ ${jsr.packageCreationUrls.join("\n")}`
|
|
|
6240
6415
|
// src/tasks/npm.ts
|
|
6241
6416
|
var import_node_child_process = require("child_process");
|
|
6242
6417
|
var import_node_process7 = __toESM(require("process"), 1);
|
|
6243
|
-
var
|
|
6418
|
+
var import_prompt_adapter_enquirer3 = require("@listr2/prompt-adapter-enquirer");
|
|
6244
6419
|
var import_promise_spawn2 = __toESM(require("@npmcli/promise-spawn"), 1);
|
|
6245
6420
|
var { open: open2 } = import_promise_spawn2.default;
|
|
6246
6421
|
var NpmAvailableError = class extends AbstractError {
|
|
@@ -6252,7 +6427,6 @@ var NpmAvailableError = class extends AbstractError {
|
|
|
6252
6427
|
};
|
|
6253
6428
|
var npmAvailableCheckTasks = {
|
|
6254
6429
|
title: "Checking npm avaliable for publising",
|
|
6255
|
-
skip: (ctx) => !!ctx.preview,
|
|
6256
6430
|
task: async (ctx, task) => {
|
|
6257
6431
|
const npm = await npmRegistry();
|
|
6258
6432
|
if (!await npm.isLoggedIn()) {
|
|
@@ -6315,6 +6489,14 @@ var npmAvailableCheckTasks = {
|
|
|
6315
6489
|
More information: ${link2("npm naming rules", "https://github.com/npm/validate-npm-package-name?tab=readme-ov-file#naming-rules")}`
|
|
6316
6490
|
);
|
|
6317
6491
|
}
|
|
6492
|
+
if (!ctx.promptEnabled) {
|
|
6493
|
+
const tfaMode = await npm.twoFactorAuthMode();
|
|
6494
|
+
if (tfaMode === "auth-and-writes") {
|
|
6495
|
+
throw new NpmAvailableError(
|
|
6496
|
+
`npm account has 2FA enabled for writes (auth-and-writes). CI publish will fail with EOTP. Use an automation token or configure granular access token at https://www.npmjs.com/package/${npm.packageName}/access`
|
|
6497
|
+
);
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6318
6500
|
}
|
|
6319
6501
|
};
|
|
6320
6502
|
var npmPublishTasks = {
|
|
@@ -6330,7 +6512,7 @@ var npmPublishTasks = {
|
|
|
6330
6512
|
const maxAttempts = 3;
|
|
6331
6513
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6332
6514
|
result = await npm.publish(
|
|
6333
|
-
await task.prompt(
|
|
6515
|
+
await task.prompt(import_prompt_adapter_enquirer3.ListrEnquirerPromptAdapter).run({
|
|
6334
6516
|
type: "password",
|
|
6335
6517
|
message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
|
|
6336
6518
|
})
|
|
@@ -6364,8 +6546,64 @@ var npmPublishTasks = {
|
|
|
6364
6546
|
}
|
|
6365
6547
|
};
|
|
6366
6548
|
|
|
6549
|
+
// src/tasks/preflight.ts
|
|
6550
|
+
var import_prompt_adapter_enquirer4 = require("@listr2/prompt-adapter-enquirer");
|
|
6551
|
+
var import_tinyexec5 = require("tinyexec");
|
|
6552
|
+
var PreflightError = class extends AbstractError {
|
|
6553
|
+
constructor() {
|
|
6554
|
+
super(...arguments);
|
|
6555
|
+
__publicField(this, "name", "Preflight Error");
|
|
6556
|
+
}
|
|
6557
|
+
};
|
|
6558
|
+
async function collectTokens(registries, task) {
|
|
6559
|
+
const existing = loadTokensFromDb(registries);
|
|
6560
|
+
const tokens = { ...existing };
|
|
6561
|
+
for (const registry of registries) {
|
|
6562
|
+
const config = TOKEN_CONFIG[registry];
|
|
6563
|
+
if (!config || tokens[registry]) continue;
|
|
6564
|
+
task.output = `Enter ${config.promptLabel}`;
|
|
6565
|
+
const token = await task.prompt(import_prompt_adapter_enquirer4.ListrEnquirerPromptAdapter).run({
|
|
6566
|
+
type: "password",
|
|
6567
|
+
message: `Enter ${config.promptLabel}`
|
|
6568
|
+
});
|
|
6569
|
+
tokens[registry] = token;
|
|
6570
|
+
new Db().set(config.dbKey, token);
|
|
6571
|
+
}
|
|
6572
|
+
return tokens;
|
|
6573
|
+
}
|
|
6574
|
+
async function syncGhSecrets(tokens) {
|
|
6575
|
+
for (const [registry, token] of Object.entries(tokens)) {
|
|
6576
|
+
const config = TOKEN_CONFIG[registry];
|
|
6577
|
+
if (!config) continue;
|
|
6578
|
+
await (0, import_tinyexec5.exec)("gh", ["secret", "set", config.ghSecretName], {
|
|
6579
|
+
throwOnError: true,
|
|
6580
|
+
nodeOptions: { input: token }
|
|
6581
|
+
});
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
async function promptGhSecretsSync(tokens, task) {
|
|
6585
|
+
const shouldSync = await task.prompt(import_prompt_adapter_enquirer4.ListrEnquirerPromptAdapter).run({
|
|
6586
|
+
type: "toggle",
|
|
6587
|
+
message: "Sync tokens to GitHub Secrets?",
|
|
6588
|
+
enabled: "Yes",
|
|
6589
|
+
disabled: "No"
|
|
6590
|
+
});
|
|
6591
|
+
if (shouldSync) {
|
|
6592
|
+
task.output = "Syncing tokens to GitHub Secrets...";
|
|
6593
|
+
try {
|
|
6594
|
+
await syncGhSecrets(tokens);
|
|
6595
|
+
task.output = "Tokens synced to GitHub Secrets.";
|
|
6596
|
+
} catch (error) {
|
|
6597
|
+
throw new PreflightError(
|
|
6598
|
+
"Failed to sync tokens to GitHub Secrets. Ensure `gh` CLI is installed and authenticated (`gh auth login`).",
|
|
6599
|
+
{ cause: error }
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
}
|
|
6603
|
+
}
|
|
6604
|
+
|
|
6367
6605
|
// src/tasks/prerequisites-check.ts
|
|
6368
|
-
var
|
|
6606
|
+
var import_prompt_adapter_enquirer5 = require("@listr2/prompt-adapter-enquirer");
|
|
6369
6607
|
var PrerequisitesCheckError = class extends AbstractError {
|
|
6370
6608
|
constructor(message, { cause } = {}) {
|
|
6371
6609
|
super(message, { cause });
|
|
@@ -6385,7 +6623,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6385
6623
|
title: "Verifying current branch is a release branch",
|
|
6386
6624
|
task: async (ctx, task) => {
|
|
6387
6625
|
if (await git.branch() !== ctx.branch) {
|
|
6388
|
-
const swtichBranch = await task.prompt(
|
|
6626
|
+
const swtichBranch = await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6389
6627
|
type: "toggle",
|
|
6390
6628
|
message: `${warningBadge} The current HEAD branch is not the release target branch. Do you want to switch branch to ${ctx.branch}?`,
|
|
6391
6629
|
enabled: "Yes",
|
|
@@ -6407,7 +6645,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6407
6645
|
task: async (_2, task) => {
|
|
6408
6646
|
task.output = "Checking for updates with `git fetch`";
|
|
6409
6647
|
if ((await git.dryFetch()).trim()) {
|
|
6410
|
-
const fetch2 = await task.prompt(
|
|
6648
|
+
const fetch2 = await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6411
6649
|
type: "toggle",
|
|
6412
6650
|
message: `${warningBadge} Local history is outdated. Do you want to run \`git fetch\`?`,
|
|
6413
6651
|
enabled: "Yes",
|
|
@@ -6424,7 +6662,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6424
6662
|
}
|
|
6425
6663
|
task.output = "Checking for updates with `git pull`";
|
|
6426
6664
|
if (await git.revisionDiffsCount()) {
|
|
6427
|
-
const pull = await task.prompt(
|
|
6665
|
+
const pull = await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6428
6666
|
type: "toggle",
|
|
6429
6667
|
message: `${warningBadge} Local history is outdated. Do you want to run \`git pull\`?`,
|
|
6430
6668
|
enabled: "Yes",
|
|
@@ -6446,7 +6684,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6446
6684
|
task: async (ctx, task) => {
|
|
6447
6685
|
if (await git.status()) {
|
|
6448
6686
|
task.output = "Local working tree is not clean.";
|
|
6449
|
-
if (!await task.prompt(
|
|
6687
|
+
if (!await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6450
6688
|
type: "toggle",
|
|
6451
6689
|
message: `${warningBadge} Local working tree is not clean. Do you want to skip?`,
|
|
6452
6690
|
enabled: "Yes",
|
|
@@ -6471,7 +6709,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6471
6709
|
return void 0;
|
|
6472
6710
|
}
|
|
6473
6711
|
if ((await git.commits(latestTag, "HEAD")).length <= 0) {
|
|
6474
|
-
if (!await task.prompt(
|
|
6712
|
+
if (!await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6475
6713
|
type: "toggle",
|
|
6476
6714
|
message: `${warningBadge} No commits exist from the latest tag. Do you want to skip?`,
|
|
6477
6715
|
enabled: "Yes",
|
|
@@ -6489,7 +6727,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6489
6727
|
task: async (ctx, task) => {
|
|
6490
6728
|
const gitTag = `v${ctx.version}`;
|
|
6491
6729
|
if (await git.checkTagExist(gitTag)) {
|
|
6492
|
-
const deleteTag = await task.prompt(
|
|
6730
|
+
const deleteTag = await task.prompt(import_prompt_adapter_enquirer5.ListrEnquirerPromptAdapter).run({
|
|
6493
6731
|
type: "toggle",
|
|
6494
6732
|
message: `${warningBadge} The Git tag '${gitTag}' already exists. Do you want to delete tag?`,
|
|
6495
6733
|
enabled: "Yes",
|
|
@@ -6511,13 +6749,13 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6511
6749
|
};
|
|
6512
6750
|
|
|
6513
6751
|
// src/tasks/required-conditions-check.ts
|
|
6514
|
-
var
|
|
6752
|
+
var import_prompt_adapter_enquirer6 = require("@listr2/prompt-adapter-enquirer");
|
|
6515
6753
|
|
|
6516
6754
|
// src/registry/custom-registry.ts
|
|
6517
|
-
var
|
|
6755
|
+
var import_tinyexec6 = require("tinyexec");
|
|
6518
6756
|
var CustomRegistry = class extends NpmRegistry {
|
|
6519
6757
|
async npm(args) {
|
|
6520
|
-
const { stdout } = await (0,
|
|
6758
|
+
const { stdout } = await (0, import_tinyexec6.exec)(
|
|
6521
6759
|
"npm",
|
|
6522
6760
|
args.concat("--registry", this.registry),
|
|
6523
6761
|
{ throwOnError: true }
|
|
@@ -6620,7 +6858,7 @@ var requiredConditionsCheckTask = (options) => createListr({
|
|
|
6620
6858
|
task: async (_3, task) => {
|
|
6621
6859
|
const jsr = await jsrRegistry();
|
|
6622
6860
|
if (!await jsr.isInstalled()) {
|
|
6623
|
-
const install = await task.prompt(
|
|
6861
|
+
const install = await task.prompt(import_prompt_adapter_enquirer6.ListrEnquirerPromptAdapter).run({
|
|
6624
6862
|
type: "toggle",
|
|
6625
6863
|
message: `${warningBadge} jsr is not installed. Do you want to install jsr?`,
|
|
6626
6864
|
enabled: "Yes",
|
|
@@ -6754,14 +6992,66 @@ async function collectPublishTasks(ctx) {
|
|
|
6754
6992
|
}
|
|
6755
6993
|
return collectRegistries(ctx).map(registryTask);
|
|
6756
6994
|
}
|
|
6995
|
+
function dryRunRegistryTask(registry) {
|
|
6996
|
+
switch (registry) {
|
|
6997
|
+
case "npm":
|
|
6998
|
+
return npmDryRunPublishTask;
|
|
6999
|
+
case "jsr":
|
|
7000
|
+
return jsrDryRunPublishTask;
|
|
7001
|
+
case "crates":
|
|
7002
|
+
return cratesDryRunPublishTask;
|
|
7003
|
+
default:
|
|
7004
|
+
return npmDryRunPublishTask;
|
|
7005
|
+
}
|
|
7006
|
+
}
|
|
7007
|
+
async function collectDryRunPublishTasks(ctx) {
|
|
7008
|
+
if (ctx.packages?.length) {
|
|
7009
|
+
const nonCratesTasks = ctx.packages.flatMap(
|
|
7010
|
+
(pkg) => pkg.registries.filter((reg) => reg !== "crates").map((reg) => dryRunRegistryTask(reg))
|
|
7011
|
+
);
|
|
7012
|
+
const cratesPaths = ctx.packages.filter((pkg) => pkg.registries.includes("crates")).map((pkg) => pkg.path);
|
|
7013
|
+
if (cratesPaths.length === 0) {
|
|
7014
|
+
return nonCratesTasks;
|
|
7015
|
+
}
|
|
7016
|
+
const sortedPaths = await sortCratesByDependencyOrder(cratesPaths);
|
|
7017
|
+
const sequentialCratesTask = {
|
|
7018
|
+
title: "Dry-run crates.io publish (sequential)",
|
|
7019
|
+
task: (_ctx, task) => task.newListr(
|
|
7020
|
+
sortedPaths.map((p) => createCratesDryRunPublishTask(p)),
|
|
7021
|
+
{ concurrent: false }
|
|
7022
|
+
)
|
|
7023
|
+
};
|
|
7024
|
+
return [...nonCratesTasks, sequentialCratesTask];
|
|
7025
|
+
}
|
|
7026
|
+
return collectRegistries(ctx).map(dryRunRegistryTask);
|
|
7027
|
+
}
|
|
6757
7028
|
async function run(options) {
|
|
6758
7029
|
const ctx = {
|
|
6759
7030
|
...options,
|
|
6760
7031
|
promptEnabled: !import_std_env.isCI && import_node_process8.default.stdin.isTTY
|
|
6761
7032
|
};
|
|
7033
|
+
let cleanupEnv;
|
|
6762
7034
|
try {
|
|
6763
7035
|
if (options.contents) import_node_process8.default.chdir(options.contents);
|
|
6764
|
-
if (
|
|
7036
|
+
if (options.preflight) {
|
|
7037
|
+
await createListr({
|
|
7038
|
+
title: "Collecting registry tokens",
|
|
7039
|
+
task: async (ctx2, task) => {
|
|
7040
|
+
const registries2 = collectRegistries(ctx2);
|
|
7041
|
+
const tokens = await collectTokens(registries2, task);
|
|
7042
|
+
await promptGhSecretsSync(tokens, task);
|
|
7043
|
+
cleanupEnv = injectTokensToEnv(tokens);
|
|
7044
|
+
ctx2.promptEnabled = false;
|
|
7045
|
+
}
|
|
7046
|
+
}).run(ctx);
|
|
7047
|
+
await prerequisitesCheckTask({
|
|
7048
|
+
skip: options.skipPrerequisitesCheck
|
|
7049
|
+
}).run(ctx);
|
|
7050
|
+
await requiredConditionsCheckTask({
|
|
7051
|
+
skip: options.skipConditionsCheck
|
|
7052
|
+
}).run(ctx);
|
|
7053
|
+
}
|
|
7054
|
+
if (!options.publishOnly && !options.preflight) {
|
|
6765
7055
|
await prerequisitesCheckTask({
|
|
6766
7056
|
skip: options.skipPrerequisitesCheck
|
|
6767
7057
|
}).run(ctx);
|
|
@@ -6775,14 +7065,55 @@ async function run(options) {
|
|
|
6775
7065
|
task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
|
|
6776
7066
|
concurrent: true
|
|
6777
7067
|
})
|
|
6778
|
-
} : [
|
|
7068
|
+
} : options.preflight ? [
|
|
7069
|
+
{
|
|
7070
|
+
skip: options.skipTests,
|
|
7071
|
+
title: "Running tests",
|
|
7072
|
+
task: async (ctx2) => {
|
|
7073
|
+
const packageManager = await getPackageManager();
|
|
7074
|
+
try {
|
|
7075
|
+
await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.testScript], {
|
|
7076
|
+
throwOnError: true
|
|
7077
|
+
});
|
|
7078
|
+
} catch (error) {
|
|
7079
|
+
throw new AbstractError(
|
|
7080
|
+
`Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
|
|
7081
|
+
{ cause: error }
|
|
7082
|
+
);
|
|
7083
|
+
}
|
|
7084
|
+
}
|
|
7085
|
+
},
|
|
7086
|
+
{
|
|
7087
|
+
skip: options.skipBuild,
|
|
7088
|
+
title: "Building the project",
|
|
7089
|
+
task: async (ctx2) => {
|
|
7090
|
+
const packageManager = await getPackageManager();
|
|
7091
|
+
try {
|
|
7092
|
+
await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.buildScript], {
|
|
7093
|
+
throwOnError: true
|
|
7094
|
+
});
|
|
7095
|
+
} catch (error) {
|
|
7096
|
+
throw new AbstractError(
|
|
7097
|
+
`Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
|
|
7098
|
+
{ cause: error }
|
|
7099
|
+
);
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
},
|
|
7103
|
+
{
|
|
7104
|
+
title: "Validating publish (dry-run)",
|
|
7105
|
+
task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
|
|
7106
|
+
concurrent: true
|
|
7107
|
+
})
|
|
7108
|
+
}
|
|
7109
|
+
] : [
|
|
6779
7110
|
{
|
|
6780
7111
|
skip: options.skipTests,
|
|
6781
7112
|
title: "Running tests",
|
|
6782
7113
|
task: async (ctx2) => {
|
|
6783
7114
|
const packageManager = await getPackageManager();
|
|
6784
7115
|
try {
|
|
6785
|
-
await (0,
|
|
7116
|
+
await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.testScript], {
|
|
6786
7117
|
throwOnError: true
|
|
6787
7118
|
});
|
|
6788
7119
|
} catch (error) {
|
|
@@ -6799,7 +7130,7 @@ async function run(options) {
|
|
|
6799
7130
|
task: async (ctx2) => {
|
|
6800
7131
|
const packageManager = await getPackageManager();
|
|
6801
7132
|
try {
|
|
6802
|
-
await (0,
|
|
7133
|
+
await (0, import_tinyexec7.exec)(packageManager, ["run", ctx2.buildScript], {
|
|
6803
7134
|
throwOnError: true
|
|
6804
7135
|
});
|
|
6805
7136
|
} catch (error) {
|
|
@@ -6924,13 +7255,24 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
|
|
|
6924
7255
|
parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
|
|
6925
7256
|
}
|
|
6926
7257
|
}
|
|
6927
|
-
|
|
6928
|
-
|
|
7258
|
+
if (options.preflight) {
|
|
7259
|
+
cleanupEnv?.();
|
|
7260
|
+
console.log(
|
|
7261
|
+
`
|
|
7262
|
+
|
|
7263
|
+
\u2705 Preflight check passed. CI publish should succeed for ${parts.join(", ")}.
|
|
7264
|
+
`
|
|
7265
|
+
);
|
|
7266
|
+
} else {
|
|
7267
|
+
console.log(
|
|
7268
|
+
`
|
|
6929
7269
|
|
|
6930
7270
|
\u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
|
|
6931
7271
|
`
|
|
6932
|
-
|
|
7272
|
+
);
|
|
7273
|
+
}
|
|
6933
7274
|
} catch (e2) {
|
|
7275
|
+
cleanupEnv?.();
|
|
6934
7276
|
consoleError(e2);
|
|
6935
7277
|
await rollback();
|
|
6936
7278
|
import_node_process8.default.exit(1);
|