@theholocron/cli 4.15.0 → 4.16.1
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/README.md +25 -1
- package/dist/cli.mjs +272 -4
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -99,6 +99,16 @@ export default defineConfig({
|
|
|
99
99
|
});
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
The generated ruleset also sets `require_extra_approval_for_unattributed_changes:
|
|
103
|
+
false` — with `required_approving_review_count: 0` and a single maintainer, the
|
|
104
|
+
GitHub default (`true`) is an unsatisfiable block on any PR with an
|
|
105
|
+
unverified/unattributed commit. Repository-admin bypass still covers the real
|
|
106
|
+
risk. And the `codecov.yml` `holocron setup` writes carries `if_not_found:
|
|
107
|
+
success` on every status default, so an infra-only PR (dep bump, config,
|
|
108
|
+
lockfile — no coverable change) passes instead of hanging on a `codecov/*`
|
|
109
|
+
required check that never posts. A re-run of `holocron setup` backfills both into
|
|
110
|
+
an existing repo.
|
|
111
|
+
|
|
102
112
|
### Git hooks
|
|
103
113
|
|
|
104
114
|
```ts
|
|
@@ -275,6 +285,20 @@ export default acmeConfig;
|
|
|
275
285
|
|
|
276
286
|
```
|
|
277
287
|
|
|
288
|
+
## Upgrading
|
|
289
|
+
|
|
290
|
+
- **`holocron upgrade node <to>`** — patches every Node.js version pin
|
|
291
|
+
(`engines.node`, `.nvmrc`, `.node-version`, `.github/workflows/*`,
|
|
292
|
+
`Dockerfile`, `.tool-versions`) to a new major.
|
|
293
|
+
- **`holocron upgrade deps`** — bumps every `@theholocron/*` pin in
|
|
294
|
+
`pnpm-workspace.yaml` (`catalog:` and every named `catalogs.*`) to its latest
|
|
295
|
+
published version, then migrates `holocron.config.ts` from the 7.x preset
|
|
296
|
+
shape (`const { repo, workflows } = node()` + `repo.requiredChecks` +
|
|
297
|
+
`workflows:`) to the current composed-preset API (`const preset = node()` +
|
|
298
|
+
`...preset` + `tasks:` + `extraRequiredChecks:`). Mechanical transforms only —
|
|
299
|
+
review the result, then `pnpm install && holocron ci`. `--pins-only` skips
|
|
300
|
+
the config migration; `--dry-run` prints without writing.
|
|
301
|
+
|
|
278
302
|
## Auth — fine-grained tokens
|
|
279
303
|
|
|
280
304
|
Each GitHub capability resolves its own fine-grained PAT so a compromised
|
|
@@ -385,7 +409,7 @@ holds the orchestration + the Holocron-specific credential resolution
|
|
|
385
409
|
never hand-edit its `.github/workflows/*`).
|
|
386
410
|
- `src/commands/` — `setup`, `sync`, `doctor`, `deploy`, `secret set`,
|
|
387
411
|
`secrets sync`, `npm publish-initial`, `sync-github`, `upgrade node`,
|
|
388
|
-
`plugin create`, `auth`
|
|
412
|
+
`upgrade deps`, `plugin create`, `auth`
|
|
389
413
|
|
|
390
414
|
## Status
|
|
391
415
|
|
package/dist/cli.mjs
CHANGED
|
@@ -3379,7 +3379,7 @@ function createIgnoreConfig() {
|
|
|
3379
3379
|
}
|
|
3380
3380
|
//#endregion
|
|
3381
3381
|
//#region src/templates/configs/codecov/codecov.yml
|
|
3382
|
-
var codecov_default = "codecov:\n require_ci_to_pass: true\n\ncoverage:\n precision: 2\n round: down\n status:\n project:\n default:\n target: auto\n threshold: 2%\n patch:\n default:\n target: 80%\n\ncomment:\n layout: \"reach,diff,flags,components\"\n behavior: default\n require_changes: true\n\ncomponent_management:\n default_rules:\n statuses:\n - type: patch\n target: 80%\n individual_components:\n";
|
|
3382
|
+
var codecov_default = "codecov:\n require_ci_to_pass: true\n\ncoverage:\n precision: 2\n round: down\n status:\n project:\n default:\n target: auto\n threshold: 2%\n # Infra-only PRs (dep bumps, config, lockfile) touch no coverable code,\n # so codecov produces no report to compare. Pass instead of blocking\n # forever on a required check that will never post.\n if_not_found: success\n patch:\n default:\n target: 80%\n if_not_found: success\n\ncomment:\n layout: \"reach,diff,flags,components\"\n behavior: default\n require_changes: true\n\ncomponent_management:\n default_rules:\n statuses:\n - type: patch\n target: 80%\n if_not_found: success\n individual_components:\n";
|
|
3383
3383
|
//#endregion
|
|
3384
3384
|
//#region src/templates/configs/codecov/utils.ts
|
|
3385
3385
|
const INDIVIDUAL_COMPONENTS_MARKER = " individual_components:";
|
|
@@ -3393,10 +3393,45 @@ function codecovComponentBlock(packages) {
|
|
|
3393
3393
|
``
|
|
3394
3394
|
]).join("\n");
|
|
3395
3395
|
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Ensure every codecov status default carries `if_not_found: success`, so an
|
|
3398
|
+
* infra-only PR (dep bump, config, lockfile — no coverable change) passes
|
|
3399
|
+
* instead of hanging forever on a required check codecov never posts.
|
|
3400
|
+
*
|
|
3401
|
+
* Injects the key on the line after each `target:` that sits inside a status
|
|
3402
|
+
* block (`coverage.status.*.default` or a `statuses:` list item — indent ≥ 8,
|
|
3403
|
+
* or ≥ 8 for a `- type:` list entry), when the immediate sibling lines do not
|
|
3404
|
+
* already declare it.
|
|
3405
|
+
*/
|
|
3406
|
+
function ensureIfNotFound(yaml) {
|
|
3407
|
+
const lines = yaml.split("\n");
|
|
3408
|
+
const out = [];
|
|
3409
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3410
|
+
const line = lines[i];
|
|
3411
|
+
out.push(line);
|
|
3412
|
+
const m = /^(\s{8,})target:\s+\S+\s*$/.exec(line);
|
|
3413
|
+
if (!m) continue;
|
|
3414
|
+
const indent = m[1];
|
|
3415
|
+
let alreadySet = false;
|
|
3416
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
3417
|
+
const sib = lines[j];
|
|
3418
|
+
if (sib.trim() === "") continue;
|
|
3419
|
+
const sibIndent = sib.match(/^\s*/)[0].length;
|
|
3420
|
+
if (sibIndent < indent.length) break;
|
|
3421
|
+
if (sibIndent === indent.length && /^\s*if_not_found\s*:/.test(sib)) {
|
|
3422
|
+
alreadySet = true;
|
|
3423
|
+
break;
|
|
3424
|
+
}
|
|
3425
|
+
if (sibIndent === indent.length && /^\s*(- |\w)/.test(sib) && !/^\s*(threshold|flags|paths|branches)\s*:/.test(sib)) break;
|
|
3426
|
+
}
|
|
3427
|
+
if (!alreadySet) out.push(`${indent}if_not_found: success`);
|
|
3428
|
+
}
|
|
3429
|
+
return out.join("\n");
|
|
3430
|
+
}
|
|
3396
3431
|
function mergeCodecovComponents(existing, packages) {
|
|
3397
3432
|
const idx = existing.indexOf(INDIVIDUAL_COMPONENTS_MARKER);
|
|
3398
|
-
if (idx === -1) return existing;
|
|
3399
|
-
return existing.slice(0, idx + 24) + codecovComponentBlock(packages);
|
|
3433
|
+
if (idx === -1) return ensureIfNotFound(existing);
|
|
3434
|
+
return ensureIfNotFound(existing.slice(0, idx + 24)) + codecovComponentBlock(packages);
|
|
3400
3435
|
}
|
|
3401
3436
|
async function readWorkspacePackages(repoRoot) {
|
|
3402
3437
|
const packagesDir = join(repoRoot, "packages");
|
|
@@ -3965,7 +4000,8 @@ function buildRulesetPayload(requiredChecks = []) {
|
|
|
3965
4000
|
dismiss_stale_reviews_on_push: false,
|
|
3966
4001
|
require_code_owner_review: false,
|
|
3967
4002
|
require_last_push_approval: false,
|
|
3968
|
-
required_review_thread_resolution: true
|
|
4003
|
+
required_review_thread_resolution: true,
|
|
4004
|
+
require_extra_approval_for_unattributed_changes: false
|
|
3969
4005
|
}
|
|
3970
4006
|
}
|
|
3971
4007
|
];
|
|
@@ -6234,6 +6270,224 @@ async function runSyncGithub(input) {
|
|
|
6234
6270
|
});
|
|
6235
6271
|
}
|
|
6236
6272
|
//#endregion
|
|
6273
|
+
//#region src/commands/upgrade-deps.ts
|
|
6274
|
+
/**
|
|
6275
|
+
* `holocron upgrade deps` — bring a repo's `@theholocron/*` dependency pins and
|
|
6276
|
+
* its `holocron.config.ts` up to the current major.
|
|
6277
|
+
*
|
|
6278
|
+
* Two concerns:
|
|
6279
|
+
*
|
|
6280
|
+
* 1. **Catalog pins.** Every `@theholocron/*` entry in `pnpm-workspace.yaml`
|
|
6281
|
+
* (`catalog:` and every named `catalogs.*`) is bumped to the latest published
|
|
6282
|
+
* version, preserving the range prefix (`^` / `~`). Never downgrades.
|
|
6283
|
+
* 2. **`holocron.config.ts` migration.** `@theholocron/holocron-config@8`
|
|
6284
|
+
* dropped the 7.x preset shape (`const { repo, workflows, providers } =
|
|
6285
|
+
* node()` + `repo.requiredChecks` + `workflows:`) for a composed-preset
|
|
6286
|
+
* (`const preset = node()` + `...preset` + `tasks:` + `extraRequiredChecks:`).
|
|
6287
|
+
* This applies the mechanical transforms and flags anything that needs a
|
|
6288
|
+
* human eye.
|
|
6289
|
+
*
|
|
6290
|
+
* After running, `pnpm install && holocron ci`.
|
|
6291
|
+
*/
|
|
6292
|
+
const FETCH_TIMEOUT_MS$1 = 4e3;
|
|
6293
|
+
/** Resolve a package's `latest` dist-tag from the npm registry. */
|
|
6294
|
+
const fetchLatestFromNpm = async (pkg) => {
|
|
6295
|
+
try {
|
|
6296
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkg)}`, {
|
|
6297
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS$1),
|
|
6298
|
+
headers: { accept: "application/vnd.npm.install-v1+json" }
|
|
6299
|
+
});
|
|
6300
|
+
if (!res.ok) return null;
|
|
6301
|
+
return (await res.json())["dist-tags"]?.["latest"] ?? null;
|
|
6302
|
+
} catch {
|
|
6303
|
+
return null;
|
|
6304
|
+
}
|
|
6305
|
+
};
|
|
6306
|
+
/** `a > b` for `x.y.z` release versions (prerelease tags are ignored). */
|
|
6307
|
+
function gt(a, b) {
|
|
6308
|
+
const pa = a.split(".").map((n) => parseInt(n, 10));
|
|
6309
|
+
const pb = b.split(".").map((n) => parseInt(n, 10));
|
|
6310
|
+
for (let i = 0; i < 3; i++) {
|
|
6311
|
+
const x = pa[i] ?? 0;
|
|
6312
|
+
const y = pb[i] ?? 0;
|
|
6313
|
+
if (x !== y) return x > y;
|
|
6314
|
+
}
|
|
6315
|
+
return false;
|
|
6316
|
+
}
|
|
6317
|
+
const CATALOG_LINE = /^(\s*['"]?)(@theholocron\/[a-z0-9-]+)(['"]?\s*:\s*['"]?)([~^]?)(\d+\.\d+\.\d+[\w.-]*)(['"]?\s*)$/;
|
|
6318
|
+
/**
|
|
6319
|
+
* Rewrite every `@theholocron/*` pin in a `pnpm-workspace.yaml` to `latest`,
|
|
6320
|
+
* keeping the range prefix. `latest` is looked up once per package.
|
|
6321
|
+
*/
|
|
6322
|
+
async function bumpCatalogs(content, fetchLatest) {
|
|
6323
|
+
const lines = content.split("\n");
|
|
6324
|
+
const wanted = /* @__PURE__ */ new Set();
|
|
6325
|
+
for (const line of lines) {
|
|
6326
|
+
const m = CATALOG_LINE.exec(line);
|
|
6327
|
+
if (m) wanted.add(m[2]);
|
|
6328
|
+
}
|
|
6329
|
+
const latest = /* @__PURE__ */ new Map();
|
|
6330
|
+
await Promise.all([...wanted].map(async (pkg) => {
|
|
6331
|
+
latest.set(pkg, await fetchLatest(pkg));
|
|
6332
|
+
}));
|
|
6333
|
+
const bumps = [];
|
|
6334
|
+
return {
|
|
6335
|
+
content: lines.map((line) => {
|
|
6336
|
+
const m = CATALOG_LINE.exec(line);
|
|
6337
|
+
if (!m) return line;
|
|
6338
|
+
const [, pre, pkg, mid, prefix, current, post] = m;
|
|
6339
|
+
const next = latest.get(pkg);
|
|
6340
|
+
if (!next || next === current || !gt(next, current)) return line;
|
|
6341
|
+
bumps.push({
|
|
6342
|
+
pkg,
|
|
6343
|
+
from: current,
|
|
6344
|
+
to: next
|
|
6345
|
+
});
|
|
6346
|
+
return `${pre}${pkg}${mid}${prefix}${next}${post}`;
|
|
6347
|
+
}).join("\n"),
|
|
6348
|
+
bumps
|
|
6349
|
+
};
|
|
6350
|
+
}
|
|
6351
|
+
/** True if the file still uses the 7.x preset shape. */
|
|
6352
|
+
function isLegacyConfig(content) {
|
|
6353
|
+
return /const\s*\{[^}]*\b(repo|workflows|providers)\b[^}]*\}\s*=\s*(compose\(|[a-zA-Z]+\(\))/.test(content) || /\.\.\.repo\.requiredChecks/.test(content) || /^\s*workflows\s*:/m.test(content);
|
|
6354
|
+
}
|
|
6355
|
+
/**
|
|
6356
|
+
* Apply the mechanical 7.x → 8.x transforms to a `holocron.config.ts`. Best
|
|
6357
|
+
* effort — the result should always be reviewed and run through `holocron ci`.
|
|
6358
|
+
*/
|
|
6359
|
+
function migrateConfig(content) {
|
|
6360
|
+
const transforms = [];
|
|
6361
|
+
const warnings = [];
|
|
6362
|
+
let s = content;
|
|
6363
|
+
if (!isLegacyConfig(s)) return {
|
|
6364
|
+
content,
|
|
6365
|
+
changed: false,
|
|
6366
|
+
transforms,
|
|
6367
|
+
warnings
|
|
6368
|
+
};
|
|
6369
|
+
s = s.replace(/const\s*\{[^}]*\}\s*=\s*((?:compose\([\s\S]*?\)|[a-zA-Z]+\([\s\S]*?\)))\s*;/, (_m, expr) => {
|
|
6370
|
+
transforms.push("destructured preset → `const preset = …`");
|
|
6371
|
+
return `const preset = ${expr.trim()};`;
|
|
6372
|
+
});
|
|
6373
|
+
if (/const preset =/.test(s) && !/\.\.\.preset\b/.test(s)) {
|
|
6374
|
+
s = s.replace(/(defineConfig\(\{\s*\n)/, `$1\t...preset,\n`);
|
|
6375
|
+
transforms.push("spread `...preset` into defineConfig()");
|
|
6376
|
+
}
|
|
6377
|
+
s = s.replace(/^\torg,\n/m, "");
|
|
6378
|
+
s = s.replace(/^\tdomain,\n/m, "");
|
|
6379
|
+
s = s.replace(/^\tdocs,\n/m, "");
|
|
6380
|
+
if (/\.\.\.repo\.properties/.test(s)) {
|
|
6381
|
+
s = s.replace(/\.\.\.repo\.properties/g, "...preset.repo?.properties");
|
|
6382
|
+
transforms.push("`...repo.properties` → `...preset.repo?.properties`");
|
|
6383
|
+
}
|
|
6384
|
+
if (/([{[,]\s*)\.\.\.repo\b(?!\.)/.test(s)) {
|
|
6385
|
+
s = s.replace(/([{[,]\s*)\.\.\.repo\b(?!\.)/g, "$1...preset.repo");
|
|
6386
|
+
transforms.push("`...repo` → `...preset.repo`");
|
|
6387
|
+
}
|
|
6388
|
+
if (/\.\.\.providers\b/.test(s)) {
|
|
6389
|
+
s = s.replace(/\.\.\.providers\b/g, "...preset.providers");
|
|
6390
|
+
transforms.push("`...providers` → `...preset.providers`");
|
|
6391
|
+
}
|
|
6392
|
+
const rc = /(\n\s*)requiredChecks\s*:\s*\[\s*\n?\s*\.\.\.repo\.requiredChecks\s*,?\s*([\s\S]*?)\n\s*\]\s*,/.exec(s);
|
|
6393
|
+
if (rc) {
|
|
6394
|
+
const custom = rc[2].trim().replace(/,\s*$/, "");
|
|
6395
|
+
s = s.replace(rc[0], "");
|
|
6396
|
+
if (custom) {
|
|
6397
|
+
const block = `\textraRequiredChecks: [\n\t\t${custom.split("\n").map((l) => l.trim()).filter(Boolean).join("\n ")}\n\t],\n`;
|
|
6398
|
+
s = s.replace(/(defineConfig\(\{\s*\n(?:\t\.\.\.preset,\n)?)/, `$1${block}`);
|
|
6399
|
+
transforms.push("`repo.requiredChecks` custom entries → top-level `extraRequiredChecks`");
|
|
6400
|
+
warnings.push("check the moved `extraRequiredChecks` — the preset already contributes lint/test/typecheck");
|
|
6401
|
+
} else {
|
|
6402
|
+
s = s.replace(/,(\s*)\.\.\.repo\.requiredChecks/g, "$1");
|
|
6403
|
+
transforms.push("dropped `...repo.requiredChecks`");
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
if (/^\s*workflows\s*:/m.test(s)) {
|
|
6407
|
+
s = s.replace(/(\n\s*)workflows(\s*:\s*\[)/, "$1tasks$2");
|
|
6408
|
+
s = s.replace(/\.\.\.workflows\b/g, "...preset.tasks");
|
|
6409
|
+
transforms.push("`workflows:` → `tasks:` (`...workflows` → `...preset.tasks`)");
|
|
6410
|
+
warnings.push("review the `tasks` array — a bare workflow name that should gate merges now needs `{ name, required: true }`");
|
|
6411
|
+
}
|
|
6412
|
+
return {
|
|
6413
|
+
content: s,
|
|
6414
|
+
changed: s !== content,
|
|
6415
|
+
transforms,
|
|
6416
|
+
warnings
|
|
6417
|
+
};
|
|
6418
|
+
}
|
|
6419
|
+
async function runUpgradeDeps(input = {}) {
|
|
6420
|
+
const print = input.print ?? ((line) => console.log(line));
|
|
6421
|
+
const logger = input.logger ?? getLogger();
|
|
6422
|
+
const cwd = input.cwd ?? process.cwd();
|
|
6423
|
+
const dryRun = input.dryRun ?? false;
|
|
6424
|
+
const pinsOnly = input.pinsOnly ?? false;
|
|
6425
|
+
const _readFile = input.readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
6426
|
+
const _writeFile = input.writeFile ?? ((p, c) => writeFileSync(p, c));
|
|
6427
|
+
const fetchLatest = input.fetchLatest ?? fetchLatestFromNpm;
|
|
6428
|
+
logger.info({
|
|
6429
|
+
dryRun: dryRun || void 0,
|
|
6430
|
+
pinsOnly: pinsOnly || void 0
|
|
6431
|
+
}, "upgrade deps: start");
|
|
6432
|
+
print(`Upgrading @theholocron/* dependencies${dryRun ? " (dry-run)" : ""}…`);
|
|
6433
|
+
const wsPath = join(cwd, "pnpm-workspace.yaml");
|
|
6434
|
+
let bumps = [];
|
|
6435
|
+
try {
|
|
6436
|
+
const result = await bumpCatalogs(_readFile(wsPath), fetchLatest);
|
|
6437
|
+
bumps = result.bumps;
|
|
6438
|
+
if (bumps.length > 0) {
|
|
6439
|
+
if (!dryRun) _writeFile(wsPath, result.content);
|
|
6440
|
+
print("");
|
|
6441
|
+
print(` pnpm-workspace.yaml — ${bumps.length} pin${bumps.length === 1 ? "" : "s"}`);
|
|
6442
|
+
for (const b of bumps) print(` ${dryRun ? "~" : "✓"} ${b.pkg} ${b.from} → ${b.to}`);
|
|
6443
|
+
} else print(" · pnpm-workspace.yaml — @theholocron/* pins already current");
|
|
6444
|
+
} catch {
|
|
6445
|
+
print(" · no pnpm-workspace.yaml — skipping catalog pins");
|
|
6446
|
+
}
|
|
6447
|
+
let configMigrated = false;
|
|
6448
|
+
let configWarnings = [];
|
|
6449
|
+
if (!pinsOnly) for (const name of ["holocron.config.ts", "holocron.config.js"]) {
|
|
6450
|
+
let cfg;
|
|
6451
|
+
try {
|
|
6452
|
+
cfg = _readFile(join(cwd, name));
|
|
6453
|
+
} catch {
|
|
6454
|
+
continue;
|
|
6455
|
+
}
|
|
6456
|
+
const m = migrateConfig(cfg);
|
|
6457
|
+
if (!m.changed) {
|
|
6458
|
+
print(` · ${name} — already on the current preset API`);
|
|
6459
|
+
break;
|
|
6460
|
+
}
|
|
6461
|
+
if (!dryRun) _writeFile(join(cwd, name), m.content);
|
|
6462
|
+
configMigrated = true;
|
|
6463
|
+
configWarnings = m.warnings;
|
|
6464
|
+
print("");
|
|
6465
|
+
print(` ${name} — ${dryRun ? "would migrate" : "migrated"} to the 8.x preset API`);
|
|
6466
|
+
for (const t of m.transforms) print(` ${dryRun ? "~" : "✓"} ${t}`);
|
|
6467
|
+
for (const w of m.warnings) print(` ⚠ ${w}`);
|
|
6468
|
+
break;
|
|
6469
|
+
}
|
|
6470
|
+
print("");
|
|
6471
|
+
if (bumps.length === 0 && !configMigrated) print("Nothing to do — already up to date.");
|
|
6472
|
+
else {
|
|
6473
|
+
print(`Next: ${dryRun ? "run without --dry-run, then " : ""}\`pnpm install && holocron ci\``);
|
|
6474
|
+
if (configMigrated) print(" review holocron.config.ts before committing.");
|
|
6475
|
+
}
|
|
6476
|
+
const status = dryRun ? "dry-run" : "ok";
|
|
6477
|
+
logger.info({
|
|
6478
|
+
bumps: bumps.length,
|
|
6479
|
+
configMigrated,
|
|
6480
|
+
warnings: configWarnings.length,
|
|
6481
|
+
status
|
|
6482
|
+
}, "upgrade deps: done");
|
|
6483
|
+
return {
|
|
6484
|
+
status,
|
|
6485
|
+
bumps,
|
|
6486
|
+
configMigrated,
|
|
6487
|
+
configWarnings
|
|
6488
|
+
};
|
|
6489
|
+
}
|
|
6490
|
+
//#endregion
|
|
6237
6491
|
//#region src/commands/upgrade-node.ts
|
|
6238
6492
|
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
6239
6493
|
"node_modules",
|
|
@@ -7433,6 +7687,20 @@ try {
|
|
|
7433
7687
|
if (report.message) getLogger().error(`upgrade node: ${report.message}`);
|
|
7434
7688
|
process.exitCode = 1;
|
|
7435
7689
|
}
|
|
7690
|
+
}).command("deps", "Bump every @theholocron/* pin to latest and migrate holocron.config.ts to the current preset API", (yy) => yy.option("pins-only", {
|
|
7691
|
+
type: "boolean",
|
|
7692
|
+
default: false,
|
|
7693
|
+
describe: "Only bump the catalog pins — skip the holocron.config.ts migration"
|
|
7694
|
+
}), async (argv) => {
|
|
7695
|
+
const report = await runUpgradeDeps({
|
|
7696
|
+
cwd: argv.cwd,
|
|
7697
|
+
dryRun: argv.dryRun,
|
|
7698
|
+
pinsOnly: argv.pinsOnly
|
|
7699
|
+
});
|
|
7700
|
+
if (report.status === "fail") {
|
|
7701
|
+
if (report.message) getLogger().error(`upgrade deps: ${report.message}`);
|
|
7702
|
+
process.exitCode = 1;
|
|
7703
|
+
}
|
|
7436
7704
|
}).demandCommand(1, "Run `holocron upgrade --help` to see available upgrade subcommands."), () => {}).command("auth <subcommand>", "Manage bootstrap credentials in the OS keyring", (y) => y.command("set <provider> [value]", "Verify + store a bootstrap token for a provider", (yy) => yy.positional("provider", {
|
|
7437
7705
|
type: "string",
|
|
7438
7706
|
demandOption: true
|