@theholocron/cli 4.19.0 → 5.0.0-alpha.4
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/cli.mjs +276 -21
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +24 -11
- package/package.json +8 -9
package/dist/cli.mjs
CHANGED
|
@@ -2154,12 +2154,11 @@ function render$10(inputs) {
|
|
|
2154
2154
|
main: "./src/index.ts",
|
|
2155
2155
|
exports: { ".": "./src/index.ts" },
|
|
2156
2156
|
scripts: {
|
|
2157
|
-
build: "tsdown",
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2157
|
+
"delivery.build": "tsdown",
|
|
2158
|
+
"sourceQuality.staticAnalysis": "holocron run sourceQuality.staticAnalysis --",
|
|
2159
|
+
"verification.typeSafety": "holocron run verification.typeSafety --",
|
|
2160
|
+
"verification.unitTests": "holocron run verification.unitTests --",
|
|
2161
2161
|
"test:watch": "vitest",
|
|
2162
|
-
"test:coverage": "vitest run --coverage",
|
|
2163
2162
|
validate: "tsx scripts/validate.mjs"
|
|
2164
2163
|
},
|
|
2165
2164
|
peerDependencies: { "@theholocron/cli": "workspace:*" },
|
|
@@ -2879,7 +2878,7 @@ function runPluginCreate(input) {
|
|
|
2879
2878
|
execFn("pnpm", [
|
|
2880
2879
|
"--filter",
|
|
2881
2880
|
pkg,
|
|
2882
|
-
"
|
|
2881
|
+
"verification.typeSafety"
|
|
2883
2882
|
], {
|
|
2884
2883
|
cwd,
|
|
2885
2884
|
stdio: "inherit"
|
|
@@ -2887,7 +2886,7 @@ function runPluginCreate(input) {
|
|
|
2887
2886
|
execFn("pnpm", [
|
|
2888
2887
|
"--filter",
|
|
2889
2888
|
pkg,
|
|
2890
|
-
"
|
|
2889
|
+
"sourceQuality.staticAnalysis"
|
|
2891
2890
|
], {
|
|
2892
2891
|
cwd,
|
|
2893
2892
|
stdio: "inherit"
|
|
@@ -2895,7 +2894,7 @@ function runPluginCreate(input) {
|
|
|
2895
2894
|
execFn("pnpm", [
|
|
2896
2895
|
"--filter",
|
|
2897
2896
|
pkg,
|
|
2898
|
-
"
|
|
2897
|
+
"verification.unitTests"
|
|
2899
2898
|
], {
|
|
2900
2899
|
cwd,
|
|
2901
2900
|
stdio: "inherit"
|
|
@@ -3018,6 +3017,193 @@ function printNextSteps$1(print, inputs) {
|
|
|
3018
3017
|
* "revoke this token at <url>" reminder — same pattern as `rando vc
|
|
3019
3018
|
* setup` for the ephemeral GH admin PAT.
|
|
3020
3019
|
*/
|
|
3020
|
+
/**
|
|
3021
|
+
* `holocron publish --sync-trust <old-file> <new-file>` — bulk-migrates npm
|
|
3022
|
+
* Trusted Publisher (OIDC) config to a new workflow filename across every
|
|
3023
|
+
* public package. See issue #689 / `scripts/npm-trust-migrate.sh` (the
|
|
3024
|
+
* shell-script prototype this supersedes) for the full "why": npm ties
|
|
3025
|
+
* Trusted Publisher to a workflow FILENAME per package, so renaming the
|
|
3026
|
+
* release workflow breaks OIDC publish (`ENEEDAUTH`) until every package's
|
|
3027
|
+
* config is updated to match.
|
|
3028
|
+
*
|
|
3029
|
+
* Requires npm@11.15.0+ (`npm trust`) — always shells out through
|
|
3030
|
+
* `npx -y npm@latest` regardless of the locally-installed npm version.
|
|
3031
|
+
*
|
|
3032
|
+
* npm's `trust` mutations — and reads, once a short-lived OTP grant lapses —
|
|
3033
|
+
* require a fresh interactive browser 2FA approval (an `EOTP` error with an
|
|
3034
|
+
* `authUrl`). No token bypasses this (npm's own docs: Granular Access Tokens
|
|
3035
|
+
* with the bypass-2FA option are explicitly unsupported for trust commands).
|
|
3036
|
+
* This command can't drive that browser step itself, so on the first `EOTP`
|
|
3037
|
+
* it stops immediately, prints the `authUrl`, and returns — the operator
|
|
3038
|
+
* approves it and re-runs; already-migrated packages are skipped on the next
|
|
3039
|
+
* pass, so it's safe to resume from wherever it stopped.
|
|
3040
|
+
*/
|
|
3041
|
+
async function runSyncTrust(input) {
|
|
3042
|
+
const print = input.print ?? ((line) => console.log(line));
|
|
3043
|
+
const logger = input.logger ?? getLogger();
|
|
3044
|
+
const cwd = input.cwd ?? process.cwd();
|
|
3045
|
+
const repo = input.repo ?? "theholocron/holocron";
|
|
3046
|
+
const dryRun = input.dryRun ?? false;
|
|
3047
|
+
const exec = input.exec ?? defaultExec$1;
|
|
3048
|
+
const packageNames = input.packages ?? discoverPublicPackages(cwd);
|
|
3049
|
+
const npm = "npx";
|
|
3050
|
+
const npmArgs = ["-y", "npm@latest"];
|
|
3051
|
+
print(`Holocron publish --sync-trust${dryRun ? " (dry-run)" : ""}`);
|
|
3052
|
+
print(` ${input.oldFile} -> ${input.newFile} (repo: ${repo})`);
|
|
3053
|
+
print(` ${packageNames.length} public packages`);
|
|
3054
|
+
print("");
|
|
3055
|
+
logger.info({
|
|
3056
|
+
oldFile: input.oldFile,
|
|
3057
|
+
newFile: input.newFile,
|
|
3058
|
+
packages: packageNames.length
|
|
3059
|
+
}, "sync-trust: start");
|
|
3060
|
+
if (packageNames.length === 0) {
|
|
3061
|
+
const message = "nothing to sync (no packages/* and root package.json is private or unnamed)";
|
|
3062
|
+
print(` ✗ ${message}`);
|
|
3063
|
+
return {
|
|
3064
|
+
status: "fail",
|
|
3065
|
+
message,
|
|
3066
|
+
packages: []
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
if (dryRun) {
|
|
3070
|
+
for (const name of packageNames) print(` … would sync ${name}`);
|
|
3071
|
+
return {
|
|
3072
|
+
status: "dry-run",
|
|
3073
|
+
message: "dry-run — no npm calls made",
|
|
3074
|
+
packages: []
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
const results = [];
|
|
3078
|
+
for (const name of packageNames) {
|
|
3079
|
+
print(`=== ${name} ===`);
|
|
3080
|
+
const parsed = parseJson((await exec(npm, [
|
|
3081
|
+
...npmArgs,
|
|
3082
|
+
"trust",
|
|
3083
|
+
"list",
|
|
3084
|
+
name,
|
|
3085
|
+
"--json"
|
|
3086
|
+
], { cwd })).stdout);
|
|
3087
|
+
if (isEotp(parsed)) {
|
|
3088
|
+
const authUrl = parsed.error?.authUrl ?? "";
|
|
3089
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3090
|
+
print(" then re-run; already-migrated packages are skipped.");
|
|
3091
|
+
results.push({
|
|
3092
|
+
name,
|
|
3093
|
+
status: "needs-auth",
|
|
3094
|
+
message: authUrl
|
|
3095
|
+
});
|
|
3096
|
+
logger.warn({
|
|
3097
|
+
package: name,
|
|
3098
|
+
authUrl
|
|
3099
|
+
}, "sync-trust: needs auth, stopping");
|
|
3100
|
+
return {
|
|
3101
|
+
status: "fail",
|
|
3102
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3103
|
+
packages: results
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
const entry = parsed;
|
|
3107
|
+
if (entry.file === input.newFile) {
|
|
3108
|
+
print(` already on ${input.newFile}, skipping`);
|
|
3109
|
+
results.push({
|
|
3110
|
+
name,
|
|
3111
|
+
status: "skipped"
|
|
3112
|
+
});
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
if (entry.id) {
|
|
3116
|
+
print(` revoking existing trust (file=${entry.file ?? "?"}, id=${entry.id})`);
|
|
3117
|
+
const revoke = await exec(npm, [
|
|
3118
|
+
...npmArgs,
|
|
3119
|
+
"trust",
|
|
3120
|
+
"revoke",
|
|
3121
|
+
name,
|
|
3122
|
+
"--id",
|
|
3123
|
+
entry.id,
|
|
3124
|
+
"-y"
|
|
3125
|
+
], { cwd });
|
|
3126
|
+
if (revoke.exitCode !== 0 && isEotp(parseJson(revoke.stdout))) {
|
|
3127
|
+
const authUrl = parseJson(revoke.stdout).error?.authUrl ?? "";
|
|
3128
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3129
|
+
results.push({
|
|
3130
|
+
name,
|
|
3131
|
+
status: "needs-auth",
|
|
3132
|
+
message: authUrl
|
|
3133
|
+
});
|
|
3134
|
+
return {
|
|
3135
|
+
status: "fail",
|
|
3136
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3137
|
+
packages: results
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
} else print(" no existing trust config found");
|
|
3141
|
+
print(` creating trust: ${input.newFile}`);
|
|
3142
|
+
const create = await exec(npm, [
|
|
3143
|
+
...npmArgs,
|
|
3144
|
+
"trust",
|
|
3145
|
+
"github",
|
|
3146
|
+
name,
|
|
3147
|
+
"--repo",
|
|
3148
|
+
repo,
|
|
3149
|
+
"--file",
|
|
3150
|
+
input.newFile,
|
|
3151
|
+
"--allow-publish",
|
|
3152
|
+
"--allow-stage-publish",
|
|
3153
|
+
"-y"
|
|
3154
|
+
], { cwd });
|
|
3155
|
+
if (create.exitCode === 0) {
|
|
3156
|
+
print(" ✓ ok");
|
|
3157
|
+
results.push({
|
|
3158
|
+
name,
|
|
3159
|
+
status: "ok"
|
|
3160
|
+
});
|
|
3161
|
+
} else {
|
|
3162
|
+
const createParsed = parseJson(create.stdout);
|
|
3163
|
+
if (isEotp(createParsed)) {
|
|
3164
|
+
const authUrl = createParsed.error?.authUrl ?? "";
|
|
3165
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3166
|
+
results.push({
|
|
3167
|
+
name,
|
|
3168
|
+
status: "needs-auth",
|
|
3169
|
+
message: authUrl
|
|
3170
|
+
});
|
|
3171
|
+
return {
|
|
3172
|
+
status: "fail",
|
|
3173
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3174
|
+
packages: results
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
const message = create.stderr.trim() || create.stdout.trim() || `exit ${create.exitCode}`;
|
|
3178
|
+
print(` ✗ FAILED: ${message}`);
|
|
3179
|
+
results.push({
|
|
3180
|
+
name,
|
|
3181
|
+
status: "fail",
|
|
3182
|
+
message
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
const failed = results.filter((r) => r.status === "fail");
|
|
3187
|
+
logger.info({
|
|
3188
|
+
ok: results.filter((r) => r.status === "ok").length,
|
|
3189
|
+
failed: failed.length
|
|
3190
|
+
}, "sync-trust: done");
|
|
3191
|
+
return {
|
|
3192
|
+
status: failed.length > 0 ? "fail" : "ok",
|
|
3193
|
+
...failed.length > 0 ? { message: `${failed.length} package(s) failed` } : {},
|
|
3194
|
+
packages: results
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
function parseJson(raw) {
|
|
3198
|
+
try {
|
|
3199
|
+
return JSON.parse(raw);
|
|
3200
|
+
} catch {
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
function isEotp(parsed) {
|
|
3205
|
+
return typeof parsed === "object" && parsed !== null && "error" in parsed && parsed.error?.code === "EOTP";
|
|
3206
|
+
}
|
|
3021
3207
|
async function runPublish(input = {}) {
|
|
3022
3208
|
const print = input.print ?? ((line) => console.log(line));
|
|
3023
3209
|
const logger = input.logger ?? getLogger();
|
|
@@ -3180,7 +3366,7 @@ function printNextSteps(print, env, packageNames, repoName) {
|
|
|
3180
3366
|
print("");
|
|
3181
3367
|
print(" → next: configure Trusted Publisher for each package on npm:");
|
|
3182
3368
|
for (const name of packageNames) print(` https://www.npmjs.com/package/${name}/access`);
|
|
3183
|
-
print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow:
|
|
3369
|
+
print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow: delivery.publish.yml`);
|
|
3184
3370
|
if (env.get("NPM_TOKEN")) {
|
|
3185
3371
|
print("");
|
|
3186
3372
|
print(" → cleanup: $NPM_TOKEN was used. Revoke it now (no API for self-revoke; UI-only):");
|
|
@@ -4518,6 +4704,10 @@ const { workflowHeader: workflowHeader$1 } = createHeader({
|
|
|
4518
4704
|
source: "packages/cli/src/commands/setup/run-setup.ts",
|
|
4519
4705
|
tool: "holocron setup"
|
|
4520
4706
|
});
|
|
4707
|
+
const { workflowHeader: thinCallerHeader } = createHeader({
|
|
4708
|
+
source: "packages/astromech/src/thin-callers.ts",
|
|
4709
|
+
tool: "holocron setup"
|
|
4710
|
+
});
|
|
4521
4711
|
async function runSetup(input) {
|
|
4522
4712
|
const print = input.print ?? ((line) => console.log(line));
|
|
4523
4713
|
const logger = getLogger();
|
|
@@ -4552,7 +4742,7 @@ async function runSetup(input) {
|
|
|
4552
4742
|
}, { skipCodes: SECURITY_SKIP_CODES[method] }));
|
|
4553
4743
|
print(formatStep(steps[steps.length - 1]));
|
|
4554
4744
|
}
|
|
4555
|
-
const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("
|
|
4745
|
+
const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("security.codeScanning");
|
|
4556
4746
|
steps.push(await runStep("source", usesAdvancedCodeQL ? "disableDefaultCodeScanning" : "enableCodeScanning", dryRun, async () => {
|
|
4557
4747
|
if (usesAdvancedCodeQL) await source.disableDefaultCodeScanning();
|
|
4558
4748
|
else return await source.enableCodeScanning();
|
|
@@ -4589,7 +4779,8 @@ async function runSetup(input) {
|
|
|
4589
4779
|
}).thinCallers();
|
|
4590
4780
|
for (const entry of tasks) {
|
|
4591
4781
|
const name = typeof entry === "string" ? entry : entry.name;
|
|
4592
|
-
|
|
4782
|
+
const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
|
|
4783
|
+
if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
|
|
4593
4784
|
steps.push({
|
|
4594
4785
|
capability: "source",
|
|
4595
4786
|
step: `write workflow ${name}`,
|
|
@@ -4599,12 +4790,12 @@ async function runSetup(input) {
|
|
|
4599
4790
|
print(formatStep(steps[steps.length - 1]));
|
|
4600
4791
|
continue;
|
|
4601
4792
|
}
|
|
4602
|
-
const filename =
|
|
4793
|
+
const filename = `${name}.yml`;
|
|
4603
4794
|
const content = files.get(filename);
|
|
4604
4795
|
if (content === void 0) continue;
|
|
4605
|
-
const step =
|
|
4796
|
+
const step = content.includes("workflows/preview.yml@main") ? `write workflow ${name} (with preview)` : `write workflow ${name}`;
|
|
4606
4797
|
steps.push(await runStep("source", step, dryRun, async () => {
|
|
4607
|
-
await source.writeWorkflowFile(filename, `${
|
|
4798
|
+
await source.writeWorkflowFile(filename, `${thinCallerHeader()}${content}`);
|
|
4608
4799
|
}));
|
|
4609
4800
|
print(formatStep(steps[steps.length - 1]));
|
|
4610
4801
|
}
|
|
@@ -4699,7 +4890,7 @@ async function runSetup(input) {
|
|
|
4699
4890
|
print(formatStep(steps[steps.length - 1]));
|
|
4700
4891
|
}
|
|
4701
4892
|
{
|
|
4702
|
-
const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("
|
|
4893
|
+
const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("verification.unitTests");
|
|
4703
4894
|
const existing = await readFile(join(input.context.repoRoot, "codecov.yml"), "utf8").catch(() => null);
|
|
4704
4895
|
if (!hasTestWorkflow && existing == null) steps.push({
|
|
4705
4896
|
capability: "source",
|
|
@@ -4718,6 +4909,23 @@ async function runSetup(input) {
|
|
|
4718
4909
|
}));
|
|
4719
4910
|
print(formatStep(steps[steps.length - 1]));
|
|
4720
4911
|
}
|
|
4912
|
+
{
|
|
4913
|
+
const content = createAstromech({
|
|
4914
|
+
cwd: input.context.repoRoot,
|
|
4915
|
+
config: { tasks: config.tasks },
|
|
4916
|
+
logger
|
|
4917
|
+
}).turboConfig();
|
|
4918
|
+
if (content === null) steps.push({
|
|
4919
|
+
capability: "source",
|
|
4920
|
+
step: "write turbo.json",
|
|
4921
|
+
status: "skip",
|
|
4922
|
+
message: "no fan-out-eligible tasks configured"
|
|
4923
|
+
});
|
|
4924
|
+
else steps.push(await runStep("source", "write turbo.json", dryRun, async () => {
|
|
4925
|
+
await source.writeRepoFile("turbo.json", content);
|
|
4926
|
+
}));
|
|
4927
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4928
|
+
}
|
|
4721
4929
|
if (source.syncLabels) {
|
|
4722
4930
|
steps.push(await runStep("source", "sync labels", dryRun, async () => {
|
|
4723
4931
|
return source.syncLabels(CANONICAL_LABELS, STALE_LABELS);
|
|
@@ -4856,7 +5064,7 @@ async function runSetup(input) {
|
|
|
4856
5064
|
if (loader.has("deployment")) {
|
|
4857
5065
|
const deploy = loader.get("deployment");
|
|
4858
5066
|
print(style.step("deployment"));
|
|
4859
|
-
const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "deploy");
|
|
5067
|
+
const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "delivery.deploy" || e.name === "knowledge.docs" || e.name === "knowledge.components");
|
|
4860
5068
|
const previewCfg = deployEntry?.with ? extractPreviewConfig(deployEntry.with, {
|
|
4861
5069
|
org: config.org,
|
|
4862
5070
|
domain: config.domain
|
|
@@ -5580,7 +5788,7 @@ async function runSyncWiki(input) {
|
|
|
5580
5788
|
//#endregion
|
|
5581
5789
|
//#region src/commands/sync.ts
|
|
5582
5790
|
const { workflowHeader } = createHeader({
|
|
5583
|
-
source: "packages/
|
|
5791
|
+
source: "packages/astromech/src/thin-callers.ts",
|
|
5584
5792
|
tool: "holocron sync"
|
|
5585
5793
|
});
|
|
5586
5794
|
const SYNC_STEPS = [
|
|
@@ -5594,6 +5802,7 @@ const SYNC_STEPS = [
|
|
|
5594
5802
|
"readme",
|
|
5595
5803
|
"workflows",
|
|
5596
5804
|
"scripts",
|
|
5805
|
+
"turbo",
|
|
5597
5806
|
"wiki"
|
|
5598
5807
|
];
|
|
5599
5808
|
const LOCAL_STEPS = /* @__PURE__ */ new Set([
|
|
@@ -5603,6 +5812,7 @@ const LOCAL_STEPS = /* @__PURE__ */ new Set([
|
|
|
5603
5812
|
"readme",
|
|
5604
5813
|
"workflows",
|
|
5605
5814
|
"scripts",
|
|
5815
|
+
"turbo",
|
|
5606
5816
|
"wiki"
|
|
5607
5817
|
]);
|
|
5608
5818
|
async function runSync(input) {
|
|
@@ -5751,6 +5961,7 @@ async function runSync(input) {
|
|
|
5751
5961
|
"readme",
|
|
5752
5962
|
"workflows",
|
|
5753
5963
|
"scripts",
|
|
5964
|
+
"turbo",
|
|
5754
5965
|
"wiki"
|
|
5755
5966
|
]) {
|
|
5756
5967
|
if (requestedSteps !== void 0 && !requestedSteps.includes(stepName)) continue;
|
|
@@ -5855,7 +6066,8 @@ async function runSync(input) {
|
|
|
5855
6066
|
}).thinCallers();
|
|
5856
6067
|
for (const entry of taskEntries) {
|
|
5857
6068
|
const name = typeof entry === "string" ? entry : entry.name;
|
|
5858
|
-
|
|
6069
|
+
const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
|
|
6070
|
+
if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
|
|
5859
6071
|
steps.push({
|
|
5860
6072
|
capability: "local",
|
|
5861
6073
|
step: `sync workflow ${name}`,
|
|
@@ -5865,10 +6077,10 @@ async function runSync(input) {
|
|
|
5865
6077
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
5866
6078
|
continue;
|
|
5867
6079
|
}
|
|
5868
|
-
const filename =
|
|
6080
|
+
const filename = `${name}.yml`;
|
|
5869
6081
|
const content = files.get(filename);
|
|
5870
6082
|
if (content === void 0) continue;
|
|
5871
|
-
const step = `sync workflow ${name}${
|
|
6083
|
+
const step = `sync workflow ${name}${content.includes("workflows/preview.yml@main") ? " (with preview)" : ""}`;
|
|
5872
6084
|
steps.push(await runSyncStep("local", step, dryRun, async () => {
|
|
5873
6085
|
await writeWorkflowFile(input.context.repoRoot, filename, `${workflowHeader()}${content}`);
|
|
5874
6086
|
}));
|
|
@@ -5900,6 +6112,27 @@ async function runSync(input) {
|
|
|
5900
6112
|
}));
|
|
5901
6113
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
5902
6114
|
}
|
|
6115
|
+
if (stepName === "turbo") {
|
|
6116
|
+
const content = createAstromech({
|
|
6117
|
+
cwd: input.context.repoRoot,
|
|
6118
|
+
config: { tasks: config.tasks },
|
|
6119
|
+
logger
|
|
6120
|
+
}).turboConfig();
|
|
6121
|
+
if (content === null) {
|
|
6122
|
+
steps.push({
|
|
6123
|
+
capability: "local",
|
|
6124
|
+
step: "sync turbo.json",
|
|
6125
|
+
status: "skip",
|
|
6126
|
+
message: "no fan-out-eligible tasks configured"
|
|
6127
|
+
});
|
|
6128
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
6129
|
+
} else {
|
|
6130
|
+
steps.push(await runSyncStep("local", "sync turbo.json", dryRun, async () => {
|
|
6131
|
+
await writeFile(join(input.context.repoRoot, "turbo.json"), content, "utf8");
|
|
6132
|
+
}));
|
|
6133
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
5903
6136
|
if (stepName === "wiki") {
|
|
5904
6137
|
const result = await runSyncWiki({
|
|
5905
6138
|
loaded: input.loaded,
|
|
@@ -7751,9 +7984,31 @@ try {
|
|
|
7751
7984
|
}).option("otp", {
|
|
7752
7985
|
type: "string",
|
|
7753
7986
|
describe: "One-time password from your authenticator (required if npm needs 2FA for writes)"
|
|
7987
|
+
}).option("sync-trust", {
|
|
7988
|
+
type: "array",
|
|
7989
|
+
describe: "Migrate npm Trusted Publisher config to a new workflow filename across every public package: --sync-trust <old-file> <new-file>. See issue #689 — npm requires fresh interactive 2FA per package; re-run after approving the printed authUrl if it stops early."
|
|
7990
|
+
}).option("repo", {
|
|
7991
|
+
type: "string",
|
|
7992
|
+
describe: "owner/repo for Trusted Publisher config (--sync-trust). Defaults to theholocron/holocron."
|
|
7754
7993
|
}), async (argv) => {
|
|
7994
|
+
if (argv.syncTrust) {
|
|
7995
|
+
const [oldFile, newFile] = argv.syncTrust;
|
|
7996
|
+
if (!oldFile || !newFile) {
|
|
7997
|
+
getLogger().error("publish --sync-trust requires exactly two values: <old-file> <new-file>");
|
|
7998
|
+
process.exitCode = 1;
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
8001
|
+
if ((await runSyncTrust({
|
|
8002
|
+
cwd: argv.cwd,
|
|
8003
|
+
oldFile,
|
|
8004
|
+
newFile,
|
|
8005
|
+
dryRun: argv.dryRun,
|
|
8006
|
+
...argv.repo ? { repo: argv.repo } : {}
|
|
8007
|
+
})).status === "fail") process.exitCode = 1;
|
|
8008
|
+
return;
|
|
8009
|
+
}
|
|
7755
8010
|
if (!argv.initial) {
|
|
7756
|
-
getLogger().error("publish: only `--initial`
|
|
8011
|
+
getLogger().error("publish: only `--initial` and `--sync-trust` are supported today. Run `holocron publish --initial`.");
|
|
7757
8012
|
process.exitCode = 1;
|
|
7758
8013
|
return;
|
|
7759
8014
|
}
|