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.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
|
|
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/
|
|
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
|
|
@@ -5184,6 +5306,8 @@ var Registry = class {
|
|
|
5184
5306
|
this.packageName = packageName;
|
|
5185
5307
|
this.registry = registry;
|
|
5186
5308
|
}
|
|
5309
|
+
async dryRunPublish(_manifestDir) {
|
|
5310
|
+
}
|
|
5187
5311
|
};
|
|
5188
5312
|
|
|
5189
5313
|
// src/registry/crates.ts
|
|
@@ -5253,7 +5377,7 @@ var CratesRegistry = class extends Registry {
|
|
|
5253
5377
|
try {
|
|
5254
5378
|
const args = ["publish"];
|
|
5255
5379
|
if (manifestDir) {
|
|
5256
|
-
args.push("--manifest-path",
|
|
5380
|
+
args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
|
|
5257
5381
|
}
|
|
5258
5382
|
await exec3("cargo", args, { throwOnError: true });
|
|
5259
5383
|
return true;
|
|
@@ -5264,6 +5388,20 @@ ${stderr}` : "Failed to run `cargo publish`";
|
|
|
5264
5388
|
throw new CratesError(message, { cause: error });
|
|
5265
5389
|
}
|
|
5266
5390
|
}
|
|
5391
|
+
async dryRunPublish(manifestDir) {
|
|
5392
|
+
try {
|
|
5393
|
+
const args = ["publish", "--dry-run"];
|
|
5394
|
+
if (manifestDir) {
|
|
5395
|
+
args.push("--manifest-path", path5.join(manifestDir, "Cargo.toml"));
|
|
5396
|
+
}
|
|
5397
|
+
await exec3("cargo", args, { throwOnError: true });
|
|
5398
|
+
} catch (error) {
|
|
5399
|
+
const stderr = error instanceof NonZeroExitError ? error.output?.stderr : void 0;
|
|
5400
|
+
const message = stderr ? `Failed to run \`cargo publish --dry-run\`:
|
|
5401
|
+
${stderr}` : "Failed to run `cargo publish --dry-run`";
|
|
5402
|
+
throw new CratesError(message, { cause: error });
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5267
5405
|
async isPublished() {
|
|
5268
5406
|
try {
|
|
5269
5407
|
const response = await fetch(
|
|
@@ -5350,85 +5488,12 @@ function createCratesPublishTask(packagePath) {
|
|
|
5350
5488
|
var cratesAvailableCheckTasks = createCratesAvailableCheckTask();
|
|
5351
5489
|
var cratesPublishTasks = createCratesPublishTask();
|
|
5352
5490
|
|
|
5353
|
-
// src/tasks/
|
|
5354
|
-
import process8 from "node:process";
|
|
5491
|
+
// src/tasks/dry-run-publish.ts
|
|
5355
5492
|
import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
|
|
5356
|
-
import npmCli from "@npmcli/promise-spawn";
|
|
5357
5493
|
|
|
5358
5494
|
// src/registry/jsr.ts
|
|
5359
5495
|
import { exec as exec4, NonZeroExitError as NonZeroExitError2 } from "tinyexec";
|
|
5360
5496
|
|
|
5361
|
-
// src/utils/db.ts
|
|
5362
|
-
import { createCipheriv, createDecipheriv, createHash } from "node:crypto";
|
|
5363
|
-
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
5364
|
-
import path5 from "node:path";
|
|
5365
|
-
var a = "aes-256-cbc";
|
|
5366
|
-
var n = statSync(import.meta.dirname);
|
|
5367
|
-
var k = `${n.rdev}${n.birthtimeMs}${n.nlink}${n.gid}`;
|
|
5368
|
-
var l = createHash("md5").update(k).digest();
|
|
5369
|
-
function e(e2, f) {
|
|
5370
|
-
const c = createCipheriv(a, createHash("sha-256").update(f).digest(), l);
|
|
5371
|
-
return c.update(e2, "utf8", "hex") + c.final("hex");
|
|
5372
|
-
}
|
|
5373
|
-
function d(g, h) {
|
|
5374
|
-
const d2 = createDecipheriv(a, createHash("sha-256").update(h).digest(), l);
|
|
5375
|
-
return d2.update(g, "hex", "utf8") + d2.final("utf8");
|
|
5376
|
-
}
|
|
5377
|
-
var Db = class {
|
|
5378
|
-
constructor() {
|
|
5379
|
-
__publicField(this, "path", path5.resolve(import.meta.dirname, ".pubm"));
|
|
5380
|
-
try {
|
|
5381
|
-
if (!statSync(this.path).isDirectory()) {
|
|
5382
|
-
mkdirSync(this.path);
|
|
5383
|
-
}
|
|
5384
|
-
} catch {
|
|
5385
|
-
try {
|
|
5386
|
-
mkdirSync(this.path);
|
|
5387
|
-
} catch (error) {
|
|
5388
|
-
throw new Error(
|
|
5389
|
-
`Failed to create token storage directory at '${this.path}': ${error instanceof Error ? error.message : error}`
|
|
5390
|
-
);
|
|
5391
|
-
}
|
|
5392
|
-
}
|
|
5393
|
-
}
|
|
5394
|
-
set(field, value) {
|
|
5395
|
-
try {
|
|
5396
|
-
writeFileSync(
|
|
5397
|
-
path5.resolve(
|
|
5398
|
-
this.path,
|
|
5399
|
-
Buffer.from(e(field, field)).toString("base64")
|
|
5400
|
-
),
|
|
5401
|
-
Buffer.from(e(`${value}`, field)),
|
|
5402
|
-
{ encoding: "binary" }
|
|
5403
|
-
);
|
|
5404
|
-
} catch (error) {
|
|
5405
|
-
throw new Error(
|
|
5406
|
-
`Failed to save token for '${field}': ${error instanceof Error ? error.message : error}`
|
|
5407
|
-
);
|
|
5408
|
-
}
|
|
5409
|
-
}
|
|
5410
|
-
get(field) {
|
|
5411
|
-
const filePath = path5.resolve(
|
|
5412
|
-
this.path,
|
|
5413
|
-
Buffer.from(e(field, field)).toString("base64")
|
|
5414
|
-
);
|
|
5415
|
-
let raw;
|
|
5416
|
-
try {
|
|
5417
|
-
raw = readFileSync(filePath);
|
|
5418
|
-
} catch {
|
|
5419
|
-
return null;
|
|
5420
|
-
}
|
|
5421
|
-
try {
|
|
5422
|
-
return d(Buffer.from(raw).toString(), field);
|
|
5423
|
-
} catch {
|
|
5424
|
-
console.warn(
|
|
5425
|
-
`Stored token for '${field}' appears corrupted. It will be re-requested.`
|
|
5426
|
-
);
|
|
5427
|
-
return null;
|
|
5428
|
-
}
|
|
5429
|
-
}
|
|
5430
|
-
};
|
|
5431
|
-
|
|
5432
5497
|
// src/utils/package-name.ts
|
|
5433
5498
|
import { builtinModules } from "node:module";
|
|
5434
5499
|
function isScopedPackage(packageName) {
|
|
@@ -5554,6 +5619,25 @@ ${stderr}` : ""}`,
|
|
|
5554
5619
|
);
|
|
5555
5620
|
}
|
|
5556
5621
|
}
|
|
5622
|
+
async dryRunPublish() {
|
|
5623
|
+
try {
|
|
5624
|
+
await exec4(
|
|
5625
|
+
"jsr",
|
|
5626
|
+
[
|
|
5627
|
+
"publish",
|
|
5628
|
+
"--dry-run",
|
|
5629
|
+
"--allow-dirty",
|
|
5630
|
+
"--token",
|
|
5631
|
+
`${JsrClient.token}`
|
|
5632
|
+
],
|
|
5633
|
+
{ throwOnError: true }
|
|
5634
|
+
);
|
|
5635
|
+
} catch (error) {
|
|
5636
|
+
throw new JsrError("Failed to run `jsr publish --dry-run`", {
|
|
5637
|
+
cause: error
|
|
5638
|
+
});
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5557
5641
|
async version() {
|
|
5558
5642
|
return await this.jsr(["--version"]);
|
|
5559
5643
|
}
|
|
@@ -5948,6 +6032,24 @@ var NpmRegistry = class extends Registry {
|
|
|
5948
6032
|
throw this.classifyPublishError(error);
|
|
5949
6033
|
}
|
|
5950
6034
|
}
|
|
6035
|
+
async dryRunPublish() {
|
|
6036
|
+
try {
|
|
6037
|
+
await this.npm(["publish", "--dry-run"]);
|
|
6038
|
+
} catch (error) {
|
|
6039
|
+
throw new NpmError("Failed to run `npm publish --dry-run`", {
|
|
6040
|
+
cause: error
|
|
6041
|
+
});
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
async twoFactorAuthMode() {
|
|
6045
|
+
try {
|
|
6046
|
+
const output = await this.npm(["profile", "get", "--json"]);
|
|
6047
|
+
const profile = JSON.parse(output);
|
|
6048
|
+
return profile?.tfa?.mode ?? null;
|
|
6049
|
+
} catch {
|
|
6050
|
+
return null;
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
5951
6053
|
async isPackageNameAvaliable() {
|
|
5952
6054
|
return isValidPackageName(this.packageName);
|
|
5953
6055
|
}
|
|
@@ -5984,7 +6086,80 @@ async function npmRegistry() {
|
|
|
5984
6086
|
return new NpmRegistry(packageJson.name);
|
|
5985
6087
|
}
|
|
5986
6088
|
|
|
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
|
+
}
|
|
6119
|
+
var npmDryRunPublishTask = {
|
|
6120
|
+
title: "Dry-run npm publish",
|
|
6121
|
+
task: async (_, task) => {
|
|
6122
|
+
task.output = "Running npm publish --dry-run...";
|
|
6123
|
+
await withTokenRetry("npm", task, async () => {
|
|
6124
|
+
const npm = await npmRegistry();
|
|
6125
|
+
await npm.dryRunPublish();
|
|
6126
|
+
});
|
|
6127
|
+
}
|
|
6128
|
+
};
|
|
6129
|
+
var jsrDryRunPublishTask = {
|
|
6130
|
+
title: "Dry-run jsr publish",
|
|
6131
|
+
task: async (_, task) => {
|
|
6132
|
+
task.output = "Running jsr publish --dry-run...";
|
|
6133
|
+
await withTokenRetry("jsr", task, async () => {
|
|
6134
|
+
const jsr = await jsrRegistry();
|
|
6135
|
+
await jsr.dryRunPublish();
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
};
|
|
6139
|
+
async function getCrateName2(packagePath) {
|
|
6140
|
+
const eco = new RustEcosystem(packagePath ?? process.cwd());
|
|
6141
|
+
return await eco.packageName();
|
|
6142
|
+
}
|
|
6143
|
+
function createCratesDryRunPublishTask(packagePath) {
|
|
6144
|
+
const label = packagePath ? ` (${packagePath})` : "";
|
|
6145
|
+
return {
|
|
6146
|
+
title: `Dry-run crates.io publish${label}`,
|
|
6147
|
+
task: async (_, task) => {
|
|
6148
|
+
task.output = "Running cargo publish --dry-run...";
|
|
6149
|
+
await withTokenRetry("crates", task, async () => {
|
|
6150
|
+
const packageName = await getCrateName2(packagePath);
|
|
6151
|
+
const registry = new CratesRegistry(packageName);
|
|
6152
|
+
await registry.dryRunPublish(packagePath);
|
|
6153
|
+
});
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
var cratesDryRunPublishTask = createCratesDryRunPublishTask();
|
|
6158
|
+
|
|
5987
6159
|
// src/tasks/jsr.ts
|
|
6160
|
+
import process8 from "node:process";
|
|
6161
|
+
import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter2 } from "@listr2/prompt-adapter-enquirer";
|
|
6162
|
+
import npmCli from "@npmcli/promise-spawn";
|
|
5988
6163
|
var { open } = npmCli;
|
|
5989
6164
|
var JsrAvailableError = class extends AbstractError {
|
|
5990
6165
|
constructor(message, { cause } = {}) {
|
|
@@ -6012,7 +6187,7 @@ var jsrAvailableCheckTasks = {
|
|
|
6012
6187
|
if (ctx.promptEnabled) {
|
|
6013
6188
|
const maxAttempts = 3;
|
|
6014
6189
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6015
|
-
JsrClient.token = await task.prompt(
|
|
6190
|
+
JsrClient.token = await task.prompt(ListrEnquirerPromptAdapter2).run({
|
|
6016
6191
|
type: "password",
|
|
6017
6192
|
message: `Please enter the jsr ${color.bold("API token")}${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`,
|
|
6018
6193
|
footer: `
|
|
@@ -6062,7 +6237,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6062
6237
|
)
|
|
6063
6238
|
)).filter((v) => v !== null);
|
|
6064
6239
|
if (searchResults.length > 0) {
|
|
6065
|
-
jsrName = await task.prompt(
|
|
6240
|
+
jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
|
|
6066
6241
|
type: "select",
|
|
6067
6242
|
message: "Is there a scoped package you want to publish in the already published list?",
|
|
6068
6243
|
choices: [
|
|
@@ -6080,7 +6255,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6080
6255
|
}
|
|
6081
6256
|
const userName = await new Git().userName();
|
|
6082
6257
|
task.output = "Select the scope of the package to publish";
|
|
6083
|
-
jsrName = await task.prompt(
|
|
6258
|
+
jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
|
|
6084
6259
|
type: "select",
|
|
6085
6260
|
message: "jsr.json does not exist, and the package name is not scoped. Please select a scope for the 'jsr' package",
|
|
6086
6261
|
choices: [
|
|
@@ -6108,7 +6283,7 @@ Generate a token from ${color.bold(link2("jsr.io", "https://jsr.io/account/token
|
|
|
6108
6283
|
});
|
|
6109
6284
|
if (jsrName === "specify") {
|
|
6110
6285
|
while (!isScopedPackage(jsrName)) {
|
|
6111
|
-
jsrName = await task.prompt(
|
|
6286
|
+
jsrName = await task.prompt(ListrEnquirerPromptAdapter2).run({
|
|
6112
6287
|
type: "input",
|
|
6113
6288
|
message: "Package name"
|
|
6114
6289
|
});
|
|
@@ -6176,7 +6351,7 @@ var jsrPublishTasks = {
|
|
|
6176
6351
|
${urls.map((url) => ` ${color.cyan(url)}`).join("\n")}`;
|
|
6177
6352
|
open(urls[0]);
|
|
6178
6353
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6179
|
-
await task.prompt(
|
|
6354
|
+
await task.prompt(ListrEnquirerPromptAdapter2).run({
|
|
6180
6355
|
type: "input",
|
|
6181
6356
|
message: `Press ${color.bold("enter")} after creating the package on jsr.io${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
|
|
6182
6357
|
});
|
|
@@ -6205,7 +6380,7 @@ ${jsr.packageCreationUrls.join("\n")}`
|
|
|
6205
6380
|
// src/tasks/npm.ts
|
|
6206
6381
|
import { spawn } from "node:child_process";
|
|
6207
6382
|
import process9 from "node:process";
|
|
6208
|
-
import { ListrEnquirerPromptAdapter as
|
|
6383
|
+
import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter3 } from "@listr2/prompt-adapter-enquirer";
|
|
6209
6384
|
import npmCli2 from "@npmcli/promise-spawn";
|
|
6210
6385
|
var { open: open2 } = npmCli2;
|
|
6211
6386
|
var NpmAvailableError = class extends AbstractError {
|
|
@@ -6217,7 +6392,6 @@ var NpmAvailableError = class extends AbstractError {
|
|
|
6217
6392
|
};
|
|
6218
6393
|
var npmAvailableCheckTasks = {
|
|
6219
6394
|
title: "Checking npm avaliable for publising",
|
|
6220
|
-
skip: (ctx) => !!ctx.preview,
|
|
6221
6395
|
task: async (ctx, task) => {
|
|
6222
6396
|
const npm = await npmRegistry();
|
|
6223
6397
|
if (!await npm.isLoggedIn()) {
|
|
@@ -6280,6 +6454,14 @@ var npmAvailableCheckTasks = {
|
|
|
6280
6454
|
More information: ${link2("npm naming rules", "https://github.com/npm/validate-npm-package-name?tab=readme-ov-file#naming-rules")}`
|
|
6281
6455
|
);
|
|
6282
6456
|
}
|
|
6457
|
+
if (!ctx.promptEnabled) {
|
|
6458
|
+
const tfaMode = await npm.twoFactorAuthMode();
|
|
6459
|
+
if (tfaMode === "auth-and-writes") {
|
|
6460
|
+
throw new NpmAvailableError(
|
|
6461
|
+
`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`
|
|
6462
|
+
);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6283
6465
|
}
|
|
6284
6466
|
};
|
|
6285
6467
|
var npmPublishTasks = {
|
|
@@ -6295,7 +6477,7 @@ var npmPublishTasks = {
|
|
|
6295
6477
|
const maxAttempts = 3;
|
|
6296
6478
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
6297
6479
|
result = await npm.publish(
|
|
6298
|
-
await task.prompt(
|
|
6480
|
+
await task.prompt(ListrEnquirerPromptAdapter3).run({
|
|
6299
6481
|
type: "password",
|
|
6300
6482
|
message: `npm OTP code${attempt > 1 ? ` (attempt ${attempt}/${maxAttempts})` : ""}`
|
|
6301
6483
|
})
|
|
@@ -6329,8 +6511,64 @@ var npmPublishTasks = {
|
|
|
6329
6511
|
}
|
|
6330
6512
|
};
|
|
6331
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
|
+
|
|
6332
6570
|
// src/tasks/prerequisites-check.ts
|
|
6333
|
-
import { ListrEnquirerPromptAdapter as
|
|
6571
|
+
import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter5 } from "@listr2/prompt-adapter-enquirer";
|
|
6334
6572
|
var PrerequisitesCheckError = class extends AbstractError {
|
|
6335
6573
|
constructor(message, { cause } = {}) {
|
|
6336
6574
|
super(message, { cause });
|
|
@@ -6350,7 +6588,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6350
6588
|
title: "Verifying current branch is a release branch",
|
|
6351
6589
|
task: async (ctx, task) => {
|
|
6352
6590
|
if (await git.branch() !== ctx.branch) {
|
|
6353
|
-
const swtichBranch = await task.prompt(
|
|
6591
|
+
const swtichBranch = await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6354
6592
|
type: "toggle",
|
|
6355
6593
|
message: `${warningBadge} The current HEAD branch is not the release target branch. Do you want to switch branch to ${ctx.branch}?`,
|
|
6356
6594
|
enabled: "Yes",
|
|
@@ -6372,7 +6610,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6372
6610
|
task: async (_2, task) => {
|
|
6373
6611
|
task.output = "Checking for updates with `git fetch`";
|
|
6374
6612
|
if ((await git.dryFetch()).trim()) {
|
|
6375
|
-
const fetch2 = await task.prompt(
|
|
6613
|
+
const fetch2 = await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6376
6614
|
type: "toggle",
|
|
6377
6615
|
message: `${warningBadge} Local history is outdated. Do you want to run \`git fetch\`?`,
|
|
6378
6616
|
enabled: "Yes",
|
|
@@ -6389,7 +6627,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6389
6627
|
}
|
|
6390
6628
|
task.output = "Checking for updates with `git pull`";
|
|
6391
6629
|
if (await git.revisionDiffsCount()) {
|
|
6392
|
-
const pull = await task.prompt(
|
|
6630
|
+
const pull = await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6393
6631
|
type: "toggle",
|
|
6394
6632
|
message: `${warningBadge} Local history is outdated. Do you want to run \`git pull\`?`,
|
|
6395
6633
|
enabled: "Yes",
|
|
@@ -6411,7 +6649,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6411
6649
|
task: async (ctx, task) => {
|
|
6412
6650
|
if (await git.status()) {
|
|
6413
6651
|
task.output = "Local working tree is not clean.";
|
|
6414
|
-
if (!await task.prompt(
|
|
6652
|
+
if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6415
6653
|
type: "toggle",
|
|
6416
6654
|
message: `${warningBadge} Local working tree is not clean. Do you want to skip?`,
|
|
6417
6655
|
enabled: "Yes",
|
|
@@ -6436,7 +6674,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6436
6674
|
return void 0;
|
|
6437
6675
|
}
|
|
6438
6676
|
if ((await git.commits(latestTag, "HEAD")).length <= 0) {
|
|
6439
|
-
if (!await task.prompt(
|
|
6677
|
+
if (!await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6440
6678
|
type: "toggle",
|
|
6441
6679
|
message: `${warningBadge} No commits exist from the latest tag. Do you want to skip?`,
|
|
6442
6680
|
enabled: "Yes",
|
|
@@ -6454,7 +6692,7 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6454
6692
|
task: async (ctx, task) => {
|
|
6455
6693
|
const gitTag = `v${ctx.version}`;
|
|
6456
6694
|
if (await git.checkTagExist(gitTag)) {
|
|
6457
|
-
const deleteTag = await task.prompt(
|
|
6695
|
+
const deleteTag = await task.prompt(ListrEnquirerPromptAdapter5).run({
|
|
6458
6696
|
type: "toggle",
|
|
6459
6697
|
message: `${warningBadge} The Git tag '${gitTag}' already exists. Do you want to delete tag?`,
|
|
6460
6698
|
enabled: "Yes",
|
|
@@ -6476,13 +6714,13 @@ var prerequisitesCheckTask = (options) => {
|
|
|
6476
6714
|
};
|
|
6477
6715
|
|
|
6478
6716
|
// src/tasks/required-conditions-check.ts
|
|
6479
|
-
import { ListrEnquirerPromptAdapter as
|
|
6717
|
+
import { ListrEnquirerPromptAdapter as ListrEnquirerPromptAdapter6 } from "@listr2/prompt-adapter-enquirer";
|
|
6480
6718
|
|
|
6481
6719
|
// src/registry/custom-registry.ts
|
|
6482
|
-
import { exec as
|
|
6720
|
+
import { exec as exec7 } from "tinyexec";
|
|
6483
6721
|
var CustomRegistry = class extends NpmRegistry {
|
|
6484
6722
|
async npm(args) {
|
|
6485
|
-
const { stdout } = await
|
|
6723
|
+
const { stdout } = await exec7(
|
|
6486
6724
|
"npm",
|
|
6487
6725
|
args.concat("--registry", this.registry),
|
|
6488
6726
|
{ throwOnError: true }
|
|
@@ -6584,7 +6822,7 @@ var requiredConditionsCheckTask = (options) => createListr({
|
|
|
6584
6822
|
task: async (_3, task) => {
|
|
6585
6823
|
const jsr = await jsrRegistry();
|
|
6586
6824
|
if (!await jsr.isInstalled()) {
|
|
6587
|
-
const install = await task.prompt(
|
|
6825
|
+
const install = await task.prompt(ListrEnquirerPromptAdapter6).run({
|
|
6588
6826
|
type: "toggle",
|
|
6589
6827
|
message: `${warningBadge} jsr is not installed. Do you want to install jsr?`,
|
|
6590
6828
|
enabled: "Yes",
|
|
@@ -6718,14 +6956,66 @@ async function collectPublishTasks(ctx) {
|
|
|
6718
6956
|
}
|
|
6719
6957
|
return collectRegistries(ctx).map(registryTask);
|
|
6720
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
|
+
}
|
|
6721
6992
|
async function run(options) {
|
|
6722
6993
|
const ctx = {
|
|
6723
6994
|
...options,
|
|
6724
6995
|
promptEnabled: !isCI2 && process10.stdin.isTTY
|
|
6725
6996
|
};
|
|
6997
|
+
let cleanupEnv;
|
|
6726
6998
|
try {
|
|
6727
6999
|
if (options.contents) process10.chdir(options.contents);
|
|
6728
|
-
if (
|
|
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) {
|
|
6729
7019
|
await prerequisitesCheckTask({
|
|
6730
7020
|
skip: options.skipPrerequisitesCheck
|
|
6731
7021
|
}).run(ctx);
|
|
@@ -6739,14 +7029,55 @@ async function run(options) {
|
|
|
6739
7029
|
task: async (ctx2, parentTask) => parentTask.newListr(await collectPublishTasks(ctx2), {
|
|
6740
7030
|
concurrent: true
|
|
6741
7031
|
})
|
|
6742
|
-
} : [
|
|
7032
|
+
} : options.preflight ? [
|
|
7033
|
+
{
|
|
7034
|
+
skip: options.skipTests,
|
|
7035
|
+
title: "Running tests",
|
|
7036
|
+
task: async (ctx2) => {
|
|
7037
|
+
const packageManager = await getPackageManager();
|
|
7038
|
+
try {
|
|
7039
|
+
await exec8(packageManager, ["run", ctx2.testScript], {
|
|
7040
|
+
throwOnError: true
|
|
7041
|
+
});
|
|
7042
|
+
} catch (error) {
|
|
7043
|
+
throw new AbstractError(
|
|
7044
|
+
`Test script '${ctx2.testScript}' failed. Run \`${packageManager} run ${ctx2.testScript}\` locally to see full output.`,
|
|
7045
|
+
{ cause: error }
|
|
7046
|
+
);
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
},
|
|
7050
|
+
{
|
|
7051
|
+
skip: options.skipBuild,
|
|
7052
|
+
title: "Building the project",
|
|
7053
|
+
task: async (ctx2) => {
|
|
7054
|
+
const packageManager = await getPackageManager();
|
|
7055
|
+
try {
|
|
7056
|
+
await exec8(packageManager, ["run", ctx2.buildScript], {
|
|
7057
|
+
throwOnError: true
|
|
7058
|
+
});
|
|
7059
|
+
} catch (error) {
|
|
7060
|
+
throw new AbstractError(
|
|
7061
|
+
`Build script '${ctx2.buildScript}' failed. Run \`${packageManager} run ${ctx2.buildScript}\` locally to see full output.`,
|
|
7062
|
+
{ cause: error }
|
|
7063
|
+
);
|
|
7064
|
+
}
|
|
7065
|
+
}
|
|
7066
|
+
},
|
|
7067
|
+
{
|
|
7068
|
+
title: "Validating publish (dry-run)",
|
|
7069
|
+
task: async (ctx2, parentTask) => parentTask.newListr(await collectDryRunPublishTasks(ctx2), {
|
|
7070
|
+
concurrent: true
|
|
7071
|
+
})
|
|
7072
|
+
}
|
|
7073
|
+
] : [
|
|
6743
7074
|
{
|
|
6744
7075
|
skip: options.skipTests,
|
|
6745
7076
|
title: "Running tests",
|
|
6746
7077
|
task: async (ctx2) => {
|
|
6747
7078
|
const packageManager = await getPackageManager();
|
|
6748
7079
|
try {
|
|
6749
|
-
await
|
|
7080
|
+
await exec8(packageManager, ["run", ctx2.testScript], {
|
|
6750
7081
|
throwOnError: true
|
|
6751
7082
|
});
|
|
6752
7083
|
} catch (error) {
|
|
@@ -6763,7 +7094,7 @@ async function run(options) {
|
|
|
6763
7094
|
task: async (ctx2) => {
|
|
6764
7095
|
const packageManager = await getPackageManager();
|
|
6765
7096
|
try {
|
|
6766
|
-
await
|
|
7097
|
+
await exec8(packageManager, ["run", ctx2.buildScript], {
|
|
6767
7098
|
throwOnError: true
|
|
6768
7099
|
});
|
|
6769
7100
|
} catch (error) {
|
|
@@ -6888,13 +7219,24 @@ ${repositoryUrl}/compare/${lastRev}...${latestTag}`;
|
|
|
6888
7219
|
parts.push(`${color.bold(name)} on ${color.red("crates.io")}`);
|
|
6889
7220
|
}
|
|
6890
7221
|
}
|
|
6891
|
-
|
|
6892
|
-
|
|
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
|
+
`
|
|
6893
7233
|
|
|
6894
7234
|
\u{1F680} Successfully published ${parts.join(", ")} ${color.blueBright(`v${ctx.version}`)} \u{1F680}
|
|
6895
7235
|
`
|
|
6896
|
-
|
|
7236
|
+
);
|
|
7237
|
+
}
|
|
6897
7238
|
} catch (e2) {
|
|
7239
|
+
cleanupEnv?.();
|
|
6898
7240
|
consoleError(e2);
|
|
6899
7241
|
await rollback();
|
|
6900
7242
|
process10.exit(1);
|