@theholocron/cli 3.29.11 → 3.30.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/cli.mjs +199 -104
- package/dist/cli.mjs.map +1 -1
- package/package.json +23 -10
package/dist/cli.mjs
CHANGED
|
@@ -14,6 +14,8 @@ import { homedir } from "node:os";
|
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
15
|
import { access, copyFile, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
|
+
import { generateReadme } from "@theholocron/components-doc/markdown";
|
|
18
|
+
import { getClients, getConfigs, getDocs, getPlugins, getSkills, getThemes, getUtils } from "@theholocron/registry-doc";
|
|
17
19
|
import { createGitHubClient } from "@theholocron/github-client";
|
|
18
20
|
import { promisify } from "node:util";
|
|
19
21
|
import * as Sentry from "@sentry/node";
|
|
@@ -2696,6 +2698,9 @@ var greetings_default$1 = "name: Greetings\n\non: # yamllint disable-line rule:t
|
|
|
2696
2698
|
//#region src/commands/workflows/lint.yml
|
|
2697
2699
|
var lint_default$1 = "name: Lint\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: lint-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: write\n issues: write\n statuses: write\n\njobs:\n lint:\n name: Lint\n uses: theholocron/.github/.github/workflows/lint.yml@main\n secrets: inherit\n with:\n enable-auto-commit: true\n";
|
|
2698
2700
|
//#endregion
|
|
2701
|
+
//#region src/commands/workflows/post-release.yml
|
|
2702
|
+
var post_release_default$1 = "name: Post-release Sync\n\non: # yamllint disable-line rule:truthy\n release:\n types: [published]\n\npermissions:\n contents: read\n\njobs:\n broadcast:\n name: Post-release Sync\n uses: theholocron/.github/.github/workflows/post-release.yml@main\n secrets: inherit\n";
|
|
2703
|
+
//#endregion
|
|
2699
2704
|
//#region src/commands/workflows/release.yml
|
|
2700
2705
|
var release_default$1 = "name: Release\n\non: # yamllint disable-line rule:truthy\n push:\n branches:\n - main\n - alpha\n workflow_dispatch:\n\npermissions:\n contents: write\n id-token: write\n issues: write\n pull-requests: write\n\nconcurrency:\n group: ${{ github.workflow }}-${{ github.ref }}\n cancel-in-progress: false\n\njobs:\n release:\n uses: theholocron/.github/.github/workflows/release.yml@main\n secrets: inherit\n";
|
|
2701
2706
|
//#endregion
|
|
@@ -2705,6 +2710,9 @@ var review_default$1 = "name: Review\n\non: # yamllint disable-line rule:truthy\
|
|
|
2705
2710
|
//#region src/commands/workflows/stale.yml
|
|
2706
2711
|
var stale_default$1 = "name: Stale\n\non: # yamllint disable-line rule:truthy\n schedule:\n - cron: \"30 1 * * *\"\n\npermissions:\n contents: write\n issues: write\n pull-requests: write\n\njobs:\n stale:\n uses: theholocron/.github/.github/workflows/stale.yml@main\n secrets: inherit\n";
|
|
2707
2712
|
//#endregion
|
|
2713
|
+
//#region src/commands/workflows/sync.yml
|
|
2714
|
+
var sync_default$1 = "name: Sync\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main]\n paths:\n - holocron.config.ts\n - package.json\n - pnpm-workspace.yaml\n workflow_dispatch:\n inputs:\n steps:\n description: \"Sync steps to run (default: all)\"\n type: string\n required: false\n\nconcurrency:\n group: sync-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: write\n pull-requests: write\n\njobs:\n sync:\n name: Sync\n uses: theholocron/.github/.github/workflows/sync.yml@main\n with:\n steps: ${{ inputs.steps }}\n secrets: inherit\n";
|
|
2715
|
+
//#endregion
|
|
2708
2716
|
//#region src/commands/workflows/test.yml
|
|
2709
2717
|
var test_default$1 = "name: Test\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: test-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n id-token: write\n statuses: write\n\njobs:\n test:\n name: Test\n uses: theholocron/.github/.github/workflows/test.yml@main\n with:\n run-unit: true\n secrets: inherit\n";
|
|
2710
2718
|
//#endregion
|
|
@@ -2712,13 +2720,22 @@ var test_default$1 = "name: Test\n\non: # yamllint disable-line rule:truthy\n p
|
|
|
2712
2720
|
var typecheck_default$1 = "name: Typecheck\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: typecheck-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n\njobs:\n typecheck:\n name: Typecheck\n uses: theholocron/.github/.github/workflows/typecheck.yml@main\n secrets: inherit\n";
|
|
2713
2721
|
//#endregion
|
|
2714
2722
|
//#region src/commands/setup-workflows.ts
|
|
2715
|
-
/**
|
|
2716
|
-
|
|
2723
|
+
/**
|
|
2724
|
+
* Header prepended to every auto-generated workflow thin caller.
|
|
2725
|
+
*
|
|
2726
|
+
* Used by both `holocron setup` (initial creation) and `holocron sync-github`
|
|
2727
|
+
* (subsequent updates) so the header is always identical regardless of which
|
|
2728
|
+
* command last wrote the file.
|
|
2729
|
+
*
|
|
2730
|
+
* @param source - path within theholocron/holocron that owns the template
|
|
2731
|
+
* @param forPrimary - true only when writing to theholocron/.github itself
|
|
2732
|
+
*/
|
|
2733
|
+
function workflowHeader(source = "packages/cli/src/commands/setup-workflows.ts", forPrimary = false) {
|
|
2717
2734
|
return [
|
|
2718
|
-
`# AUTO-GENERATED — do not edit directly.`,
|
|
2735
|
+
forPrimary ? `# AUTO-GENERATED — do not edit in theholocron/.github directly.` : `# AUTO-GENERATED — do not edit directly.`,
|
|
2719
2736
|
`# Source: theholocron/holocron · ${source}`,
|
|
2720
|
-
`# Tool: holocron
|
|
2721
|
-
`# Changes:
|
|
2737
|
+
`# Tool: holocron sync-github`,
|
|
2738
|
+
`# Changes: edit source in theholocron/holocron and push to alpha or main.`,
|
|
2722
2739
|
``
|
|
2723
2740
|
].join("\n");
|
|
2724
2741
|
}
|
|
@@ -2735,8 +2752,10 @@ const WORKFLOW_TEMPLATES = {
|
|
|
2735
2752
|
typecheck: typecheck_default$1,
|
|
2736
2753
|
codeql: codeql_default$1,
|
|
2737
2754
|
review: review_default$1,
|
|
2755
|
+
"post-release": post_release_default$1,
|
|
2738
2756
|
release: release_default$1,
|
|
2739
2757
|
stale: stale_default$1,
|
|
2758
|
+
sync: sync_default$1,
|
|
2740
2759
|
greetings: greetings_default$1,
|
|
2741
2760
|
dependencies: dependencies_default$1,
|
|
2742
2761
|
bookkeeping: bookkeeping_default$1,
|
|
@@ -3978,6 +3997,138 @@ function runSkillsUpdate(input) {
|
|
|
3978
3997
|
return { status: exitCode === 0 ? "ok" : "fail" };
|
|
3979
3998
|
}
|
|
3980
3999
|
//#endregion
|
|
4000
|
+
//#region src/commands/sync-readme.ts
|
|
4001
|
+
const MARKER_INSTALL_START = "<!-- holocron:installation -->";
|
|
4002
|
+
const MARKER_INSTALL_END = "<!-- /holocron:installation -->";
|
|
4003
|
+
const REGISTRY_MAP = {
|
|
4004
|
+
"@theholocron/clients": getClients,
|
|
4005
|
+
"@theholocron/configs": getConfigs,
|
|
4006
|
+
"@theholocron/docs": getDocs,
|
|
4007
|
+
"@theholocron/holocron": getPlugins,
|
|
4008
|
+
"@theholocron/skills": getSkills,
|
|
4009
|
+
"@theholocron/themes": getThemes,
|
|
4010
|
+
"@theholocron/utils": getUtils
|
|
4011
|
+
};
|
|
4012
|
+
function generateInstallBlock(pkg) {
|
|
4013
|
+
const { name = "", bin, peerDependencies = {} } = pkg;
|
|
4014
|
+
const isCli = Boolean(bin);
|
|
4015
|
+
const isReact = Boolean(peerDependencies["react"]);
|
|
4016
|
+
const lines = [];
|
|
4017
|
+
lines.push("## Installation", "");
|
|
4018
|
+
if (isCli) lines.push("```bash", `npm install --global ${name}`, "```");
|
|
4019
|
+
else lines.push("```bash", `pnpm install ${name}`, "```");
|
|
4020
|
+
lines.push("", "## Usage", "");
|
|
4021
|
+
if (isCli) {
|
|
4022
|
+
const commands = typeof bin === "string" ? [name.split("/").at(-1)] : Object.keys(bin);
|
|
4023
|
+
lines.push("```bash");
|
|
4024
|
+
for (const cmd of commands) lines.push(`${cmd} --help`);
|
|
4025
|
+
lines.push("```");
|
|
4026
|
+
} else if (isReact) lines.push("```tsx", `import { } from "${name}";`, "", "function App() {", ` return <></>;`, "}", "```");
|
|
4027
|
+
else lines.push("```typescript", `import { } from "${name}";`, "```");
|
|
4028
|
+
return lines.join("\n");
|
|
4029
|
+
}
|
|
4030
|
+
/**
|
|
4031
|
+
* Replace content between `<!-- holocron:marker -->` / `<!-- /holocron:marker -->` tags.
|
|
4032
|
+
* Returns the updated README string, or the original if the marker pair is absent.
|
|
4033
|
+
*/
|
|
4034
|
+
function replaceMarkerBlock(readme, marker, content) {
|
|
4035
|
+
const start = `<!-- holocron:${marker} -->`;
|
|
4036
|
+
const end = `<!-- /holocron:${marker} -->`;
|
|
4037
|
+
const lines = readme.split("\n");
|
|
4038
|
+
const startIdx = lines.findIndex((l) => l.trim() === start);
|
|
4039
|
+
const endIdx = lines.findIndex((l) => l.trim() === end);
|
|
4040
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return readme;
|
|
4041
|
+
lines.splice(startIdx + 1, endIdx - startIdx - 1, "", ...content.split("\n"), "");
|
|
4042
|
+
return lines.join("\n");
|
|
4043
|
+
}
|
|
4044
|
+
/**
|
|
4045
|
+
* Apply all README updates in a single read-write cycle.
|
|
4046
|
+
* Returns true if the README was found (even if no changes were needed).
|
|
4047
|
+
*/
|
|
4048
|
+
async function updateReadme(repoRoot, installBlock, sections, dryRun, readFileFn, writeFileFn) {
|
|
4049
|
+
const readmePath = join(repoRoot, "README.md");
|
|
4050
|
+
let content;
|
|
4051
|
+
try {
|
|
4052
|
+
content = await readFileFn(readmePath, "utf8");
|
|
4053
|
+
} catch {
|
|
4054
|
+
return false;
|
|
4055
|
+
}
|
|
4056
|
+
let updated = content;
|
|
4057
|
+
for (const [marker, text] of Object.entries(sections)) updated = replaceMarkerBlock(updated, marker, text);
|
|
4058
|
+
const lines = updated.split("\n");
|
|
4059
|
+
const startIdx = lines.findIndex((l) => l.trim() === MARKER_INSTALL_START);
|
|
4060
|
+
const endIdx = lines.findIndex((l) => l.trim() === MARKER_INSTALL_END);
|
|
4061
|
+
const blockLines = installBlock.split("\n");
|
|
4062
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
4063
|
+
lines.splice(startIdx + 1, endIdx - startIdx - 1, "", ...blockLines, "");
|
|
4064
|
+
updated = lines.join("\n");
|
|
4065
|
+
} else {
|
|
4066
|
+
const descEnd = lines.findIndex((l) => l.trim() === "<!-- /holocron:description -->");
|
|
4067
|
+
const insertAfter = descEnd !== -1 ? descEnd : lines.findIndex((l) => /^# /.test(l));
|
|
4068
|
+
if (insertAfter === -1) return false;
|
|
4069
|
+
lines.splice(insertAfter + 1, 0, "", MARKER_INSTALL_START, "", ...blockLines, "", MARKER_INSTALL_END);
|
|
4070
|
+
updated = lines.join("\n");
|
|
4071
|
+
}
|
|
4072
|
+
if (!dryRun) await writeFileFn(readmePath, updated, "utf8");
|
|
4073
|
+
return true;
|
|
4074
|
+
}
|
|
4075
|
+
async function updateIndexMdxDescription(repoRoot, description, dryRun, readFileFn, writeFileFn) {
|
|
4076
|
+
const mdxPath = join(repoRoot, "docs", "src", "content", "docs", "index.mdx");
|
|
4077
|
+
let content;
|
|
4078
|
+
try {
|
|
4079
|
+
content = await readFileFn(mdxPath, "utf8");
|
|
4080
|
+
} catch {
|
|
4081
|
+
return;
|
|
4082
|
+
}
|
|
4083
|
+
const updated = content.replace(/^(description:\s*).*$/m, `$1${description}`);
|
|
4084
|
+
if (updated !== content && !dryRun) await writeFileFn(mdxPath, updated, "utf8");
|
|
4085
|
+
}
|
|
4086
|
+
async function runSyncReadme(input) {
|
|
4087
|
+
const { loaded, context, print = console.log, readFileFn = readFile, writeFileFn = writeFile } = input;
|
|
4088
|
+
const { repoRoot, dryRun = false } = context;
|
|
4089
|
+
const namespaces = loaded.resolved.env?.namespaces ?? [];
|
|
4090
|
+
print(style.header(`Syncing README installation block${dryRun ? " (dry-run)" : ""}…`));
|
|
4091
|
+
if (namespaces.length > 0) print(style.hint(` namespaces: ${namespaces.join(", ")}`));
|
|
4092
|
+
let pkg;
|
|
4093
|
+
try {
|
|
4094
|
+
const raw = await readFileFn(join(repoRoot, "package.json"), "utf8");
|
|
4095
|
+
pkg = JSON.parse(raw);
|
|
4096
|
+
} catch {
|
|
4097
|
+
return {
|
|
4098
|
+
status: "fail",
|
|
4099
|
+
updated: false,
|
|
4100
|
+
message: "Could not read package.json"
|
|
4101
|
+
};
|
|
4102
|
+
}
|
|
4103
|
+
const description = loaded.resolved.description ?? "";
|
|
4104
|
+
const sections = generateReadme({
|
|
4105
|
+
description,
|
|
4106
|
+
homepage: loaded.resolved.homepage ?? pkg.homepage ?? "",
|
|
4107
|
+
scripts: pkg.scripts ?? {},
|
|
4108
|
+
packages: pkg.name ? REGISTRY_MAP[pkg.name]?.() : void 0
|
|
4109
|
+
});
|
|
4110
|
+
const markerSections = {
|
|
4111
|
+
...sections.packages !== void 0 && { packages: sections.packages },
|
|
4112
|
+
development: sections.development,
|
|
4113
|
+
releases: sections.releases
|
|
4114
|
+
};
|
|
4115
|
+
const updated = await updateReadme(repoRoot, generateInstallBlock(pkg), markerSections, dryRun, readFileFn, writeFileFn);
|
|
4116
|
+
if (!updated) {
|
|
4117
|
+
print(style.warn("README.md not found or has no writable location for installation block"));
|
|
4118
|
+
return {
|
|
4119
|
+
status: "fail",
|
|
4120
|
+
updated: false,
|
|
4121
|
+
message: "README.md update failed"
|
|
4122
|
+
};
|
|
4123
|
+
}
|
|
4124
|
+
if (description) await updateIndexMdxDescription(repoRoot, description, dryRun, readFileFn, writeFileFn);
|
|
4125
|
+
print(dryRun ? style.hint(" would update README.md") : style.success(" updated README.md"));
|
|
4126
|
+
return {
|
|
4127
|
+
status: dryRun ? "dry-run" : "ok",
|
|
4128
|
+
updated
|
|
4129
|
+
};
|
|
4130
|
+
}
|
|
4131
|
+
//#endregion
|
|
3981
4132
|
//#region src/commands/sync.ts
|
|
3982
4133
|
const SYNC_STEPS = [
|
|
3983
4134
|
"labels",
|
|
@@ -3986,12 +4137,14 @@ const SYNC_STEPS = [
|
|
|
3986
4137
|
"topics",
|
|
3987
4138
|
"keywords",
|
|
3988
4139
|
"description",
|
|
3989
|
-
"homepage"
|
|
4140
|
+
"homepage",
|
|
4141
|
+
"readme"
|
|
3990
4142
|
];
|
|
3991
4143
|
const LOCAL_STEPS = /* @__PURE__ */ new Set([
|
|
3992
4144
|
"keywords",
|
|
3993
4145
|
"description",
|
|
3994
|
-
"homepage"
|
|
4146
|
+
"homepage",
|
|
4147
|
+
"readme"
|
|
3995
4148
|
]);
|
|
3996
4149
|
async function runSync(input) {
|
|
3997
4150
|
const print = input.print ?? ((line) => console.log(line));
|
|
@@ -4129,7 +4282,8 @@ async function runSync(input) {
|
|
|
4129
4282
|
for (const stepName of [
|
|
4130
4283
|
"keywords",
|
|
4131
4284
|
"description",
|
|
4132
|
-
"homepage"
|
|
4285
|
+
"homepage",
|
|
4286
|
+
"readme"
|
|
4133
4287
|
]) {
|
|
4134
4288
|
if (requestedSteps !== void 0 && !requestedSteps.includes(stepName)) continue;
|
|
4135
4289
|
if (stepName === "keywords") {
|
|
@@ -4197,6 +4351,20 @@ async function runSync(input) {
|
|
|
4197
4351
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
4198
4352
|
}
|
|
4199
4353
|
}
|
|
4354
|
+
if (stepName === "readme") {
|
|
4355
|
+
const report = await runSyncReadme({
|
|
4356
|
+
loaded: input.loaded,
|
|
4357
|
+
context: input.context,
|
|
4358
|
+
print: () => {}
|
|
4359
|
+
});
|
|
4360
|
+
steps.push({
|
|
4361
|
+
capability: "local",
|
|
4362
|
+
step: "sync readme",
|
|
4363
|
+
status: report.status === "ok" ? "ok" : report.status === "dry-run" ? "dry-run" : "skip",
|
|
4364
|
+
...report.message ? { message: report.message } : {}
|
|
4365
|
+
});
|
|
4366
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
4367
|
+
}
|
|
4200
4368
|
}
|
|
4201
4369
|
const summary = steps.reduce((acc, s) => {
|
|
4202
4370
|
if (s.status === "ok") acc.ok += 1;
|
|
@@ -4285,6 +4453,9 @@ async function updateReadmeDescription(repoRoot, description) {
|
|
|
4285
4453
|
return true;
|
|
4286
4454
|
}
|
|
4287
4455
|
//#endregion
|
|
4456
|
+
//#region src/templates/actions/auto-commit.yml
|
|
4457
|
+
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 - uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0\n id: commit\n with:\n branch: ${{ inputs.branch }}\n commit_message: ${{ inputs.commit-message }}\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";
|
|
4458
|
+
//#endregion
|
|
4288
4459
|
//#region src/templates/actions/install.yml
|
|
4289
4460
|
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";
|
|
4290
4461
|
//#endregion
|
|
@@ -4313,7 +4484,10 @@ var deploy_default = "name: Deploy\n\non: # yamllint disable-line rule:truthy\n
|
|
|
4313
4484
|
var greetings_default = "name: Greetings\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n\njobs:\n greeting:\n name: Greet first-time contributors\n permissions:\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 5\n # Group by the issue/PR number so duplicate events don't race each other.\n steps:\n - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n name: Greet on first contribution\n with:\n script: |\n // Only greet on the initial open — ignore synchronize, reopened, etc.\n if (context.payload.action !== 'opened') return;\n\n const actor = context.actor;\n const { owner, repo } = context.repo;\n\n // Payload inspection is more reliable than context.eventName for detecting\n // whether this is an issue vs. PR event — works regardless of how GitHub\n // propagates event names through workflow_call chains.\n const isIssue = !!context.payload.issue && !context.payload.pull_request;\n // listForRepo returns both issues and PRs (GitHub treats PRs as issues),\n // sorted newest-first. Filter by type to track first-issue and first-PR\n // independently, and avoid search-index eventual-consistency lag.\n const { data: recent } = await github.rest.issues.listForRepo({\n owner, repo,\n creator: actor,\n state: 'all',\n per_page: 100\n });\n\n const sameType = recent.filter(item =>\n isIssue ? !item.pull_request : !!item.pull_request\n );\n\n if (sameType.length !== 1) return;\n const body = isIssue\n ? `Hey @${actor}!\\n\\nWe really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we are a limited number of volunteers, so it is possible that this will not be addressed as swiftly.\\n\\nYour patience is much appreciated and we will get back to you as quickly as possible.`\n : `Hey @${actor}!\\n\\nWe really appreciate you taking the time to help out with this PR. The collaborators on this project attempt to help as many people as possible, but we are a limited number of volunteers, so it is possible that this will not be addressed as swiftly.\\n\\nYour patience is much appreciated and we will get back to you as quickly as possible.`;\n\n await github.rest.issues.createComment({\n owner,\n repo,\n issue_number: context.issue.number,\n body\n });\n";
|
|
4314
4485
|
//#endregion
|
|
4315
4486
|
//#region src/templates/workflows/lint.yml
|
|
4316
|
-
var lint_default = "name: Lint\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n eslint-config:\n description: >\n Filename for the ESLint flat config used by super-linter for\n JavaScript, JSX, TSX, and TypeScript (ES) files. Defaults to\n eslint.config.ts (the org standard). Note: ESLint 9 requires\n --flag unstable_ts_config to load .ts configs; if super-linter\n cannot load it, override with eslint.config.mjs or eslint.config.js.\n type: string\n required: false\n default: eslint.config.ts\n prettier-config:\n description: >\n Filename for the Prettier config. Defaults to prettier.config.ts\n (the org standard). Prettier 3.x loads .ts configs natively.\n type: string\n required: false\n default: prettier.config.ts\n yaml-config:\n description: >\n Filename for the yamllint config. Defaults to yamllint.config.yml.\n type: string\n required: false\n default: yamllint.config.yml\n enable-auto-commit:\n description: >\n Auto-commit super-linter fixes as a verified commit via a GitHub App.\n Requires SUPER_LINTER_APP_ID and SUPER_LINTER_PRIVATE_KEY secrets.\n type: boolean\n required: false\n default: false\n secrets:\n SUPER_LINTER_APP_ID:\n required: false\n SUPER_LINTER_PRIVATE_KEY:\n required: false\n\njobs:\n super-lint:\n name: Lint entire codebase\n permissions:\n contents: write\n issues: write\n statuses: write\n runs-on: ubuntu-latest\n timeout-minutes: 30\n env:\n APP_ID_SET: ${{ secrets.SUPER_LINTER_APP_ID != '' }}\n steps:\n - name: Generate GitHub App token\n id: app-token\n # Runs before checkout so the token is used as the checkout credential,\n # which makes the subsequent push go through the App and produce a\n # Verified commit. Skipped when auto-commit is disabled or secrets unset.\n if: >\n inputs.enable-auto-commit == true &&\n github.event.pull_request != null &&\n github.event.pull_request.head.repo.full_name == github.repository &&\n github.ref_name != github.event.repository.default_branch &&\n env.APP_ID_SET == 'true'\n uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n with:\n app-id: ${{ secrets.SUPER_LINTER_APP_ID }}\n private-key: ${{ secrets.SUPER_LINTER_PRIVATE_KEY }}\n\n - name: Resolve App bot identity\n id: app-bot\n # GitHub marks commits as Verified when the author email matches the\n # App bot's noreply address (<numeric-id>+<slug>[bot]@users.noreply.github.com).\n # The numeric ID must be fetched via the API — it differs from the App ID.\n # app-slug is passed via env rather than interpolated into the script to\n # prevent code injection (CWE-78).\n if: steps.app-token.conclusion == 'success'\n run: |\n BOT_SLUG=\"${APP_SLUG}[bot]\"\n BOT_ID=$(gh api \"/users/${BOT_SLUG}\" --jq .id)\n echo \"name=${BOT_SLUG}\" >> \"$GITHUB_OUTPUT\"\n echo \"email=${BOT_ID}+${BOT_SLUG}@users.noreply.github.com\" >> \"$GITHUB_OUTPUT\"\n env:\n GH_TOKEN: ${{ steps.app-token.outputs.token }}\n APP_SLUG: ${{ steps.app-token.outputs.app-slug }}\n\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n fetch-depth: 0\n # Use the App token when available so the push credential is the App\n # bot — GitHub marks those commits as Verified automatically.\n token: ${{ steps.app-token.outputs.token || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n if: ${{ hashFiles('pnpm-lock.yaml') != '' }}\n\n - name: Detect project features\n # Writes VALIDATE_*/FIX_* to GITHUB_ENV only when the feature exists.\n # Also writes step outputs for values referenced in expression context\n # (GITHUB_ENV is not readable via steps.*.outputs — they need GITHUB_OUTPUT).\n # All values written are hardcoded 'true' — no user input in the script.\n # Config file paths (inputs.*) stay in GH Actions expression context in\n # the super-linter env: block below, never shell-evaluated (CWE-78).\n id: detect\n run: |\n has() { find . -not -path '*/node_modules/*' -name \"$1\" -print -quit 2>/dev/null | grep -q .; }\n\n if { has 'eslint.config.ts' || has 'eslint.config.mjs' || has 'eslint.config.js' || has '.eslintrc.json' || has '.eslintrc.yml'; }; then\n {\n echo \"VALIDATE_JAVASCRIPT_ES=true\"\n echo \"VALIDATE_TYPESCRIPT_ES=true\"\n } >> \"$GITHUB_ENV\"\n echo \"eslint=true\" >> \"$GITHUB_OUTPUT\"\n fi\n\n if { has '*.js' || has '*.jsx' || has '*.mjs' || has '*.cjs' || has '*.ts' || has '*.tsx'; }; then\n {\n echo \"VALIDATE_JAVASCRIPT_PRETTIER=true\"\n echo \"VALIDATE_JSX_PRETTIER=true\"\n echo \"VALIDATE_TYPESCRIPT_PRETTIER=true\"\n echo \"VALIDATE_TSX=true\"\n echo \"FIX_JAVASCRIPT_PRETTIER=true\"\n echo \"FIX_JSX_PRETTIER=true\"\n echo \"FIX_TYPESCRIPT_PRETTIER=true\"\n echo \"FIX_TSX=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.css' || has '*.scss' || has 'stylelint.config.ts' || has 'stylelint.config.mjs' || has 'stylelint.config.js'; }; then\n {\n echo \"VALIDATE_CSS=true\"\n echo \"STYLELINT_CONFIG_FILE=stylelint.config.ts\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.graphql' || has '*.gql'; }; then\n {\n echo \"VALIDATE_GRAPHQL_PRETTIER=true\"\n echo \"FIX_GRAPHQL_PRETTIER=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.html' || has '*.htm'; }; then\n {\n echo \"VALIDATE_HTML_PRETTIER=true\"\n echo \"FIX_HTML_PRETTIER=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '.env' || has '.env.example' || has '.env.local'; }; then\n {\n echo \"VALIDATE_ENV=true\"\n echo \"FIX_ENV=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has 'Dockerfile' || has '*.Dockerfile'; }; then\n echo \"VALIDATE_DOCKERFILE=true\" >> \"$GITHUB_ENV\"\n fi\n\n - uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0\n name: Run Super Linter\n env:\n GITHUB_TOKEN: ${{ github.token }}\n DEFAULT_BRANCH: ${{ github.event.pull_request.base.ref || github.event.repository.default_branch }}\n ANNOTATE_ONLY: true\n DISABLE_COMMENTS: false\n IGNORE_GITIGNORED_FILES: true\n LINTER_RULES_PATH: /\n EDITORCONFIG_FILE_NAME: \".editorconfig-checker.json\"\n # Config file paths — inputs stay in expression context, never shell-evaluated.\n # When VALIDATE_JAVASCRIPT_ES is not set by detect, ESLint doesn't run so\n # the empty-string fallback (→ eslint.config.mjs in container) is safe.\n JAVASCRIPT_ES_CONFIG_FILE: ${{ steps.detect.outputs.eslint == 'true' && inputs.eslint-config || '' }}\n TYPESCRIPT_ES_CONFIG_FILE: ${{ steps.detect.outputs.eslint == 'true' && inputs.eslint-config || '' }}\n PRETTIER_CONFIG: ${{ inputs.prettier-config }}\n YAML_CONFIG_FILE: ${{ inputs.yaml-config }}\n # Always-on linters\n FIX_MARKDOWN_PRETTIER: true\n VALIDATE_EDITORCONFIG: true\n VALIDATE_GIT_COMMITLINT: true\n VALIDATE_GIT_MERGE_CONFLICT_MARKERS: true\n VALIDATE_GITHUB_ACTIONS: true\n VALIDATE_GITLEAKS: true\n VALIDATE_MARKDOWN_PRETTIER: true\n VALIDATE_YAML: true\n\n - uses:
|
|
4487
|
+
var lint_default = "name: Lint\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n eslint-config:\n description: >\n Filename for the ESLint flat config used by super-linter for\n JavaScript, JSX, TSX, and TypeScript (ES) files. Defaults to\n eslint.config.ts (the org standard). Note: ESLint 9 requires\n --flag unstable_ts_config to load .ts configs; if super-linter\n cannot load it, override with eslint.config.mjs or eslint.config.js.\n type: string\n required: false\n default: eslint.config.ts\n prettier-config:\n description: >\n Filename for the Prettier config. Defaults to prettier.config.ts\n (the org standard). Prettier 3.x loads .ts configs natively.\n type: string\n required: false\n default: prettier.config.ts\n yaml-config:\n description: >\n Filename for the yamllint config. Defaults to yamllint.config.yml.\n type: string\n required: false\n default: yamllint.config.yml\n enable-auto-commit:\n description: >\n Auto-commit super-linter fixes as a verified commit via a GitHub App.\n Requires SUPER_LINTER_APP_ID and SUPER_LINTER_PRIVATE_KEY secrets.\n type: boolean\n required: false\n default: false\n secrets:\n SUPER_LINTER_APP_ID:\n required: false\n SUPER_LINTER_PRIVATE_KEY:\n required: false\n\njobs:\n super-lint:\n name: Lint entire codebase\n permissions:\n contents: write\n issues: write\n statuses: write\n runs-on: ubuntu-latest\n timeout-minutes: 30\n env:\n APP_ID_SET: ${{ secrets.SUPER_LINTER_APP_ID != '' }}\n steps:\n - name: Generate GitHub App token\n id: app-token\n # Runs before checkout so the token is used as the checkout credential,\n # which makes the subsequent push go through the App and produce a\n # Verified commit. Skipped when auto-commit is disabled or secrets unset.\n if: >\n inputs.enable-auto-commit == true &&\n github.event.pull_request != null &&\n github.event.pull_request.head.repo.full_name == github.repository &&\n github.ref_name != github.event.repository.default_branch &&\n env.APP_ID_SET == 'true'\n uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n with:\n app-id: ${{ secrets.SUPER_LINTER_APP_ID }}\n private-key: ${{ secrets.SUPER_LINTER_PRIVATE_KEY }}\n\n - name: Resolve App bot identity\n id: app-bot\n # GitHub marks commits as Verified when the author email matches the\n # App bot's noreply address (<numeric-id>+<slug>[bot]@users.noreply.github.com).\n # The numeric ID must be fetched via the API — it differs from the App ID.\n # app-slug is passed via env rather than interpolated into the script to\n # prevent code injection (CWE-78).\n if: steps.app-token.conclusion == 'success'\n run: |\n BOT_SLUG=\"${APP_SLUG}[bot]\"\n BOT_ID=$(gh api \"/users/${BOT_SLUG}\" --jq .id)\n echo \"name=${BOT_SLUG}\" >> \"$GITHUB_OUTPUT\"\n echo \"email=${BOT_ID}+${BOT_SLUG}@users.noreply.github.com\" >> \"$GITHUB_OUTPUT\"\n env:\n GH_TOKEN: ${{ steps.app-token.outputs.token }}\n APP_SLUG: ${{ steps.app-token.outputs.app-slug }}\n\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n fetch-depth: 0\n # Use the App token when available so the push credential is the App\n # bot — GitHub marks those commits as Verified automatically.\n token: ${{ steps.app-token.outputs.token || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n if: ${{ hashFiles('pnpm-lock.yaml') != '' }}\n\n - name: Detect project features\n # Writes VALIDATE_*/FIX_* to GITHUB_ENV only when the feature exists.\n # Also writes step outputs for values referenced in expression context\n # (GITHUB_ENV is not readable via steps.*.outputs — they need GITHUB_OUTPUT).\n # All values written are hardcoded 'true' — no user input in the script.\n # Config file paths (inputs.*) stay in GH Actions expression context in\n # the super-linter env: block below, never shell-evaluated (CWE-78).\n id: detect\n run: |\n has() { find . -not -path '*/node_modules/*' -name \"$1\" -print -quit 2>/dev/null | grep -q .; }\n\n if { has 'eslint.config.ts' || has 'eslint.config.mjs' || has 'eslint.config.js' || has '.eslintrc.json' || has '.eslintrc.yml'; }; then\n {\n echo \"VALIDATE_JAVASCRIPT_ES=true\"\n echo \"VALIDATE_TYPESCRIPT_ES=true\"\n } >> \"$GITHUB_ENV\"\n echo \"eslint=true\" >> \"$GITHUB_OUTPUT\"\n fi\n\n if { has '*.js' || has '*.jsx' || has '*.mjs' || has '*.cjs' || has '*.ts' || has '*.tsx'; }; then\n {\n echo \"VALIDATE_JAVASCRIPT_PRETTIER=true\"\n echo \"VALIDATE_JSX_PRETTIER=true\"\n echo \"VALIDATE_TYPESCRIPT_PRETTIER=true\"\n echo \"VALIDATE_TSX=true\"\n echo \"FIX_JAVASCRIPT_PRETTIER=true\"\n echo \"FIX_JSX_PRETTIER=true\"\n echo \"FIX_TYPESCRIPT_PRETTIER=true\"\n echo \"FIX_TSX=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.css' || has '*.scss' || has 'stylelint.config.ts' || has 'stylelint.config.mjs' || has 'stylelint.config.js'; }; then\n {\n echo \"VALIDATE_CSS=true\"\n echo \"STYLELINT_CONFIG_FILE=stylelint.config.ts\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.graphql' || has '*.gql'; }; then\n {\n echo \"VALIDATE_GRAPHQL_PRETTIER=true\"\n echo \"FIX_GRAPHQL_PRETTIER=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '*.html' || has '*.htm'; }; then\n {\n echo \"VALIDATE_HTML_PRETTIER=true\"\n echo \"FIX_HTML_PRETTIER=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has '.env' || has '.env.example' || has '.env.local'; }; then\n {\n echo \"VALIDATE_ENV=true\"\n echo \"FIX_ENV=true\"\n } >> \"$GITHUB_ENV\"\n fi\n\n if { has 'Dockerfile' || has '*.Dockerfile'; }; then\n echo \"VALIDATE_DOCKERFILE=true\" >> \"$GITHUB_ENV\"\n fi\n\n - uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0\n name: Run Super Linter\n env:\n GITHUB_TOKEN: ${{ github.token }}\n DEFAULT_BRANCH: ${{ github.event.pull_request.base.ref || github.event.repository.default_branch }}\n ANNOTATE_ONLY: true\n DISABLE_COMMENTS: false\n IGNORE_GITIGNORED_FILES: true\n LINTER_RULES_PATH: /\n EDITORCONFIG_FILE_NAME: \".editorconfig-checker.json\"\n # Config file paths — inputs stay in expression context, never shell-evaluated.\n # When VALIDATE_JAVASCRIPT_ES is not set by detect, ESLint doesn't run so\n # the empty-string fallback (→ eslint.config.mjs in container) is safe.\n JAVASCRIPT_ES_CONFIG_FILE: ${{ steps.detect.outputs.eslint == 'true' && inputs.eslint-config || '' }}\n TYPESCRIPT_ES_CONFIG_FILE: ${{ steps.detect.outputs.eslint == 'true' && inputs.eslint-config || '' }}\n PRETTIER_CONFIG: ${{ inputs.prettier-config }}\n YAML_CONFIG_FILE: ${{ inputs.yaml-config }}\n # Always-on linters\n FIX_MARKDOWN_PRETTIER: true\n VALIDATE_EDITORCONFIG: true\n VALIDATE_GIT_COMMITLINT: true\n VALIDATE_GIT_MERGE_CONFLICT_MARKERS: true\n VALIDATE_GITHUB_ACTIONS: true\n VALIDATE_GITLEAKS: true\n VALIDATE_MARKDOWN_PRETTIER: true\n VALIDATE_YAML: true\n\n - uses: theholocron/.github/.github/actions/auto-commit@main\n name: Commit and push linting fixes\n if: >\n inputs.enable-auto-commit == true &&\n github.event.pull_request != null &&\n github.event.pull_request.head.repo.full_name == github.repository &&\n github.ref_name != github.event.repository.default_branch &&\n env.APP_ID_SET == 'true'\n with:\n token: ${{ steps.app-token.outputs.token }}\n branch: ${{ github.event.pull_request.head.ref || github.head_ref || github.ref }}\n commit-message: \"chore: fix linting issues\\n\\nSigned-off-by: ${{ steps.app-bot.outputs.name }} <${{ steps.app-bot.outputs.email }}>\"\n commit-options: \"--no-verify\"\n commit-user-name: ${{ steps.app-bot.outputs.name }}\n commit-user-email: ${{ steps.app-bot.outputs.email }}\n commit-author: \"${{ steps.app-bot.outputs.name }} <${{ steps.app-bot.outputs.email }}>\"\n";
|
|
4488
|
+
//#endregion
|
|
4489
|
+
//#region src/templates/workflows/post-release.yml
|
|
4490
|
+
var post_release_default = "name: Post-release Sync\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n secrets:\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 broadcast:\n name: Broadcast readme sync\n runs-on: ubuntu-latest\n timeout-minutes: 5\n steps:\n - name: Trigger broadcast readme sync\n run: |\n gh workflow run sync-broadcast.yml \\\n --repo theholocron/.github \\\n --field \"steps=readme\"\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n";
|
|
4317
4491
|
//#endregion
|
|
4318
4492
|
//#region src/templates/workflows/release.yml
|
|
4319
4493
|
var release_default = "name: Release\n\n# Semantic-release with OIDC Trusted Publishing.\n# actions/setup-node writes a default NODE_AUTH_TOKEN=${{ github.token }}\n# which shadows OIDC auth. We explicitly clear it so npm falls through to\n# the Trusted Publisher OIDC exchange.\n# The calling repo must have a .releaserc.json that configures branches,\n# plugins, and any publish options. npm@11+ is installed to support OIDC.\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n run-build:\n description: Run `pnpm build` before releasing\n type: boolean\n required: false\n default: true\n sentry-project:\n description: >\n Sentry project slug for sourcemap upload and release creation after\n publishing. Omit to skip the Sentry release step entirely.\n type: string\n required: false\n default: \"\"\n secrets:\n HOLOCRON_RELEASE_TOKEN:\n description: >\n Fine-grained PAT (Contents + Issues + Pull requests: write) owned by\n an admin. Required when the default branch is protected by a ruleset —\n github.token cannot push through rulesets, but an admin PAT can.\n Takes priority over HOLOCRON_SYNC_TOKEN. Falls back to github.token.\n required: false\n HOLOCRON_SYNC_TOKEN:\n description: >\n Legacy alias for HOLOCRON_RELEASE_TOKEN — kept for backward compatibility.\n Prefer HOLOCRON_RELEASE_TOKEN for new repos.\n required: false\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 github.token.\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for `gh` CLI calls. Used when\n HOLOCRON_READ_TOKEN is not set.\n required: false\n SENTRY_AUTH_TOKEN:\n description: >\n Sentry auth token for sourcemap upload and release creation.\n Required when sentry-project is set. Use the org-level secret.\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n release:\n name: Semantic release\n permissions:\n contents: write\n id-token: write\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 30\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n # Do not cancel in-progress releases — a partial release is worse than a slow one.\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n fetch-depth: 0\n persist-credentials: false\n # Use HOLOCRON_RELEASE_TOKEN when available — git push (tags, release commits)\n # uses the checkout credential, not GITHUB_TOKEN env var. The\n # built-in github.token cannot push through branch protection rulesets.\n token: ${{ secrets.HOLOCRON_RELEASE_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - name: Configure git identity\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 git config --global user.name \"$GIT_NAME\"\n git config --global user.email \"$GIT_EMAIL\"\n git config --global format.signoff true\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - name: Upgrade npm for OIDC support\n run: npm install -g npm@11 sigstore\n # sigstore is required by libnpmpublish/provenance.js at module parse\n # time — before any config takes effect. Some npm 11.x builds stopped\n # bundling it; installing it globally into the same prefix ensures it\n # resolves regardless of npm version. (Discovered 2026-07-09.)\n\n - run: pnpm build\n name: Build\n if: ${{ inputs.run-build == true }}\n\n - run: npx semantic-release\n name: Release\n env:\n # Prefer HOLOCRON_RELEASE_TOKEN (fine-grained PAT, Contents+Issues+PRs write,\n # owned by an admin with ruleset bypass) so @semantic-release/git can\n # push the version-bump commit through branch protection. Falls back to\n # HOLOCRON_SYNC_TOKEN (legacy) then github.token for unprotected repos.\n GITHUB_TOKEN: ${{ secrets.HOLOCRON_RELEASE_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || github.token }}\n HUSKY: \"0\"\n NPM_CONFIG_PROVENANCE: true\n\n - name: Get release version\n id: release_version\n if: ${{ inputs.sentry-project != '' }}\n env:\n SENTRY_PROJECT: ${{ inputs.sentry-project }}\n run: |\n TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo \"\")\n if [ -n \"$TAG\" ]; then\n echo \"release=${SENTRY_PROJECT}@${TAG#v}\" >> \"$GITHUB_OUTPUT\"\n fi\n\n - name: Create Sentry release\n if: ${{ inputs.sentry-project != '' && steps.release_version.outputs.release != '' }}\n uses: getsentry/action-release@ff07929a6537bac57790c3451cf4d364aca38528 # v3\n env:\n SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}\n SENTRY_ORG: theholocron\n SENTRY_PROJECT: ${{ inputs.sentry-project }}\n with:\n environment: production\n version: ${{ steps.release_version.outputs.release }}\n sourcemaps: \"**/dist\"\n";
|
|
@@ -4324,8 +4498,14 @@ var review_default = "name: Review\n\n# ReviewDog is the annotation layer — po
|
|
|
4324
4498
|
//#region src/templates/workflows/stale.yml
|
|
4325
4499
|
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 is marked stale\n type: number\n required: false\n default: 30\n days-before-close:\n description: Days of inactivity after stale label before closing\n type: number\n required: false\n default: 5\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-stale: ${{ inputs.days-before-stale }}\n exempt-all-pr-milestones: true\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";
|
|
4326
4500
|
//#endregion
|
|
4501
|
+
//#region src/templates/workflows/sync.yml
|
|
4502
|
+
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.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme description\".\n type: string\n required: false\n secrets:\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 - name: Run holocron sync\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n pnpm exec holocron sync --steps $STEPS\n else\n pnpm exec holocron sync\n fi\n env:\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\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\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";
|
|
4503
|
+
//#endregion
|
|
4504
|
+
//#region src/templates/workflows/sync-broadcast.yml
|
|
4505
|
+
var sync_broadcast_default = "name: Sync Broadcast\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 done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n";
|
|
4506
|
+
//#endregion
|
|
4327
4507
|
//#region src/templates/workflows/sync-github.yml
|
|
4328
|
-
var sync_github_default = "name: Sync GitHub 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# 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 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 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";
|
|
4508
|
+
var sync_github_default = "name: Sync GitHub 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 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 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";
|
|
4329
4509
|
//#endregion
|
|
4330
4510
|
//#region src/templates/workflows/test.yml
|
|
4331
4511
|
var test_default = "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 - run: pnpm test:coverage\n name: Run tests with coverage\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";
|
|
@@ -4343,8 +4523,9 @@ var typecheck_default = "name: Typecheck\n\non: # yamllint disable-line rule:tru
|
|
|
4343
4523
|
* inlines them as string exports at build time.
|
|
4344
4524
|
*/
|
|
4345
4525
|
const ACTIONS = {
|
|
4346
|
-
"
|
|
4526
|
+
"auto-commit/action": auto_commit_default,
|
|
4347
4527
|
"install/action": install_default,
|
|
4528
|
+
"setup/action": setup_default,
|
|
4348
4529
|
"setup-node/action": setup_node_default
|
|
4349
4530
|
};
|
|
4350
4531
|
const REUSABLE_WORKFLOWS = {
|
|
@@ -4355,10 +4536,13 @@ const REUSABLE_WORKFLOWS = {
|
|
|
4355
4536
|
deploy: deploy_default,
|
|
4356
4537
|
greetings: greetings_default,
|
|
4357
4538
|
lint: lint_default,
|
|
4539
|
+
"post-release": post_release_default,
|
|
4358
4540
|
release: release_default,
|
|
4359
4541
|
review: review_default,
|
|
4360
4542
|
stale: stale_default,
|
|
4543
|
+
"sync-broadcast": sync_broadcast_default,
|
|
4361
4544
|
"sync-github": sync_github_default,
|
|
4545
|
+
sync: sync_default,
|
|
4362
4546
|
test: test_default,
|
|
4363
4547
|
typecheck: typecheck_default
|
|
4364
4548
|
};
|
|
@@ -4457,15 +4641,6 @@ function reusableHeader(source) {
|
|
|
4457
4641
|
``
|
|
4458
4642
|
].join("\n");
|
|
4459
4643
|
}
|
|
4460
|
-
function thinCallerHeader(forPrimary = false) {
|
|
4461
|
-
return [
|
|
4462
|
-
forPrimary ? `# AUTO-GENERATED — do not edit in theholocron/.github directly.` : `# AUTO-GENERATED — do not edit directly.`,
|
|
4463
|
-
`# Source: theholocron/holocron · packages/cli/src/commands/setup-workflows.ts`,
|
|
4464
|
-
`# Tool: holocron sync-github`,
|
|
4465
|
-
`# Changes: edit source in theholocron/holocron and push to alpha or main.`,
|
|
4466
|
-
``
|
|
4467
|
-
].join("\n");
|
|
4468
|
-
}
|
|
4469
4644
|
function buildBatch(repo, allowedWorkflows, withOverrides) {
|
|
4470
4645
|
const files = [];
|
|
4471
4646
|
const isPrimaryGithubRepo = repo === DEFAULT_REPO;
|
|
@@ -4481,7 +4656,7 @@ function buildBatch(repo, allowedWorkflows, withOverrides) {
|
|
|
4481
4656
|
for (const [name, content] of Object.entries(WORKFLOW_TEMPLATES)) {
|
|
4482
4657
|
files.push({
|
|
4483
4658
|
path: `workflow-templates/${name}.yml`,
|
|
4484
|
-
content:
|
|
4659
|
+
content: workflowHeader(void 0, true) + content
|
|
4485
4660
|
});
|
|
4486
4661
|
const props = WORKFLOW_TEMPLATE_PROPERTIES[name];
|
|
4487
4662
|
if (props) files.push({
|
|
@@ -4496,7 +4671,7 @@ function buildBatch(repo, allowedWorkflows, withOverrides) {
|
|
|
4496
4671
|
if (!content) continue;
|
|
4497
4672
|
files.push({
|
|
4498
4673
|
path: `.github/workflows/${name}.yml`,
|
|
4499
|
-
content:
|
|
4674
|
+
content: workflowHeader() + content
|
|
4500
4675
|
});
|
|
4501
4676
|
}
|
|
4502
4677
|
return files;
|
|
@@ -4708,86 +4883,6 @@ async function runSyncGithub(input) {
|
|
|
4708
4883
|
};
|
|
4709
4884
|
}
|
|
4710
4885
|
//#endregion
|
|
4711
|
-
//#region src/commands/sync-readme.ts
|
|
4712
|
-
const README_INSTALL_START = "<!-- holocron:installation -->";
|
|
4713
|
-
const README_INSTALL_END = "<!-- /holocron:installation -->";
|
|
4714
|
-
function generateInstallBlock(pkg) {
|
|
4715
|
-
const { name = "", bin, peerDependencies = {} } = pkg;
|
|
4716
|
-
const isCli = Boolean(bin);
|
|
4717
|
-
const isReact = Boolean(peerDependencies["react"]);
|
|
4718
|
-
const lines = [];
|
|
4719
|
-
lines.push("## Installation", "");
|
|
4720
|
-
if (isCli) lines.push("```bash", `npm install --global ${name}`, "```");
|
|
4721
|
-
else lines.push("```bash", `pnpm install ${name}`, "```");
|
|
4722
|
-
lines.push("", "## Usage", "");
|
|
4723
|
-
if (isCli) {
|
|
4724
|
-
const commands = typeof bin === "string" ? [name.split("/").at(-1)] : Object.keys(bin ?? {});
|
|
4725
|
-
lines.push("```bash");
|
|
4726
|
-
for (const cmd of commands) lines.push(`${cmd} --help`);
|
|
4727
|
-
lines.push("```");
|
|
4728
|
-
} else if (isReact) lines.push("```tsx", `import { } from "${name}";`, "", "function App() {", ` return <></>;`, "}", "```");
|
|
4729
|
-
else lines.push("```typescript", `import { } from "${name}";`, "```");
|
|
4730
|
-
return lines.join("\n");
|
|
4731
|
-
}
|
|
4732
|
-
async function updateReadmeInstallation(repoRoot, block, dryRun, readFileFn, writeFileFn) {
|
|
4733
|
-
const readmePath = join(repoRoot, "README.md");
|
|
4734
|
-
let content;
|
|
4735
|
-
try {
|
|
4736
|
-
content = await readFileFn(readmePath, "utf8");
|
|
4737
|
-
} catch {
|
|
4738
|
-
return false;
|
|
4739
|
-
}
|
|
4740
|
-
const lines = content.split("\n");
|
|
4741
|
-
const startIdx = lines.findIndex((l) => l.trim() === README_INSTALL_START);
|
|
4742
|
-
const endIdx = lines.findIndex((l) => l.trim() === README_INSTALL_END);
|
|
4743
|
-
const blockLines = block.split("\n");
|
|
4744
|
-
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
4745
|
-
if (dryRun) return true;
|
|
4746
|
-
lines.splice(startIdx + 1, endIdx - startIdx - 1, "", ...blockLines, "");
|
|
4747
|
-
await writeFileFn(readmePath, lines.join("\n"), "utf8");
|
|
4748
|
-
return true;
|
|
4749
|
-
}
|
|
4750
|
-
const descEnd = lines.findIndex((l) => l.trim() === "<!-- /holocron:description -->");
|
|
4751
|
-
const insertAfter = descEnd !== -1 ? descEnd : lines.findIndex((l) => /^# /.test(l));
|
|
4752
|
-
if (insertAfter === -1) return false;
|
|
4753
|
-
if (dryRun) return true;
|
|
4754
|
-
lines.splice(insertAfter + 1, 0, "", README_INSTALL_START, "", ...blockLines, "", README_INSTALL_END);
|
|
4755
|
-
await writeFileFn(readmePath, lines.join("\n"), "utf8");
|
|
4756
|
-
return true;
|
|
4757
|
-
}
|
|
4758
|
-
async function runSyncReadme(input) {
|
|
4759
|
-
const { loaded, context, print = console.log, readFileFn = readFile, writeFileFn = writeFile } = input;
|
|
4760
|
-
const { repoRoot, dryRun = false } = context;
|
|
4761
|
-
const namespaces = loaded.resolved.env?.namespaces ?? [];
|
|
4762
|
-
print(style.header(`Syncing README installation block${dryRun ? " (dry-run)" : ""}…`));
|
|
4763
|
-
if (namespaces.length > 0) print(style.hint(` namespaces: ${namespaces.join(", ")}`));
|
|
4764
|
-
let pkg;
|
|
4765
|
-
try {
|
|
4766
|
-
const raw = await readFileFn(join(repoRoot, "package.json"), "utf8");
|
|
4767
|
-
pkg = JSON.parse(raw);
|
|
4768
|
-
} catch {
|
|
4769
|
-
return {
|
|
4770
|
-
status: "fail",
|
|
4771
|
-
updated: false,
|
|
4772
|
-
message: "Could not read package.json"
|
|
4773
|
-
};
|
|
4774
|
-
}
|
|
4775
|
-
const updated = await updateReadmeInstallation(repoRoot, generateInstallBlock(pkg), dryRun, readFileFn, writeFileFn);
|
|
4776
|
-
if (!updated) {
|
|
4777
|
-
print(style.warn("README.md not found or has no writable location for installation block"));
|
|
4778
|
-
return {
|
|
4779
|
-
status: "fail",
|
|
4780
|
-
updated: false,
|
|
4781
|
-
message: "README.md update failed"
|
|
4782
|
-
};
|
|
4783
|
-
}
|
|
4784
|
-
print(dryRun ? style.hint(" would update README.md") : style.success(" updated README.md"));
|
|
4785
|
-
return {
|
|
4786
|
-
status: dryRun ? "dry-run" : "ok",
|
|
4787
|
-
updated
|
|
4788
|
-
};
|
|
4789
|
-
}
|
|
4790
|
-
//#endregion
|
|
4791
4886
|
//#region src/commands/upgrade-node.ts
|
|
4792
4887
|
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
4793
4888
|
"node_modules",
|
|
@@ -5527,10 +5622,10 @@ try {
|
|
|
5527
5622
|
dryRun: argv.dryRun,
|
|
5528
5623
|
...argv.otp ? { otp: argv.otp } : {}
|
|
5529
5624
|
})).status === "fail") process.exitCode = 1;
|
|
5530
|
-
}).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("sync [steps..]", "Sync state from config to the provider and local files (labels, properties, topics, keywords, description)", (y) => y.positional("steps", {
|
|
5625
|
+
}).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("sync [steps..]", "Sync state from config to the provider and local files (labels, properties, topics, keywords, description, homepage, readme)", (y) => y.positional("steps", {
|
|
5531
5626
|
type: "string",
|
|
5532
5627
|
array: true,
|
|
5533
|
-
describe: "Steps to run: labels, properties, topics, keywords, description (default: all)"
|
|
5628
|
+
describe: "Steps to run: labels, properties, topics, keywords, description, homepage, readme (default: all)"
|
|
5534
5629
|
}).option("repo", {
|
|
5535
5630
|
type: "string",
|
|
5536
5631
|
describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
|