@theholocron/cli 3.58.0 → 3.58.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/dist/cli.mjs +48 -12
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -549,24 +549,55 @@ var PluginLoader = class {
|
|
|
549
549
|
context;
|
|
550
550
|
importer;
|
|
551
551
|
registry = /* @__PURE__ */ new Map();
|
|
552
|
+
failures = [];
|
|
552
553
|
constructor(config, context, importer = defaultImporter) {
|
|
553
554
|
this.config = config;
|
|
554
555
|
this.context = context;
|
|
555
556
|
this.importer = importer;
|
|
556
557
|
}
|
|
557
|
-
/**
|
|
558
|
+
/**
|
|
559
|
+
* Imports every configured plugin and builds the capability registry.
|
|
560
|
+
*
|
|
561
|
+
* Never throws for a single plugin's failure — a missing vendor token,
|
|
562
|
+
* an uninstalled package, or an unimplemented capability records a
|
|
563
|
+
* {@link PluginLoadFailure} and the load continues. This is the
|
|
564
|
+
* "soft-skip over hard-fail" contract: a command that needs a
|
|
565
|
+
* capability learns it is absent via `has()` / `get()` (which
|
|
566
|
+
* re-surfaces the original error), and orchestrators report the skip
|
|
567
|
+
* in their summary. Inspect {@link loadFailures} for the full list.
|
|
568
|
+
*/
|
|
558
569
|
async load() {
|
|
559
570
|
const entries = Object.entries(this.config.providers);
|
|
560
571
|
for (const [key, entry] of entries) {
|
|
561
572
|
if (!entry) continue;
|
|
562
|
-
if (entry.cardinality === "single")
|
|
573
|
+
if (entry.cardinality === "single") try {
|
|
574
|
+
this.registry.set(key, await this.loadOne(key, entry.tuple));
|
|
575
|
+
} catch (err) {
|
|
576
|
+
this.recordFailure(key, entry.tuple, err);
|
|
577
|
+
}
|
|
563
578
|
else {
|
|
564
579
|
const impls = [];
|
|
565
|
-
for (const tuple of entry.tuples)
|
|
566
|
-
|
|
580
|
+
for (const tuple of entry.tuples) try {
|
|
581
|
+
impls.push(await this.loadOne(key, tuple));
|
|
582
|
+
} catch (err) {
|
|
583
|
+
this.recordFailure(key, tuple, err);
|
|
584
|
+
}
|
|
585
|
+
if (impls.length > 0) this.registry.set(key, impls);
|
|
567
586
|
}
|
|
568
587
|
}
|
|
569
588
|
}
|
|
589
|
+
recordFailure(key, tuple, err) {
|
|
590
|
+
this.failures.push({
|
|
591
|
+
key,
|
|
592
|
+
provider: tuple.provider,
|
|
593
|
+
packageName: tuple.packageName,
|
|
594
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
/** Providers that failed to load during {@link load}. Empty on a clean load. */
|
|
598
|
+
loadFailures() {
|
|
599
|
+
return this.failures;
|
|
600
|
+
}
|
|
570
601
|
/**
|
|
571
602
|
* Type-safe lookup. Single-cardinality keys return one impl;
|
|
572
603
|
* many-cardinality keys return an array. `ResolvedCapability<K>`
|
|
@@ -574,7 +605,11 @@ var PluginLoader = class {
|
|
|
574
605
|
*/
|
|
575
606
|
get(key) {
|
|
576
607
|
const impl = this.registry.get(key);
|
|
577
|
-
if (impl === void 0)
|
|
608
|
+
if (impl === void 0) {
|
|
609
|
+
const failure = this.failures.find((f) => f.key === key);
|
|
610
|
+
if (failure) throw failure.error;
|
|
611
|
+
throw new LoaderError(`capability \`${key}\` is not loaded — is it declared in holocron.config.json?`);
|
|
612
|
+
}
|
|
578
613
|
return impl;
|
|
579
614
|
}
|
|
580
615
|
/** Whether a capability has been loaded. */
|
|
@@ -994,6 +1029,12 @@ async function runDoctor(input) {
|
|
|
994
1029
|
print(style.header(`Holocron doctor — ${config.name}`));
|
|
995
1030
|
print(style.dim(` config: ${input.loaded.filepath}`));
|
|
996
1031
|
print("");
|
|
1032
|
+
for (const failure of loader.loadFailures()) rows.push({
|
|
1033
|
+
capability: failure.key,
|
|
1034
|
+
provider: failure.provider,
|
|
1035
|
+
status: "fail",
|
|
1036
|
+
message: failure.error.message
|
|
1037
|
+
});
|
|
997
1038
|
for (const key of loader.loadedKeys()) {
|
|
998
1039
|
const cardinality = CARDINALITY[key];
|
|
999
1040
|
const entry = config.providers[key];
|
|
@@ -5263,12 +5304,7 @@ async function runSync(input) {
|
|
|
5263
5304
|
const dryRun = input.context.dryRun ?? false;
|
|
5264
5305
|
const requestedSteps = input.steps;
|
|
5265
5306
|
const steps = [];
|
|
5266
|
-
|
|
5267
|
-
else try {
|
|
5268
|
-
await loader.load();
|
|
5269
|
-
} catch (err) {
|
|
5270
|
-
if (!(err instanceof AuthError) && !(err instanceof LoaderError)) throw err;
|
|
5271
|
-
}
|
|
5307
|
+
await loader.load();
|
|
5272
5308
|
print(`Holocron sync — ${config.name}${dryRun ? " (dry-run)" : ""}`);
|
|
5273
5309
|
print(` config: ${input.loaded.filepath}`);
|
|
5274
5310
|
print("");
|
|
@@ -5674,7 +5710,7 @@ var security_default = "name: Security\n\non: # yamllint disable-line rule:truth
|
|
|
5674
5710
|
var stale_default = "name: Stale\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n days-before-stale:\n description: Days of inactivity before an issue or PR is marked stale (applies to both unless type-specific value is set)\n type: number\n required: false\n default: 30\n days-before-close:\n description: Days after stale label before closing (applies to both unless type-specific value is set)\n type: number\n required: false\n default: 5\n days-before-issue-stale:\n description: Days of inactivity before an issue is marked stale; overrides days-before-stale when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-issue-close:\n description: Days after stale label before closing an issue; overrides days-before-close when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-pr-stale:\n description: Days of inactivity before a PR is marked stale; overrides days-before-stale when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-pr-close:\n description: Days after stale label before closing a PR; overrides days-before-close when set to a non-negative value\n type: number\n required: false\n default: -1\n exempt-issue-labels:\n description: Comma-separated labels that exempt an issue from being marked stale\n type: string\n required: false\n default: \"in-progress,wip\"\n exempt-pr-labels:\n description: Comma-separated labels that exempt a PR from being marked stale\n type: string\n required: false\n default: \"\"\n exempt-all-issue-milestones:\n description: Issues assigned to any milestone are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-pr-milestones:\n description: PRs assigned to any milestone are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-issue-projects:\n description: Issues assigned to any project are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-pr-projects:\n description: PRs assigned to any project are never marked stale\n type: boolean\n required: false\n default: true\n\njobs:\n stale:\n name: Mark stale issues and pull requests\n permissions:\n contents: write\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0\n name: Run Stale\n with:\n close-issue-message: >\n This issue was closed because it has been stalled for\n ${{ inputs.days-before-close }} days with no activity.\n days-before-close: ${{ inputs.days-before-close }}\n days-before-issue-close: ${{ inputs.days-before-issue-close }}\n days-before-issue-stale: ${{ inputs.days-before-issue-stale }}\n days-before-pr-close: ${{ inputs.days-before-pr-close }}\n days-before-pr-stale: ${{ inputs.days-before-pr-stale }}\n days-before-stale: ${{ inputs.days-before-stale }}\n exempt-all-issue-milestones: ${{ inputs.exempt-all-issue-milestones }}\n exempt-all-issue-projects: ${{ inputs.exempt-all-issue-projects }}\n exempt-all-pr-milestones: ${{ inputs.exempt-all-pr-milestones }}\n exempt-all-pr-projects: ${{ inputs.exempt-all-pr-projects }}\n exempt-issue-labels: ${{ inputs.exempt-issue-labels }}\n exempt-pr-labels: ${{ inputs.exempt-pr-labels }}\n stale-issue-label: wontfix\n stale-issue-message: >\n This issue is stale because it has been open ${{ inputs.days-before-stale }}\n days with no activity. Remove the stale label or comment, or this will be\n closed in ${{ inputs.days-before-close }} days.\n stale-pr-label: wontfix\n stale-pr-message: >\n This PR is stale because it has been open ${{ inputs.days-before-stale }}\n days with no activity. Remove the stale label or comment, or this will be\n closed in ${{ inputs.days-before-close }} days.\n";
|
|
5675
5711
|
//#endregion
|
|
5676
5712
|
//#region src/templates/workflows/sync.yml
|
|
5677
|
-
var sync_default = "name: Sync\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n steps:\n description: >\n Sync steps to run (default: all). Valid values:\n labels, properties, teams, topics, keywords, description, homepage, readme, workflows, wiki.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme wiki\".\n type: string\n required: false\n secrets:\n HOLOCRON_ADMIN_TOKEN:\n description: Fine-grained PAT with admin scopes (labels, properties, teams).\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n required: false\n HOLOCRON_DEPLOY_TOKEN:\n description: Fine-grained PAT for GitHub Pages configuration.\n required: false\n HOLOCRON_ISSUES_TOKEN:\n description: Fine-grained PAT for issue management.\n required: false\n HOLOCRON_ORG_TOKEN:\n description: Org-scoped fine-grained PAT for team sync and org properties.\n required: false\n HOLOCRON_READ_TOKEN:\n description: Fine-grained PAT for read-only GitHub API calls.\n required: false\n HOLOCRON_SYNC_TOKEN:\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for gh CLI calls. Used when\n HOLOCRON_SYNC_TOKEN is not set.\n required: false\n\njobs:\n sync:\n name: Sync repo from config\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: write\n pull-requests: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Run holocron sync\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n
|
|
5713
|
+
var sync_default = "name: Sync\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n steps:\n description: >\n Sync steps to run (default: all). Valid values:\n labels, properties, teams, topics, keywords, description, homepage, readme, workflows, wiki.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme wiki\".\n type: string\n required: false\n secrets:\n HOLOCRON_ADMIN_TOKEN:\n description: Fine-grained PAT with admin scopes (labels, properties, teams).\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n required: false\n HOLOCRON_DEPLOY_TOKEN:\n description: Fine-grained PAT for GitHub Pages configuration.\n required: false\n HOLOCRON_ISSUES_TOKEN:\n description: Fine-grained PAT for issue management.\n required: false\n HOLOCRON_ORG_TOKEN:\n description: Org-scoped fine-grained PAT for team sync and org properties.\n required: false\n HOLOCRON_READ_TOKEN:\n description: Fine-grained PAT for read-only GitHub API calls.\n required: false\n HOLOCRON_SYNC_TOKEN:\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for gh CLI calls. Used when\n HOLOCRON_SYNC_TOKEN is not set.\n required: false\n\njobs:\n sync:\n name: Sync repo from config\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: write\n pull-requests: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Run holocron sync\n # Call the built entry directly — `pnpm exec holocron` relies on a\n # node_modules/.bin/holocron symlink that pnpm cannot create at install\n # time (dist/ does not exist yet), same as sync-github.yml.\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n node packages/cli/dist/cli.mjs sync --steps $STEPS\n else\n node packages/cli/dist/cli.mjs sync\n fi\n env:\n HOLOCRON_ADMIN_TOKEN: ${{ secrets.HOLOCRON_ADMIN_TOKEN }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET }}\n HOLOCRON_DEPLOY_TOKEN: ${{ secrets.HOLOCRON_DEPLOY_TOKEN }}\n HOLOCRON_ISSUES_TOKEN: ${{ secrets.HOLOCRON_ISSUES_TOKEN }}\n HOLOCRON_ORG_TOKEN: ${{ secrets.HOLOCRON_ORG_TOKEN }}\n HOLOCRON_READ_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n\n - name: Format generated files\n run: pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 2>/dev/null || true\n\n - uses: theholocron/.github/.github/actions/auto-commit@main\n id: auto-commit\n name: Commit sync changes\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n branch: chore/auto-sync\n commit-message: \"chore: sync from holocron.config\"\n commit-options: \"--no-verify\"\n\n - name: Open PR if changes were committed\n if: steps.auto-commit.outputs.changes-detected == 'true'\n run: |\n gh pr create \\\n --title \"chore: sync README and repo metadata\" \\\n --body \"Automated sync triggered by changes to config or package files. Merge to apply.\" \\\n --base main \\\n --head chore/auto-sync \\\n || echo \"PR already open — branch updated.\"\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - name: Broadcast wiki sync if navbar changed\n if: >-\n steps.auto-commit.outputs.changes-detected == 'true' &&\n github.event_name == 'push' &&\n (inputs.steps == '' || contains(inputs.steps, 'wiki'))\n run: |\n if git diff HEAD~1 --name-only | grep -q 'fern/docs.yml'; then\n gh workflow run sync-dispatch.yml \\\n --repo theholocron/.github \\\n --field \"steps=wiki\" \\\n || echo \"skipping broadcast — insufficient permissions\"\n fi\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n";
|
|
5678
5714
|
//#endregion
|
|
5679
5715
|
//#region src/templates/workflows/sync-dispatch.yml
|
|
5680
5716
|
var sync_dispatch_default = "name: Sync Dispatch\n\non: # yamllint disable-line rule:truthy\n workflow_dispatch:\n inputs:\n steps:\n description: >\n Sync steps to pass to each repo's sync.yml. Default is \"readme\"\n (only README marker blocks are updated).\n type: string\n required: false\n default: readme\n\npermissions:\n contents: read\n\njobs:\n broadcast:\n name: Broadcast sync to all repos\n runs-on: ubuntu-latest\n timeout-minutes: 15\n steps:\n - name: Dispatch sync to all repos with sync.yml\n run: |\n gh api /orgs/theholocron/repos --paginate --jq '.[].name' \\\n | while IFS= read -r repo; do\n gh api \"/repos/theholocron/$repo/contents/.github/workflows/sync.yml\" --silent 2>/dev/null || continue\n echo \"Dispatching sync to theholocron/$repo\"\n gh workflow run sync.yml \\\n --repo \"theholocron/$repo\" \\\n --field \"steps=$STEPS\" \\\n || echo \"Warning: could not dispatch to theholocron/$repo — skipping\"\n done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n";
|