@theholocron/astromech 4.10.3 → 4.12.0
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/index.d.mts +43 -11
- package/dist/index.mjs +164 -17
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { r as TasksConfig } from "./schema-BegK4MsY.mjs";
|
|
2
2
|
//#region src/run.d.ts
|
|
3
3
|
/**
|
|
4
|
-
* `holocron run <task> [-- <passthrough>]` — run a registry task
|
|
4
|
+
* `holocron run <task> [job] [-- <passthrough>]` — run a registry task (or one
|
|
5
|
+
* of its sub-jobs) locally.
|
|
6
|
+
*
|
|
7
|
+
* A `job` argument only applies to tasks that declare `jobs` (today: `audit`);
|
|
8
|
+
* for any other task it is folded back into the passthrough (`holocron run
|
|
9
|
+
* build src/`). `holocron run audit performance` runs one job; `holocron run
|
|
10
|
+
* audit` runs every job in declared order.
|
|
5
11
|
*
|
|
6
12
|
* Resolution:
|
|
7
13
|
*
|
|
8
|
-
* 0. task === "lint"
|
|
14
|
+
* 0. task === "lint" (no job) → the linter aggregate (see below)
|
|
15
|
+
* J. job given → TASKS[task].jobs[job].local (unknown job → exit 1)
|
|
9
16
|
* 1. turbo.json defines the task → `turbo run <task>`
|
|
10
17
|
* 2. package.json has a `<task>` script → `<pm> run <task>`
|
|
11
18
|
* (unless it's the `holocron run …` thin caller — that recurses)
|
|
12
19
|
* 3. TASKS[task].local resolves → `<tool> <args> <org-flags> <passthrough>`
|
|
13
|
-
* 4. TASKS[task].
|
|
14
|
-
*
|
|
15
|
-
*
|
|
20
|
+
* 4. TASKS[task].jobs has entries → run each job in declared order
|
|
21
|
+
* 5. TASKS[task].local === null → "enforced in CI" (skip, even with --required)
|
|
22
|
+
* 5b. known task, nothing resolved → "no <task> task" (exit 0, or 1 with --required)
|
|
23
|
+
* 6. unknown task → "unknown task" (exit 1)
|
|
16
24
|
*
|
|
17
25
|
* `lint` runs the resolved linter set (`config.tasks` `linters`, else
|
|
18
26
|
* auto-detected): the eslint slot goes through the standard turbo / script /
|
|
@@ -49,6 +57,11 @@ interface RunDeps {
|
|
|
49
57
|
interface RunTaskInput extends RunDeps {
|
|
50
58
|
/** Registry task name, e.g. `"test"`. */
|
|
51
59
|
task: string;
|
|
60
|
+
/**
|
|
61
|
+
* Sub-job within the task, e.g. `"performance"` in `holocron run audit
|
|
62
|
+
* performance`. Ignored (folded into `passthrough`) for tasks with no `jobs`.
|
|
63
|
+
*/
|
|
64
|
+
job?: string;
|
|
52
65
|
/** Directory to run in. */
|
|
53
66
|
cwd: string;
|
|
54
67
|
/** Args after `--` on the command line, forwarded to the tool / turbo / script. */
|
|
@@ -290,6 +303,12 @@ interface AstromechOptions {
|
|
|
290
303
|
lookPath?: (cwd: string, bin: string) => string | null;
|
|
291
304
|
}
|
|
292
305
|
interface RunOptions {
|
|
306
|
+
/**
|
|
307
|
+
* Sub-job within the task — `performance` in `holocron run audit
|
|
308
|
+
* performance`. Only meaningful for tasks that declare `jobs` (`audit`);
|
|
309
|
+
* ignored otherwise. Omit to run every job the task has.
|
|
310
|
+
*/
|
|
311
|
+
job?: string;
|
|
293
312
|
/** Args after `--`, forwarded to the tool / turbo / script. */
|
|
294
313
|
passthrough?: string[];
|
|
295
314
|
/** Print the resolved command without running it. */
|
|
@@ -300,7 +319,7 @@ interface RunOptions {
|
|
|
300
319
|
filter?: string;
|
|
301
320
|
}
|
|
302
321
|
interface Astromech {
|
|
303
|
-
/** Run one task locally. */
|
|
322
|
+
/** Run one task — or one of its sub-jobs (`opts.job`) — locally. */
|
|
304
323
|
run(task: string, opts?: RunOptions): RunTaskReport;
|
|
305
324
|
/**
|
|
306
325
|
* Run the merge-gating checks locally, in CI order — "will CI pass?".
|
|
@@ -439,6 +458,18 @@ interface LocalRunner {
|
|
|
439
458
|
/** The task is already a holocron subcommand (`sync`, `sync-wiki`). */
|
|
440
459
|
command?: string;
|
|
441
460
|
}
|
|
461
|
+
/** One sub-job of a task — `performance` in `holocron run audit performance`. */
|
|
462
|
+
interface JobDef {
|
|
463
|
+
/** How the job runs locally; `null` → no local equivalent (enforced in CI). */
|
|
464
|
+
local: LocalRunner | null;
|
|
465
|
+
/**
|
|
466
|
+
* The CI status-check context this job reports as (`audit / Knip`) — every
|
|
467
|
+
* sub-job is a CI job. `holocron run audit` and `holocron ci` label each job
|
|
468
|
+
* line with it; the task-level `… / Conclusion` context lives in
|
|
469
|
+
* `WORKFLOW_CHECK_CONTEXTS`.
|
|
470
|
+
*/
|
|
471
|
+
checkContext: string;
|
|
472
|
+
}
|
|
442
473
|
interface TaskDef {
|
|
443
474
|
/**
|
|
444
475
|
* `null` — the registry has no built-in runner (CodeQL, deploys, audit's
|
|
@@ -448,10 +479,11 @@ interface TaskDef {
|
|
|
448
479
|
* is `required` (a CI-only check isn't a local one).
|
|
449
480
|
*/
|
|
450
481
|
local: LocalRunner | null;
|
|
451
|
-
/**
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
482
|
+
/**
|
|
483
|
+
* Sub-jobs, keyed by slug — `holocron run audit performance`. Declared order
|
|
484
|
+
* is run order: `holocron run audit` (no job) runs each in turn.
|
|
485
|
+
*/
|
|
486
|
+
jobs?: Record<string, JobDef>;
|
|
455
487
|
/** Org-default flags injected by tool name. Removed by a repo override. */
|
|
456
488
|
flags?: Record<string, string[]>;
|
|
457
489
|
/**
|
|
@@ -503,4 +535,4 @@ declare const WORKFLOW_TEMPLATE_PROPERTIES: Record<string, string>;
|
|
|
503
535
|
*/
|
|
504
536
|
declare function reusableTemplates(): Map<string, string>;
|
|
505
537
|
//#endregion
|
|
506
|
-
export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, type SuperLinterConfig, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, baselineSuperLinterEnv, createAstromech, deriveDeployPaths, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, normalizeWorkflowWith, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
|
|
538
|
+
export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, type SuperLinterConfig, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, baselineSuperLinterEnv, createAstromech, deriveDeployPaths, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, normalizeWorkflowWith, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -45,7 +45,31 @@ const TASKS = {
|
|
|
45
45
|
] } },
|
|
46
46
|
sync: { local: { command: "sync" } },
|
|
47
47
|
wiki: { local: { command: "sync-wiki" } },
|
|
48
|
-
audit: {
|
|
48
|
+
audit: {
|
|
49
|
+
local: null,
|
|
50
|
+
jobs: {
|
|
51
|
+
"bundle-size": {
|
|
52
|
+
local: null,
|
|
53
|
+
checkContext: "audit / Audit the bundle size"
|
|
54
|
+
},
|
|
55
|
+
knip: {
|
|
56
|
+
local: { tool: "knip" },
|
|
57
|
+
checkContext: "audit / Knip"
|
|
58
|
+
},
|
|
59
|
+
performance: {
|
|
60
|
+
local: { detect: [{
|
|
61
|
+
when: /^lighthouse\.config\.(cjs|js|mjs|ts)$/,
|
|
62
|
+
tool: "lhci",
|
|
63
|
+
args: ["autorun"]
|
|
64
|
+
}, {
|
|
65
|
+
when: /^lighthouserc\.(cjs|js|mjs|json|yml|yaml)$/,
|
|
66
|
+
tool: "lhci",
|
|
67
|
+
args: ["autorun"]
|
|
68
|
+
}] },
|
|
69
|
+
checkContext: "audit / Audit the performance"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
49
73
|
codeql: { local: null },
|
|
50
74
|
deploy: { local: null }
|
|
51
75
|
};
|
|
@@ -197,18 +221,26 @@ function resolveLinters(opts) {
|
|
|
197
221
|
//#endregion
|
|
198
222
|
//#region src/run.ts
|
|
199
223
|
/**
|
|
200
|
-
* `holocron run <task> [-- <passthrough>]` — run a registry task
|
|
224
|
+
* `holocron run <task> [job] [-- <passthrough>]` — run a registry task (or one
|
|
225
|
+
* of its sub-jobs) locally.
|
|
226
|
+
*
|
|
227
|
+
* A `job` argument only applies to tasks that declare `jobs` (today: `audit`);
|
|
228
|
+
* for any other task it is folded back into the passthrough (`holocron run
|
|
229
|
+
* build src/`). `holocron run audit performance` runs one job; `holocron run
|
|
230
|
+
* audit` runs every job in declared order.
|
|
201
231
|
*
|
|
202
232
|
* Resolution:
|
|
203
233
|
*
|
|
204
|
-
* 0. task === "lint"
|
|
234
|
+
* 0. task === "lint" (no job) → the linter aggregate (see below)
|
|
235
|
+
* J. job given → TASKS[task].jobs[job].local (unknown job → exit 1)
|
|
205
236
|
* 1. turbo.json defines the task → `turbo run <task>`
|
|
206
237
|
* 2. package.json has a `<task>` script → `<pm> run <task>`
|
|
207
238
|
* (unless it's the `holocron run …` thin caller — that recurses)
|
|
208
239
|
* 3. TASKS[task].local resolves → `<tool> <args> <org-flags> <passthrough>`
|
|
209
|
-
* 4. TASKS[task].
|
|
210
|
-
*
|
|
211
|
-
*
|
|
240
|
+
* 4. TASKS[task].jobs has entries → run each job in declared order
|
|
241
|
+
* 5. TASKS[task].local === null → "enforced in CI" (skip, even with --required)
|
|
242
|
+
* 5b. known task, nothing resolved → "no <task> task" (exit 0, or 1 with --required)
|
|
243
|
+
* 6. unknown task → "unknown task" (exit 1)
|
|
212
244
|
*
|
|
213
245
|
* `lint` runs the resolved linter set (`config.tasks` `linters`, else
|
|
214
246
|
* auto-detected): the eslint slot goes through the standard turbo / script /
|
|
@@ -252,19 +284,47 @@ function makeRunOne(input) {
|
|
|
252
284
|
}
|
|
253
285
|
function runTask(input) {
|
|
254
286
|
const { print, logger, readFile, fileExists, listDir, task, cwd } = input;
|
|
255
|
-
const
|
|
287
|
+
const def = TASKS[task];
|
|
288
|
+
let job = input.job;
|
|
289
|
+
let passthrough = input.passthrough ?? [];
|
|
290
|
+
if (job !== void 0 && !def?.jobs) {
|
|
291
|
+
passthrough = [job, ...passthrough];
|
|
292
|
+
job = void 0;
|
|
293
|
+
}
|
|
294
|
+
if (job !== void 0) {
|
|
295
|
+
const jobDef = def.jobs[job];
|
|
296
|
+
if (!jobDef) {
|
|
297
|
+
const known = Object.keys(def.jobs).join(", ");
|
|
298
|
+
print(`✗ unknown job "${job}" for "${task}" (known: ${known})`);
|
|
299
|
+
logger.warn({
|
|
300
|
+
task,
|
|
301
|
+
job,
|
|
302
|
+
status: "unknown"
|
|
303
|
+
}, `run: ${task} ${job}`);
|
|
304
|
+
return {
|
|
305
|
+
status: "unknown",
|
|
306
|
+
message: `unknown job "${task} ${job}"`
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const label = `${task} ${job}`;
|
|
310
|
+
return runUnit(input, label, jobDef.local, makeRunOne({
|
|
311
|
+
...input,
|
|
312
|
+
task: label
|
|
313
|
+
}), passthrough);
|
|
314
|
+
}
|
|
256
315
|
const run = makeRunOne(input);
|
|
257
|
-
if (task === "lint") return runLintAggregate(input);
|
|
316
|
+
if (task === "lint") return runLintAggregate(input, passthrough);
|
|
258
317
|
if (turboDefinesTask(cwd, task, readFile, fileExists)) {
|
|
318
|
+
const filterArg = input.filter ? [`--filter=${input.filter}`] : [];
|
|
319
|
+
const forwarded = [...def?.local && !def.local.command && def.local.tool ? def.flags?.[def.local.tool] ?? [] : [], ...passthrough];
|
|
259
320
|
const args = [
|
|
260
321
|
"run",
|
|
261
322
|
task,
|
|
262
|
-
...
|
|
263
|
-
...
|
|
323
|
+
...filterArg,
|
|
324
|
+
...forwarded.length ? ["--", ...forwarded] : []
|
|
264
325
|
];
|
|
265
326
|
return run(resolveBin(cwd, "turbo", fileExists), args);
|
|
266
327
|
}
|
|
267
|
-
const def = TASKS[task];
|
|
268
328
|
const script = packageJsonScript(cwd, task, readFile, fileExists);
|
|
269
329
|
if (script && !/^holocron run\b/.test(script.trim())) return run(packageManager(cwd, readFile, fileExists), [
|
|
270
330
|
"run",
|
|
@@ -287,6 +347,7 @@ function runTask(input) {
|
|
|
287
347
|
]);
|
|
288
348
|
}
|
|
289
349
|
}
|
|
350
|
+
if (def?.jobs && Object.keys(def.jobs).length > 0) return runAllJobs(input, def.jobs, passthrough);
|
|
290
351
|
if (def?.local === null) {
|
|
291
352
|
const msg = `no local equivalent for ${task} — enforced in CI`;
|
|
292
353
|
print(`· ${msg}`);
|
|
@@ -322,13 +383,94 @@ function runTask(input) {
|
|
|
322
383
|
};
|
|
323
384
|
}
|
|
324
385
|
/**
|
|
386
|
+
* Run one registry unit — a task's own runner or a single sub-job. Resolution
|
|
387
|
+
* is registry-only (`turbo` / `package.json` scripts are keyed by task, not
|
|
388
|
+
* `task/job`). `label` names the unit in messages (`audit performance`).
|
|
389
|
+
*
|
|
390
|
+
* `local: null` → CI-only by design, always a skip (never a failure, even with
|
|
391
|
+
* `--required`). A declared runner with no matching repo file, or whose tool
|
|
392
|
+
* isn't installed locally → a skip (`--required` turns it into a failure — the
|
|
393
|
+
* repo claims a check it can't back). Mirrors the lint aggregate: run what's
|
|
394
|
+
* here, flag the rest, let CI enforce.
|
|
395
|
+
*/
|
|
396
|
+
function runUnit(input, label, local, run, passthrough) {
|
|
397
|
+
const { print, logger, listDir, lookPath, cwd } = input;
|
|
398
|
+
const skip = (msg) => {
|
|
399
|
+
print(input.required ? `✗ ${msg} (required)` : `· ${msg}`);
|
|
400
|
+
logger[input.required ? "warn" : "debug"]({
|
|
401
|
+
task: label,
|
|
402
|
+
status: input.required ? "fail" : "skip"
|
|
403
|
+
}, `run: ${label}`);
|
|
404
|
+
return {
|
|
405
|
+
status: input.required ? "fail" : "skip",
|
|
406
|
+
message: msg
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
if (local) {
|
|
410
|
+
const runner = resolveRunner(local, cwd, listDir);
|
|
411
|
+
if (!runner) return skip(`no ${label} runner for this repo`);
|
|
412
|
+
const found = lookPath(cwd, runner.tool);
|
|
413
|
+
if (!found) return skip(`${runner.tool} not installed locally for ${label} — enforced in CI`);
|
|
414
|
+
return run(found, [...runner.args, ...passthrough]);
|
|
415
|
+
}
|
|
416
|
+
const msg = `no local equivalent for ${label} — enforced in CI`;
|
|
417
|
+
print(`· ${msg}`);
|
|
418
|
+
logger.debug({
|
|
419
|
+
task: label,
|
|
420
|
+
status: "skip"
|
|
421
|
+
}, `run: ${label}`);
|
|
422
|
+
return {
|
|
423
|
+
status: "skip",
|
|
424
|
+
message: msg
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* `holocron run <task>` for a task that is a container of sub-jobs (`audit`) —
|
|
429
|
+
* run each job in declared order. Status precedence: any `fail` → `fail`; else
|
|
430
|
+
* any `ok` → `ok`; else any `dry-run` → `dry-run`; else `skip` (nothing ran).
|
|
431
|
+
*/
|
|
432
|
+
function runAllJobs(input, jobs, passthrough) {
|
|
433
|
+
const { print, task } = input;
|
|
434
|
+
const perJob = {
|
|
435
|
+
...input,
|
|
436
|
+
required: false
|
|
437
|
+
};
|
|
438
|
+
const reports = [];
|
|
439
|
+
for (const [name, jobDef] of Object.entries(jobs)) {
|
|
440
|
+
const label = `${task} ${name}`;
|
|
441
|
+
print(`▶ ${jobDef.checkContext}`);
|
|
442
|
+
reports.push(runUnit(perJob, label, jobDef.local, makeRunOne({
|
|
443
|
+
...perJob,
|
|
444
|
+
task: label
|
|
445
|
+
}), passthrough));
|
|
446
|
+
}
|
|
447
|
+
const command = reports.map((r) => r.command).filter((c) => Boolean(c)).join(" && ");
|
|
448
|
+
const statuses = new Set(reports.map((r) => r.status));
|
|
449
|
+
if (statuses.has("fail")) return {
|
|
450
|
+
status: "fail",
|
|
451
|
+
command,
|
|
452
|
+
message: `one or more ${task} jobs failed`
|
|
453
|
+
};
|
|
454
|
+
if (statuses.has("ok")) return {
|
|
455
|
+
status: "ok",
|
|
456
|
+
command
|
|
457
|
+
};
|
|
458
|
+
if (statuses.has("dry-run")) return {
|
|
459
|
+
status: "dry-run",
|
|
460
|
+
command
|
|
461
|
+
};
|
|
462
|
+
return {
|
|
463
|
+
status: "skip",
|
|
464
|
+
message: `no ${task} job has a local equivalent — enforced in CI`
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
325
468
|
* `holocron run lint` — the resolved linter set, run natively. The eslint
|
|
326
469
|
* slot reuses turbo / the `lint` script / `eslint .`; the rest run their
|
|
327
470
|
* `localBin` when it resolves on PATH. Worst exit code wins.
|
|
328
471
|
*/
|
|
329
|
-
function runLintAggregate(input) {
|
|
472
|
+
function runLintAggregate(input, passthrough) {
|
|
330
473
|
const { print, logger, readFile, fileExists, listDir, lookPath, cwd } = input;
|
|
331
|
-
const passthrough = input.passthrough ?? [];
|
|
332
474
|
const dryRun = input.dryRun ?? false;
|
|
333
475
|
const runOne = makeRunOne(input);
|
|
334
476
|
const pass = passthrough.length ? ["--", ...passthrough] : [];
|
|
@@ -820,6 +962,9 @@ function requiredChecks(config) {
|
|
|
820
962
|
//#region src/templates/reusable/actions/auto-commit.yml
|
|
821
963
|
var auto_commit_default = "name: Auto Commit\ndescription: >\n Commit and push any file changes using stefanzweifel/git-auto-commit-action.\n If a branch name is provided and differs from the current branch, a new branch\n is created automatically.\n\ninputs:\n token:\n description: >\n GitHub token with push access to the target branch. Used to authenticate\n the git remote so the commit can be pushed.\n required: true\n branch:\n description: >\n Branch to commit to. Leave empty to commit to the current branch (useful\n in PR contexts). Provide an explicit name (e.g. chore/auto-sync) to push\n to a separate branch.\n required: false\n default: \"\"\n commit-message:\n description: Commit message.\n required: true\n commit-options:\n description: >\n Additional options passed to git commit (e.g. --no-verify).\n required: false\n default: \"\"\n commit-user-name:\n description: Git user.name for the commit.\n required: false\n default: \"github-actions[bot]\"\n commit-user-email:\n description: Git user.email for the commit.\n required: false\n default: \"41898282+github-actions[bot]@users.noreply.github.com\"\n commit-author:\n description: >\n Git author string in \"Name <email>\" format.\n required: false\n default: \"github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>\"\n\noutputs:\n changes-detected:\n description: \"'true' if any files were committed, 'false' otherwise.\"\n value: ${{ steps.commit.outputs.changes_detected }}\n\nruns:\n using: composite\n steps:\n - name: Configure git remote with token\n shell: bash\n run: |\n REMOTE_URL=$(git remote get-url origin)\n AUTHED_URL=$(echo \"$REMOTE_URL\" | sed \"s|https://|https://x-access-token:${TOKEN}@|\")\n git remote set-url origin \"$AUTHED_URL\"\n env:\n TOKEN: ${{ inputs.token }}\n\n - name: Reset branch to current HEAD\n if: ${{ inputs.branch != '' }}\n shell: bash\n run: git checkout -B \"$BRANCH\"\n env:\n BRANCH: ${{ inputs.branch }}\n\n - name: Build signed commit message\n id: msg\n shell: bash\n run: |\n TRAILER=\"Signed-off-by: ${COMMIT_USER_NAME} <${COMMIT_USER_EMAIL}>\"\n if echo \"$COMMIT_MESSAGE\" | grep -qF \"$TRAILER\"; then\n FINAL=\"$COMMIT_MESSAGE\"\n else\n FINAL=\"${COMMIT_MESSAGE}\"$'\\n\\n'\"${TRAILER}\"\n fi\n {\n echo \"value<<EOF_TRAILER\"\n printf '%s' \"$FINAL\"\n echo \"\"\n echo \"EOF_TRAILER\"\n } >> \"$GITHUB_OUTPUT\"\n env:\n COMMIT_MESSAGE: ${{ inputs.commit-message }}\n COMMIT_USER_NAME: ${{ inputs.commit-user-name }}\n COMMIT_USER_EMAIL: ${{ inputs.commit-user-email }}\n\n - uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0\n id: commit\n with:\n branch: ${{ inputs.branch }}\n commit_message: ${{ steps.msg.outputs.value }}\n commit_options: ${{ inputs.commit-options }}\n commit_user_name: ${{ inputs.commit-user-name }}\n commit_user_email: ${{ inputs.commit-user-email }}\n commit_author: ${{ inputs.commit-author }}\n";
|
|
822
964
|
//#endregion
|
|
965
|
+
//#region src/templates/reusable/actions/holocron.yml
|
|
966
|
+
var holocron_default = "name: Holocron\ndescription: >\n Run a Holocron CLI task (`holocron run <task> [job]`). Builds\n @theholocron/cli from source first when it resolves to an unbuilt\n workspace checkout (the holocron repo itself); a no-op everywhere else.\n Requires dependencies to be installed already (run the setup action first).\n\ninputs:\n task:\n description: Manifest task to run (typecheck, test, build, audit).\n required: true\n job:\n description: Optional sub-job within the task (e.g. knip, performance for audit).\n required: false\n default: \"\"\n args:\n description: Extra arguments forwarded to the tool after `--`.\n required: false\n default: \"\"\n\nruns:\n using: composite\n\n steps:\n - name: Build the workspace when the CLI resolves to an unbuilt checkout\n shell: bash\n # The holocron repo carries @theholocron/cli as `workspace:*`; its bin\n # (dist/cli.mjs) does not exist on a fresh checkout. `pnpm build` is a\n # turbo run — cached and cheap once warm. A no-op everywhere else, where\n # the published tarball already ships dist/.\n run: |\n if [ ! -f node_modules/@theholocron/cli/dist/cli.mjs ]; then\n echo \"→ @theholocron/cli dist/ missing — building the workspace from source\" >&2\n pnpm build\n fi\n\n - name: holocron run ${{ inputs.task }}\n shell: bash\n env:\n HOLOCRON_TASK: ${{ inputs.task }}\n HOLOCRON_JOB: ${{ inputs.job }}\n HOLOCRON_ARGS: ${{ inputs.args }}\n run: |\n set -- run \"$HOLOCRON_TASK\"\n [ -n \"$HOLOCRON_JOB\" ] && set -- \"$@\" \"$HOLOCRON_JOB\"\n # shellcheck disable=SC2086\n [ -n \"$HOLOCRON_ARGS\" ] && set -- \"$@\" -- $HOLOCRON_ARGS\n pnpm exec holocron \"$@\"\n";
|
|
967
|
+
//#endregion
|
|
823
968
|
//#region src/templates/reusable/actions/install.yml
|
|
824
969
|
var install_default = "name: Install dependencies\ndescription: Install project dependencies with pnpm frozen lockfile.\n\nruns:\n using: composite\n\n steps:\n - name: Install dependencies\n if: ${{ hashFiles('pnpm-lock.yaml') != '' }}\n shell: bash\n run: pnpm install --frozen-lockfile\n";
|
|
825
970
|
//#endregion
|
|
@@ -845,7 +990,7 @@ var setup_node_default = "name: Setup Node\ndescription: Install pnpm and Node.j
|
|
|
845
990
|
*/
|
|
846
991
|
/** `workflow_call` implementations → `.github/workflows/<name>.yml`. */
|
|
847
992
|
const REUSABLE_WORKFLOWS = {
|
|
848
|
-
audit: "name: Audit\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n
|
|
993
|
+
audit: "name: Audit\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n run-knip:\n description: Run Knip to detect unused files, exports, and dependencies\n type: boolean\n required: false\n default: false\n run-performance:\n description: Run Lighthouse CI performance audit (requires a lighthouse config file)\n type: boolean\n required: false\n default: false\n lighthouse-config:\n description: >\n Path to the Lighthouse CI config file, forwarded to `holocron run\n audit performance -- --config`. Defaults to lighthouse.config.cjs\n (the org standard).\n type: string\n required: false\n default: lighthouse.config.cjs\n secrets:\n CODECOV_TOKEN:\n required: false\n LHCI_GITHUB_APP_TOKEN:\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n bundle-size:\n name: Audit the bundle size\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n # Read by the bundle-stats uploader inside `holocron run build`.\n CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - uses: theholocron/.github/.github/actions/holocron@main\n name: Build and upload bundle stats\n with:\n task: build\n\n knip:\n name: Knip\n if: ${{ inputs.run-knip }}\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 10\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n persist-credentials: false\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - uses: theholocron/.github/.github/actions/holocron@main\n name: Run Knip\n with:\n task: audit\n job: knip\n\n performance:\n name: Audit the performance\n if: ${{ inputs.run-performance }}\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n # Read by `lhci autorun` inside `holocron run audit performance`.\n LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: npm install -g @lhci/cli@0.14.x\n name: Install Lighthouse CLI\n\n - uses: theholocron/.github/.github/actions/holocron@main\n name: Run Lighthouse CI\n with:\n task: audit\n job: performance\n args: --config=${{ inputs.lighthouse-config }}\n\n\n conclusion:\n name: Conclusion\n runs-on: ubuntu-latest\n if: always()\n needs: [bundle-size, knip, performance]\n steps:\n - name: Check job statuses\n run: |\n if [[ \"$RESULTS\" == *\"failure\"* ]] || [[ \"$RESULTS\" == *\"cancelled\"* ]]; then\n exit 1\n fi\n env:\n RESULTS: ${{ join(needs.*.result, ',') }}\n",
|
|
849
994
|
bookkeeping: "name: Bookkeeping\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n configuration-path:\n description: Path to the labeler configuration file in the calling repo\n type: string\n required: false\n default: .github/labeler.yml\n\njobs:\n label:\n name: Apply Labels\n permissions:\n contents: read\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 5\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n with:\n sparse-checkout: ${{ inputs.configuration-path || '.github/labeler.yml' }}\n sparse-checkout-cone-mode: false\n\n - uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4\n if: ${{ github.event_name == 'pull_request' && hashFiles(inputs.configuration-path || '.github/labeler.yml') != '' }}\n # v3.4 bundles Node 20; allow it to run under Actions' current default.\n env:\n ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true\n with:\n # Fall back to default path when triggered directly (not via workflow_call)\n # because inputs.* defaults only apply on workflow_call events.\n configuration-path: ${{ inputs.configuration-path || '.github/labeler.yml' }}\n include-title: 1\n include-body: 0\n sync-labels: 1\n enable-versioned-regex: 0\n repo-token: ${{ github.token }}\n",
|
|
850
995
|
dependencies: "name: Dependencies\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n secrets:\n merge-token:\n description: >\n Optional privileged token for auto-merge. Falls back to GITHUB_TOKEN.\n Required when branch protection enforces required reviews — GITHUB_TOKEN\n cannot approve its own PRs.\n required: false\n\njobs:\n dependabot:\n name: Update the dependencies\n permissions:\n contents: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 5\n if: github.event.pull_request.user.login == 'dependabot[bot]'\n steps:\n - uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0\n name: Fetch Dependabot metadata\n id: metadata\n\n - run: gh pr merge --auto --squash \"$PR_URL\"\n # --squash is intentional: repo protection sets allow_merge_commit: false,\n # so --merge would fail on any repo using the standard preset.\n name: Enable auto-merge for Dependabot PRs\n if: steps.metadata.outputs.update-type == 'version-update:semver-patch'\n env:\n PR_URL: ${{ github.event.pull_request.html_url }}\n GH_TOKEN: ${{ secrets.merge-token || github.token }}\n",
|
|
851
996
|
deploy: "name: Deploy\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n type:\n description: \"Type of deployment: docs or storybook\"\n required: true\n type: string\n storybook-projects:\n description: >\n JSON array of { \"name\"?, \"workingDir\", \"outputDir\"? } objects for storybook deploys.\n Each is built via `pnpm -C <workingDir> build:storybook`. If \"name\" is provided the\n output is placed under `sandbox/<name>/`; omit \"name\" for single-repo deploys and the\n output lands directly in `sandbox/`.\n type: string\n required: false\n default: \"[]\"\n build-script:\n description: pnpm script that builds the Storybook static output (single storybook, type:storybook only)\n type: string\n required: false\n default: build:storybook\n output-dir:\n description: Directory where Storybook writes its static output (single storybook, type:storybook only)\n type: string\n required: false\n default: storybook-static\n\njobs:\n deploy:\n name: Deploy\n runs-on: ubuntu-latest\n permissions:\n contents: read\n pages: write\n id-token: write\n environment:\n name: github-pages\n url: ${{ steps.deployment.outputs.page_url }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - name: Build docs site\n if: ${{ inputs.type == 'docs' }}\n run: pnpm -C docs build\n\n - name: Build Storybook projects\n if: ${{ inputs.storybook-projects != '[]' }}\n env:\n PROJECTS: ${{ inputs.storybook-projects }}\n run: |\n echo \"$PROJECTS\" | jq -c '.[]' | while IFS= read -r project; do\n workingDir=$(echo \"$project\" | jq -r '.workingDir')\n pnpm -C \"$workingDir\" build:storybook\n done\n\n - name: Build Storybook\n if: ${{ inputs.type == 'storybook' && inputs.storybook-projects == '[]' }}\n env:\n BUILD_SCRIPT: ${{ inputs.build-script }}\n run: pnpm run \"$BUILD_SCRIPT\"\n\n - name: Assemble site\n env:\n DEPLOY_TYPE: ${{ inputs.type }}\n PROJECTS: ${{ inputs.storybook-projects }}\n STORYBOOK_OUTPUT_DIR: ${{ inputs.output-dir }}\n run: |\n mkdir -p _site\n if [ \"$DEPLOY_TYPE\" = \"docs\" ]; then\n cp -r docs/dist/. _site/\n fi\n if [ \"$PROJECTS\" != \"[]\" ]; then\n echo \"$PROJECTS\" | jq -c '.[]' | while IFS= read -r project; do\n name=$(echo \"$project\" | jq -r '.name // \"\"')\n workingDir=$(echo \"$project\" | jq -r '.workingDir')\n outputDir=$(echo \"$project\" | jq -r '.outputDir // \"storybook-static\"')\n if [ -n \"$name\" ]; then\n target=\"_site/sandbox/${name}\"\n else\n target=\"_site/sandbox\"\n fi\n mkdir -p \"$target\"\n cp -r \"${workingDir}/${outputDir}/.\" \"$target/\"\n done\n elif [ \"$DEPLOY_TYPE\" = \"storybook\" ]; then\n mkdir -p _site/sandbox\n cp -r \"${STORYBOOK_OUTPUT_DIR}/.\" _site/sandbox/\n fi\n\n - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1\n name: Upload pages artifact\n with:\n path: _site\n\n - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5\n id: deployment\n name: Deploy to GitHub Pages\n",
|
|
@@ -860,13 +1005,14 @@ const REUSABLE_WORKFLOWS = {
|
|
|
860
1005
|
tag: "name: Tag\n\n# Release Please — fully automated tag and GitHub Release from Conventional Commits.\n# No package.json or npm publishing required. Operates in \"simple\" mode by default:\n# analyzes commits since the last tag, maintains a rolling Release PR, and creates\n# a tag + GitHub Release when that PR is merged.\n#\n# The calling repo must have two files at the root:\n# release-please-config.json — declares packages and release-type\n# .release-please-manifest.json — tracks the current version\n\non: # yamllint disable-line rule:truthy\n # Self-trigger: when this workflow lives in theholocron/.github itself,\n # push to main runs Release Please for that repo's own releases.\n push:\n branches:\n - main\n workflow_call:\n inputs:\n release-type:\n description: Release Please release type (simple, node, python, etc.)\n type: string\n required: false\n default: simple\n config-file:\n description: Path to release-please-config.json\n type: string\n required: false\n default: release-please-config.json\n manifest-file:\n description: Path to .release-please-manifest.json\n type: string\n required: false\n default: .release-please-manifest.json\n\njobs:\n tag:\n name: Tag release\n permissions:\n contents: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: google-github-actions/release-please-action@e4dc86ba9405554aeba3c6bb2d169500e7d3b4ee # v4.1.1\n name: Run Release Please\n with:\n release-type: ${{ inputs.release-type }}\n config-file: ${{ inputs.config-file }}\n manifest-file: ${{ inputs.manifest-file }}\n",
|
|
861
1006
|
"sync-github": "name: Sync workflow templates\n\n# Builds the holocron CLI from source and pushes updated workflow templates\n# and composite actions to downstream .github repos. Runs whenever the\n# template source files change on main or alpha.\n#\n# Secrets required:\n# HOLOCRON_SYNC_TOKEN — fine-grained PAT (resource owner: org) with:\n# Actions: Read and write (dispatch workflow runs via gh workflow run)\n# Contents: Read and write (git trees, blobs, refs)\n# Pull requests: Read and write (open sync PR)\n# Workflows: Read and write (write .github/workflows/*.yml)\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n primary-repo:\n description: >\n Primary .github repo — receives composite actions, reusable workflows,\n and thin-caller templates. Requires a PR (branch protection assumed).\n type: string\n required: false\n default: theholocron/.github\n secondary-repos:\n description: >\n Space-separated list of secondary repos (reusable workflows + thin\n callers only, no composite actions). Changes are delivered via pull\n request, same as the primary repo.\n type: string\n required: false\n default: \"\"\n sync-branch:\n description: Branch name used for the primary and secondary repo PRs\n type: string\n required: false\n default: chore/sync-templates\n secrets:\n HOLOCRON_SYNC_TOKEN:\n required: true\n HOLOCRON_READ_TOKEN:\n description: >\n Fine-grained PAT for read-only GitHub API calls (e.g. resolving git\n committer identity via `gh api user`). Falls back to HOLOCRON_SYNC_TOKEN.\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for `gh` CLI calls. Used when neither\n HOLOCRON_READ_TOKEN nor HOLOCRON_SYNC_TOKEN is set.\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 Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n sync:\n name: Sync templates\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n POSTHOG_PROJECT_TOKEN: ${{ vars.POSTHOG_PROJECT_TOKEN }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Cache actionlint\n id: cache-actionlint\n uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0\n with:\n path: /tmp/actionlint\n key: actionlint-v1.7.7-linux-amd64\n\n - name: Download actionlint\n if: steps.cache-actionlint.outputs.cache-hit != 'true'\n run: |\n curl -fsSL https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz \\\n | tar -xz -C /tmp actionlint\n\n - name: Validate generated workflows\n run: |\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --output-dir /tmp/sync-validate\n /tmp/actionlint /tmp/sync-validate/.github/workflows/*.yml\n env:\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n\n - name: Sync primary repo (PR)\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$PRIMARY_REPO\" \"$SYNC_BRANCH\" 2>/dev/null || true\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n\n - name: Sync secondary repos (PR)\n if: ${{ inputs.secondary-repos != '' }}\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n for repo in $SECONDARY_REPOS; do\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$repo\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$repo\" \"$SYNC_BRANCH\" 2>/dev/null || true\n done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n SECONDARY_REPOS: ${{ inputs.secondary-repos }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n",
|
|
862
1007
|
sync: "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 Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\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 || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n POSTHOG_PROJECT_TOKEN: ${{ vars.POSTHOG_PROJECT_TOKEN }}\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: |\n pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 2>/dev/null || true\n # `holocron sync` writes package.json fields (keywords, description,\n # homepage) with a plain assignment, which appends new keys at the\n # end — re-apply the canonical order the lint config enforces.\n pnpm exec eslint --fix --no-warn-ignored package.json 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",
|
|
863
|
-
test: "name: Test\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n run-unit:\n description: Run unit tests with coverage (disable for UI-only repos that use Storybook testing exclusively)\n type: boolean\n required: false\n default: true\n run-storybook:\n description: Run Storybook vitest interaction tests (requires .storybook/ setup)\n type: boolean\n required: false\n default: false\n run-interaction:\n description: Run Storybook interaction and accessibility tests with Playwright\n type: boolean\n required: false\n default: false\n run-chromatic:\n description: Publish Storybook to Chromatic for visual regression testing\n type: boolean\n required: false\n default: false\n chromatic-projects:\n description: >\n JSON array of Chromatic projects to build, one matrix job per entry.\n Each entry: { \"tokenName\": \"WEB\", \"workingDir\": \"apps/web\", \"buildScript\": \"build:storybook\" }.\n tokenName maps to secret CHROMATIC_PROJECT_TOKEN_<TOKENNAME>; use \"default\"\n (or omit for the legacy empty-string form) to use the bare CHROMATIC_PROJECT_TOKEN\n secret for single-project repos.\n buildScript defaults to \"build:storybook\" when omitted.\n Default runs a single job from the repo root using CHROMATIC_PROJECT_TOKEN.\n type: string\n required: false\n default: '[{\"tokenName\":\"default\",\"workingDir\":\".\",\"buildScript\":\"build:storybook\"}]'\n run-user-flow:\n description: Run Cypress E2E user-flow tests (requires cypress.config.*)\n type: boolean\n required: false\n default: false\n wait-on-url:\n description: URL to wait for before running Cypress tests (default is Vite dev server; override for non-Vite stacks e.g. http://localhost:3000 for Next.js)\n type: string\n required: false\n default: \"http://localhost:5173\"\n secrets:\n CHROMATIC_PROJECT_TOKEN:\n required: false\n CYPRESS_RECORD_KEY:\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n unit:\n name: Run tests and collect coverage\n if: ${{ inputs.run-unit }}\n permissions:\n contents: read\n id-token: write\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n -
|
|
864
|
-
typecheck: "name: Typecheck\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n secrets:\n TURBO_TOKEN:\n required: false\n\njobs:\n typecheck:\n name: tsc --noEmit\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 10\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n -
|
|
1008
|
+
test: "name: Test\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n run-unit:\n description: Run unit tests with coverage (disable for UI-only repos that use Storybook testing exclusively)\n type: boolean\n required: false\n default: true\n run-storybook:\n description: Run Storybook vitest interaction tests (requires .storybook/ setup)\n type: boolean\n required: false\n default: false\n run-interaction:\n description: Run Storybook interaction and accessibility tests with Playwright\n type: boolean\n required: false\n default: false\n run-chromatic:\n description: Publish Storybook to Chromatic for visual regression testing\n type: boolean\n required: false\n default: false\n chromatic-projects:\n description: >\n JSON array of Chromatic projects to build, one matrix job per entry.\n Each entry: { \"tokenName\": \"WEB\", \"workingDir\": \"apps/web\", \"buildScript\": \"build:storybook\" }.\n tokenName maps to secret CHROMATIC_PROJECT_TOKEN_<TOKENNAME>; use \"default\"\n (or omit for the legacy empty-string form) to use the bare CHROMATIC_PROJECT_TOKEN\n secret for single-project repos.\n buildScript defaults to \"build:storybook\" when omitted.\n Default runs a single job from the repo root using CHROMATIC_PROJECT_TOKEN.\n type: string\n required: false\n default: '[{\"tokenName\":\"default\",\"workingDir\":\".\",\"buildScript\":\"build:storybook\"}]'\n run-user-flow:\n description: Run Cypress E2E user-flow tests (requires cypress.config.*)\n type: boolean\n required: false\n default: false\n wait-on-url:\n description: URL to wait for before running Cypress tests (default is Vite dev server; override for non-Vite stacks e.g. http://localhost:3000 for Next.js)\n type: string\n required: false\n default: \"http://localhost:5173\"\n secrets:\n CHROMATIC_PROJECT_TOKEN:\n required: false\n CYPRESS_RECORD_KEY:\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n unit:\n name: Run tests and collect coverage\n if: ${{ inputs.run-unit }}\n permissions:\n contents: read\n id-token: write\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - uses: theholocron/.github/.github/actions/holocron@main\n name: Run tests with coverage\n with:\n task: test\n\n - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0\n name: Upload coverage to Codecov\n with:\n use_oidc: true\n\n - uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1\n name: Upload test results to Codecov\n if: ${{ !cancelled() }}\n with:\n use_oidc: true\n files: '**/test-report.junit.xml'\n\n storybook:\n name: Run Storybook interaction tests\n if: ${{ inputs.run-storybook }}\n permissions:\n contents: read\n id-token: write\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm exec playwright install chromium --with-deps\n name: Install Playwright\n\n - run: pnpm test:storybook\n name: Run Storybook tests\n\n - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0\n name: Upload coverage to Codecov\n with:\n use_oidc: true\n\n - uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1\n name: Upload test results to Codecov\n if: ${{ !cancelled() }}\n with:\n use_oidc: true\n files: '**/test-report.junit.xml'\n\n visual-and-composition:\n name: Test Visual and Composition (${{ matrix.project.tokenName }})\n if: ${{ inputs.run-chromatic }}\n strategy:\n fail-fast: false\n matrix:\n project: ${{ fromJSON(inputs.chromatic-projects) }}\n permissions:\n contents: read\n statuses: write\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n fetch-depth: 0\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - uses: chromaui/action@14cfaef73576e69f95f47f60058063f46ca38719 # v18\n name: Publish to Chromatic\n with:\n projectToken: ${{ (matrix.project.tokenName == 'default' || matrix.project.tokenName == '') && secrets.CHROMATIC_PROJECT_TOKEN || secrets[format('CHROMATIC_PROJECT_TOKEN_{0}', matrix.project.tokenName)] }}\n token: ${{ github.token }}\n buildScriptName: ${{ matrix.project.buildScript || 'build:storybook' }}\n workingDir: ${{ matrix.project.workingDir || '.' }}\n storybookBaseDir: ${{ matrix.project.storybookBaseDir || '' }}\n untraced: ${{ matrix.project.untraced || '' }}\n onlyStoryFiles: ${{ matrix.project.onlyStoryFiles || '' }}\n exitZeroOnChanges: ${{ matrix.project.exitZeroOnChanges || false }}\n\n interaction-and-accessibility:\n name: Test Interactions and Accessibility\n if: ${{ inputs.run-interaction }}\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 15\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm exec playwright install --with-deps\n name: Install Playwright\n\n - run: pnpm test:storybook\n name: Run interaction and accessibility tests\n\n user-flow:\n name: Test User Flow\n if: ${{ inputs.run-user-flow }}\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 15\n strategy:\n fail-fast: false\n matrix:\n containers: [1, 2]\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm exec cypress install\n name: Install Cypress binary\n\n - uses: cypress-io/github-action@1052aa98bbbe4f55210f844878213c07d9c8c399 # v6.7.13\n name: Cypress run\n with:\n start: pnpm dev\n wait-on: ${{ inputs.wait-on-url }}\n record: true\n parallel: true\n env:\n CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n\n conclusion:\n name: Conclusion\n runs-on: ubuntu-latest\n if: always()\n needs: [unit, storybook, visual-and-composition, interaction-and-accessibility, user-flow]\n steps:\n - name: Check job statuses\n run: |\n if [[ \"$RESULTS\" == *\"failure\"* ]] || [[ \"$RESULTS\" == *\"cancelled\"* ]]; then\n exit 1\n fi\n env:\n RESULTS: ${{ join(needs.*.result, ',') }}\n",
|
|
1009
|
+
typecheck: "name: Typecheck\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n secrets:\n TURBO_TOKEN:\n required: false\n\njobs:\n typecheck:\n name: tsc --noEmit\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 10\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - uses: theholocron/.github/.github/actions/holocron@main\n name: Type check\n with:\n task: typecheck\n\n\n conclusion:\n name: Conclusion\n runs-on: ubuntu-latest\n if: always()\n needs: [typecheck]\n steps:\n - name: Check job statuses\n run: |\n if [[ \"$RESULTS\" == *\"failure\"* ]] || [[ \"$RESULTS\" == *\"cancelled\"* ]]; then\n exit 1\n fi\n env:\n RESULTS: ${{ join(needs.*.result, ',') }}\n",
|
|
865
1010
|
wiki: "name: Wiki\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n fern-version:\n description: >\n Fern CLI version to install. Pin this to avoid breaking changes when\n Fern updates their config schema.\n type: string\n required: false\n default: \"5.114.1\"\n preview:\n description: >\n When true, publishes a preview instead of production.\n Requires preview-id to be set.\n type: boolean\n required: false\n default: false\n preview-id:\n description: >\n Stable ID for the preview URL. The preview is accessible at\n {fern-org}-preview-{id}.docs.buildwithfern.com. Use the PR number\n (e.g. \"pr-123\") so the same URL is reused on every push to\n the branch.\n type: string\n required: false\n default: \"\"\n fern-org:\n description: >\n Fern workspace org slug (e.g. \"holocron\"). When set, a GitHub\n deployment is created after a successful preview so the URL appears\n in the PR sidebar widget — the same pattern as Cloudflare Pages previews.\n type: string\n required: false\n default: \"\"\n base-path:\n description: >\n Basepath appended to the preview URL (e.g. \"holocron\" when using\n multi-source routing with wiki.theholocron.dev/holocron).\n Omit for single-instance Fern sites.\n type: string\n required: false\n default: \"\"\n secrets:\n HOLOCRON_FERN_TOKEN:\n required: false\n FERN_TOKEN:\n required: false\n\njobs:\n publish:\n name: ${{ inputs.preview && 'Preview' || 'Publish' }} to Wiki\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n deployments: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - name: Install Fern CLI\n run: npm install -g \"fern-api@$FERN_VERSION\"\n env:\n FERN_VERSION: ${{ inputs.fern-version }}\n\n - name: Publish docs\n if: ${{ !inputs.preview }}\n run: fern generate --docs\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n\n - name: Preview docs\n if: ${{ inputs.preview && inputs.preview-id != '' }}\n run: fern generate --docs --preview --id \"$PREVIEW_ID\"\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n\n - name: Report preview URL\n if: ${{ inputs.preview && inputs.preview-id != '' && inputs.fern-org != '' }}\n env:\n GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n FERN_ORG: ${{ inputs.fern-org }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n BASE_PATH: ${{ inputs.base-path }}\n run: |\n PREVIEW_URL=\"https://${FERN_ORG}-preview-${PREVIEW_ID}.docs.buildwithfern.com${BASE_PATH:+/${BASE_PATH}}\"\n PAYLOAD=$(printf '{\"ref\":\"%s\",\"environment\":\"wiki (Preview)\",\"description\":\"Wiki\",\"production_environment\":false,\"auto_merge\":false,\"required_contexts\":[]}' \\\n \"$GITHUB_HEAD_REF\")\n DEPLOY_ID=$(echo \"$PAYLOAD\" | gh api \"repos/${GITHUB_REPOSITORY}/deployments\" \\\n --method POST --input - | jq -r '.id')\n gh api \"repos/${GITHUB_REPOSITORY}/deployments/${DEPLOY_ID}/statuses\" \\\n --method POST --field state=success --field environment_url=\"$PREVIEW_URL\"\n"
|
|
866
1011
|
};
|
|
867
1012
|
/** Composite actions → `.github/actions/<name>/action.yml` (key includes `/action`). */
|
|
868
1013
|
const REUSABLE_ACTIONS = {
|
|
869
1014
|
"auto-commit/action": auto_commit_default,
|
|
1015
|
+
"holocron/action": holocron_default,
|
|
870
1016
|
"install/action": install_default,
|
|
871
1017
|
"setup/action": setup_default,
|
|
872
1018
|
"setup-node/action": setup_node_default
|
|
@@ -1026,11 +1172,12 @@ function createAstromech(options) {
|
|
|
1026
1172
|
...deps,
|
|
1027
1173
|
task,
|
|
1028
1174
|
cwd: options.cwd,
|
|
1175
|
+
...opts.job !== void 0 ? { job: opts.job } : {},
|
|
1029
1176
|
passthrough: opts.passthrough ?? [],
|
|
1030
1177
|
dryRun: opts.dryRun ?? false,
|
|
1031
1178
|
required: opts.required ?? false,
|
|
1032
1179
|
...opts.filter ? { filter: opts.filter } : {},
|
|
1033
|
-
...task === "lint" ? { linters: lintEntry()?.linters } : {}
|
|
1180
|
+
...task === "lint" && opts.job === void 0 ? { linters: lintEntry()?.linters } : {}
|
|
1034
1181
|
}),
|
|
1035
1182
|
ci: (opts = {}) => runCi({
|
|
1036
1183
|
...deps,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/astromech",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0",
|
|
4
4
|
"description": "The Holocron task runner — one task manifest drives `holocron run`, `holocron ci`, the CI workflows, package.json scripts, linters, and required checks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@theholocron/datapad": "4.
|
|
40
|
+
"@theholocron/datapad": "4.12.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@theholocron/eslint-config": "^8.0.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"tsdown": "^0.22.14",
|
|
54
54
|
"typescript": "^5.9.3",
|
|
55
55
|
"vitest": "^4.1.11",
|
|
56
|
-
"@theholocron/rollup-plugin-transform-template": "4.
|
|
56
|
+
"@theholocron/rollup-plugin-transform-template": "4.12.0"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">=22"
|