@theholocron/cli 2.0.0-alpha.8 → 2.0.0-alpha.9
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 +140 -7
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1130,6 +1130,78 @@ jobs:
|
|
|
1130
1130
|
name: Upload results to Codecov
|
|
1131
1131
|
with:
|
|
1132
1132
|
token: \${{ secrets.CODECOV_TOKEN }}
|
|
1133
|
+
`,
|
|
1134
|
+
"sync-github": `\
|
|
1135
|
+
name: Sync GitHub Templates
|
|
1136
|
+
|
|
1137
|
+
# Builds the holocron CLI from source and pushes updated workflow templates
|
|
1138
|
+
# and composite actions to downstream .github repos. Runs whenever the
|
|
1139
|
+
# template source files change on main or alpha.
|
|
1140
|
+
#
|
|
1141
|
+
# Secrets required:
|
|
1142
|
+
# SYNC_TOKEN — fine-grained PAT or GitHub App token with Contents write
|
|
1143
|
+
# access to the primary and secondary repos.
|
|
1144
|
+
|
|
1145
|
+
on: # yamllint disable-line rule:truthy
|
|
1146
|
+
workflow_call:
|
|
1147
|
+
inputs:
|
|
1148
|
+
primary-repo:
|
|
1149
|
+
description: >
|
|
1150
|
+
Primary .github repo — receives composite actions, reusable workflows,
|
|
1151
|
+
and thin-caller templates. Requires a PR (branch protection assumed).
|
|
1152
|
+
type: string
|
|
1153
|
+
required: false
|
|
1154
|
+
default: theholocron/.github
|
|
1155
|
+
secondary-repos:
|
|
1156
|
+
description: >
|
|
1157
|
+
Space-separated list of secondary repos (reusable workflows + thin
|
|
1158
|
+
callers only, no composite actions). Pushed directly to main.
|
|
1159
|
+
type: string
|
|
1160
|
+
required: false
|
|
1161
|
+
default: ""
|
|
1162
|
+
sync-branch:
|
|
1163
|
+
description: Branch name used for the primary-repo PR
|
|
1164
|
+
type: string
|
|
1165
|
+
required: false
|
|
1166
|
+
default: chore/sync-templates
|
|
1167
|
+
secrets:
|
|
1168
|
+
SYNC_TOKEN:
|
|
1169
|
+
required: true
|
|
1170
|
+
|
|
1171
|
+
jobs:
|
|
1172
|
+
sync:
|
|
1173
|
+
name: Sync templates
|
|
1174
|
+
runs-on: ubuntu-latest
|
|
1175
|
+
timeout-minutes: 15
|
|
1176
|
+
permissions:
|
|
1177
|
+
contents: read
|
|
1178
|
+
steps:
|
|
1179
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
1180
|
+
name: Checkout repository
|
|
1181
|
+
|
|
1182
|
+
- uses: theholocron/.github/.github/actions/setup@main
|
|
1183
|
+
name: Setup
|
|
1184
|
+
|
|
1185
|
+
- run: pnpm build
|
|
1186
|
+
name: Build CLI
|
|
1187
|
+
|
|
1188
|
+
- name: Sync primary repo (PR)
|
|
1189
|
+
run: >
|
|
1190
|
+
node packages/cli/dist/cli.mjs sync-github
|
|
1191
|
+
--repo \${{ inputs.primary-repo }}
|
|
1192
|
+
--branch \${{ inputs.sync-branch }}
|
|
1193
|
+
--pr
|
|
1194
|
+
env:
|
|
1195
|
+
GITHUB_TOKEN: \${{ secrets.SYNC_TOKEN }}
|
|
1196
|
+
|
|
1197
|
+
- name: Sync secondary repos (direct push)
|
|
1198
|
+
if: \${{ inputs.secondary-repos != '' }}
|
|
1199
|
+
run: |
|
|
1200
|
+
for repo in \${{ inputs.secondary-repos }}; do
|
|
1201
|
+
node packages/cli/dist/cli.mjs sync-github --repo "$repo"
|
|
1202
|
+
done
|
|
1203
|
+
env:
|
|
1204
|
+
GITHUB_TOKEN: \${{ secrets.SYNC_TOKEN }}
|
|
1133
1205
|
`,
|
|
1134
1206
|
typecheck: `\
|
|
1135
1207
|
name: Typecheck
|
|
@@ -1393,6 +1465,32 @@ jobs:
|
|
|
1393
1465
|
audit:
|
|
1394
1466
|
uses: ${ref("audit")}
|
|
1395
1467
|
secrets: inherit
|
|
1468
|
+
`,
|
|
1469
|
+
"sync-github": `\
|
|
1470
|
+
name: Sync GitHub Templates
|
|
1471
|
+
|
|
1472
|
+
on: # yamllint disable-line rule:truthy
|
|
1473
|
+
push:
|
|
1474
|
+
branches: [main, alpha]
|
|
1475
|
+
paths:
|
|
1476
|
+
- packages/cli/src/templates/index.ts
|
|
1477
|
+
- packages/cli/src/commands/setup-workflows.ts
|
|
1478
|
+
|
|
1479
|
+
concurrency:
|
|
1480
|
+
group: sync-github-\${{ github.ref }}
|
|
1481
|
+
cancel-in-progress: true
|
|
1482
|
+
|
|
1483
|
+
permissions:
|
|
1484
|
+
contents: read
|
|
1485
|
+
|
|
1486
|
+
jobs:
|
|
1487
|
+
sync:
|
|
1488
|
+
name: Sync
|
|
1489
|
+
uses: ${ref("sync-github")}
|
|
1490
|
+
with:
|
|
1491
|
+
secondary-repos: theholocron/.github-private
|
|
1492
|
+
secrets:
|
|
1493
|
+
SYNC_TOKEN: \${{ secrets.SYNC_TOKEN }}
|
|
1396
1494
|
`
|
|
1397
1495
|
};
|
|
1398
1496
|
const KNOWN_WORKFLOWS = new Set(Object.keys(WORKFLOW_TEMPLATES));
|
|
@@ -1420,9 +1518,9 @@ function thinCallerHeader() {
|
|
|
1420
1518
|
``
|
|
1421
1519
|
].join("\n");
|
|
1422
1520
|
}
|
|
1423
|
-
function buildBatch() {
|
|
1521
|
+
function buildBatch(repo) {
|
|
1424
1522
|
const files = [];
|
|
1425
|
-
for (const [name, content] of Object.entries(ACTIONS)) files.push({
|
|
1523
|
+
if (repo === DEFAULT_REPO) for (const [name, content] of Object.entries(ACTIONS)) files.push({
|
|
1426
1524
|
path: `.github/actions/${name}.yml`,
|
|
1427
1525
|
content: reusableHeader(`packages/cli/src/templates/index.ts`) + content
|
|
1428
1526
|
});
|
|
@@ -1440,7 +1538,7 @@ async function runSyncGithub(input) {
|
|
|
1440
1538
|
const print = input.print ?? ((line) => console.log(line));
|
|
1441
1539
|
const repo = input.repo ?? DEFAULT_REPO;
|
|
1442
1540
|
const [owner, repoName] = repo.split("/");
|
|
1443
|
-
const { token, dryRun = false } = input;
|
|
1541
|
+
const { token, dryRun = false, branch, createPr = false } = input;
|
|
1444
1542
|
const message = input.message ?? `chore: sync from theholocron/holocron`;
|
|
1445
1543
|
const fetchFn = input.fetch ?? globalThis.fetch;
|
|
1446
1544
|
const headers = {
|
|
@@ -1450,19 +1548,21 @@ async function runSyncGithub(input) {
|
|
|
1450
1548
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
1451
1549
|
};
|
|
1452
1550
|
print(`holocron sync-github${dryRun ? " (dry-run)" : ""}`);
|
|
1453
|
-
print(` repo:
|
|
1551
|
+
print(` repo: ${repo}`);
|
|
1552
|
+
if (branch) print(` branch: ${branch}`);
|
|
1454
1553
|
print("");
|
|
1455
|
-
const batch = buildBatch();
|
|
1554
|
+
const batch = buildBatch(repo);
|
|
1456
1555
|
let created = 0;
|
|
1457
1556
|
let updated = 0;
|
|
1458
1557
|
let unchanged = 0;
|
|
1459
1558
|
for (const file of batch) {
|
|
1559
|
+
const ref = branch ? `?ref=${encodeURIComponent(branch)}` : "";
|
|
1460
1560
|
const url = `${API_BASE}/repos/${owner}/${repoName}/contents/${file.path}`;
|
|
1461
1561
|
const newContent = Buffer.from(file.content, "utf8").toString("base64");
|
|
1462
1562
|
let existingSha;
|
|
1463
1563
|
let existingContent;
|
|
1464
1564
|
try {
|
|
1465
|
-
const getRes = await fetchFn(url
|
|
1565
|
+
const getRes = await fetchFn(`${url}${ref}`, { headers });
|
|
1466
1566
|
if (getRes.ok) {
|
|
1467
1567
|
const data = await getRes.json();
|
|
1468
1568
|
existingSha = data.sha;
|
|
@@ -1486,6 +1586,7 @@ async function runSyncGithub(input) {
|
|
|
1486
1586
|
content: newContent
|
|
1487
1587
|
};
|
|
1488
1588
|
if (existingSha) body.sha = existingSha;
|
|
1589
|
+
if (branch) body.branch = branch;
|
|
1489
1590
|
const putRes = await fetchFn(url, {
|
|
1490
1591
|
method: "PUT",
|
|
1491
1592
|
headers,
|
|
@@ -1507,13 +1608,36 @@ async function runSyncGithub(input) {
|
|
|
1507
1608
|
if (existingSha) updated++;
|
|
1508
1609
|
else created++;
|
|
1509
1610
|
}
|
|
1611
|
+
const changed = created + updated;
|
|
1510
1612
|
print("");
|
|
1511
1613
|
print(` ${created} created, ${updated} updated, ${unchanged} unchanged`);
|
|
1614
|
+
let prUrl;
|
|
1615
|
+
if (branch && createPr && changed > 0 && !dryRun) {
|
|
1616
|
+
const prRes = await fetchFn(`${API_BASE}/repos/${owner}/${repoName}/pulls`, {
|
|
1617
|
+
method: "POST",
|
|
1618
|
+
headers,
|
|
1619
|
+
body: JSON.stringify({
|
|
1620
|
+
title: message,
|
|
1621
|
+
head: branch,
|
|
1622
|
+
base: "main",
|
|
1623
|
+
body: "Auto-generated by `holocron sync-github`. Review and merge to apply template updates."
|
|
1624
|
+
})
|
|
1625
|
+
});
|
|
1626
|
+
if (prRes.ok) {
|
|
1627
|
+
prUrl = (await prRes.json()).html_url;
|
|
1628
|
+
print(` → PR opened: ${prUrl}`);
|
|
1629
|
+
} else {
|
|
1630
|
+
const err = await prRes.json();
|
|
1631
|
+
if (err.errors?.some((e) => e.message.includes("already exists"))) print(` → PR already open for ${branch} — branch updated, ready to merge`);
|
|
1632
|
+
else print(` ⚠ PR creation failed: ${err.message ?? prRes.status}`);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1512
1635
|
return {
|
|
1513
1636
|
status: dryRun ? "dry-run" : "ok",
|
|
1514
1637
|
created,
|
|
1515
1638
|
updated,
|
|
1516
|
-
unchanged
|
|
1639
|
+
unchanged,
|
|
1640
|
+
prUrl
|
|
1517
1641
|
};
|
|
1518
1642
|
}
|
|
1519
1643
|
//#endregion
|
|
@@ -3493,6 +3617,13 @@ await yargs(hideBin(process.argv)).scriptName("holocron").usage("$0 <command> [o
|
|
|
3493
3617
|
type: "string",
|
|
3494
3618
|
default: "theholocron/.github",
|
|
3495
3619
|
describe: "Target org/repo (default: theholocron/.github)"
|
|
3620
|
+
}).option("branch", {
|
|
3621
|
+
type: "string",
|
|
3622
|
+
describe: "Push to this branch instead of the default branch (enables PR-based workflow for protected repos)"
|
|
3623
|
+
}).option("pr", {
|
|
3624
|
+
type: "boolean",
|
|
3625
|
+
default: false,
|
|
3626
|
+
describe: "Open a PR after pushing to --branch (no-op without --branch)"
|
|
3496
3627
|
}).option("message", {
|
|
3497
3628
|
type: "string",
|
|
3498
3629
|
describe: "Commit message (default: chore: sync from theholocron/holocron)"
|
|
@@ -3507,6 +3638,8 @@ await yargs(hideBin(process.argv)).scriptName("holocron").usage("$0 <command> [o
|
|
|
3507
3638
|
token,
|
|
3508
3639
|
repo: argv.repo,
|
|
3509
3640
|
dryRun: argv.dryRun,
|
|
3641
|
+
...argv.branch ? { branch: argv.branch } : {},
|
|
3642
|
+
...argv.pr ? { createPr: true } : {},
|
|
3510
3643
|
...argv.message ? { message: argv.message } : {}
|
|
3511
3644
|
})).status === "fail") process.exitCode = 1;
|
|
3512
3645
|
}).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.9",
|
|
4
4
|
"description": "The Holocron CLI — a pluggable, capability-based orchestrator for spinning up and operating software projects.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/cli#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|