@theholocron/cli 4.18.3 → 5.0.0-alpha.3
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 +325 -47
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +24 -11
- package/package.json +8 -9
package/dist/cli.mjs
CHANGED
|
@@ -2154,12 +2154,11 @@ function render$10(inputs) {
|
|
|
2154
2154
|
main: "./src/index.ts",
|
|
2155
2155
|
exports: { ".": "./src/index.ts" },
|
|
2156
2156
|
scripts: {
|
|
2157
|
-
build: "tsdown",
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2157
|
+
"delivery.build": "tsdown",
|
|
2158
|
+
"sourceQuality.staticAnalysis": "holocron run sourceQuality.staticAnalysis --",
|
|
2159
|
+
"verification.typeSafety": "holocron run verification.typeSafety --",
|
|
2160
|
+
"verification.unitTests": "holocron run verification.unitTests --",
|
|
2161
2161
|
"test:watch": "vitest",
|
|
2162
|
-
"test:coverage": "vitest run --coverage",
|
|
2163
2162
|
validate: "tsx scripts/validate.mjs"
|
|
2164
2163
|
},
|
|
2165
2164
|
peerDependencies: { "@theholocron/cli": "workspace:*" },
|
|
@@ -2879,7 +2878,7 @@ function runPluginCreate(input) {
|
|
|
2879
2878
|
execFn("pnpm", [
|
|
2880
2879
|
"--filter",
|
|
2881
2880
|
pkg,
|
|
2882
|
-
"
|
|
2881
|
+
"verification.typeSafety"
|
|
2883
2882
|
], {
|
|
2884
2883
|
cwd,
|
|
2885
2884
|
stdio: "inherit"
|
|
@@ -2887,7 +2886,7 @@ function runPluginCreate(input) {
|
|
|
2887
2886
|
execFn("pnpm", [
|
|
2888
2887
|
"--filter",
|
|
2889
2888
|
pkg,
|
|
2890
|
-
"
|
|
2889
|
+
"sourceQuality.staticAnalysis"
|
|
2891
2890
|
], {
|
|
2892
2891
|
cwd,
|
|
2893
2892
|
stdio: "inherit"
|
|
@@ -2895,7 +2894,7 @@ function runPluginCreate(input) {
|
|
|
2895
2894
|
execFn("pnpm", [
|
|
2896
2895
|
"--filter",
|
|
2897
2896
|
pkg,
|
|
2898
|
-
"
|
|
2897
|
+
"verification.unitTests"
|
|
2899
2898
|
], {
|
|
2900
2899
|
cwd,
|
|
2901
2900
|
stdio: "inherit"
|
|
@@ -3018,6 +3017,193 @@ function printNextSteps$1(print, inputs) {
|
|
|
3018
3017
|
* "revoke this token at <url>" reminder — same pattern as `rando vc
|
|
3019
3018
|
* setup` for the ephemeral GH admin PAT.
|
|
3020
3019
|
*/
|
|
3020
|
+
/**
|
|
3021
|
+
* `holocron publish --sync-trust <old-file> <new-file>` — bulk-migrates npm
|
|
3022
|
+
* Trusted Publisher (OIDC) config to a new workflow filename across every
|
|
3023
|
+
* public package. See issue #689 / `scripts/npm-trust-migrate.sh` (the
|
|
3024
|
+
* shell-script prototype this supersedes) for the full "why": npm ties
|
|
3025
|
+
* Trusted Publisher to a workflow FILENAME per package, so renaming the
|
|
3026
|
+
* release workflow breaks OIDC publish (`ENEEDAUTH`) until every package's
|
|
3027
|
+
* config is updated to match.
|
|
3028
|
+
*
|
|
3029
|
+
* Requires npm@11.15.0+ (`npm trust`) — always shells out through
|
|
3030
|
+
* `npx -y npm@latest` regardless of the locally-installed npm version.
|
|
3031
|
+
*
|
|
3032
|
+
* npm's `trust` mutations — and reads, once a short-lived OTP grant lapses —
|
|
3033
|
+
* require a fresh interactive browser 2FA approval (an `EOTP` error with an
|
|
3034
|
+
* `authUrl`). No token bypasses this (npm's own docs: Granular Access Tokens
|
|
3035
|
+
* with the bypass-2FA option are explicitly unsupported for trust commands).
|
|
3036
|
+
* This command can't drive that browser step itself, so on the first `EOTP`
|
|
3037
|
+
* it stops immediately, prints the `authUrl`, and returns — the operator
|
|
3038
|
+
* approves it and re-runs; already-migrated packages are skipped on the next
|
|
3039
|
+
* pass, so it's safe to resume from wherever it stopped.
|
|
3040
|
+
*/
|
|
3041
|
+
async function runSyncTrust(input) {
|
|
3042
|
+
const print = input.print ?? ((line) => console.log(line));
|
|
3043
|
+
const logger = input.logger ?? getLogger();
|
|
3044
|
+
const cwd = input.cwd ?? process.cwd();
|
|
3045
|
+
const repo = input.repo ?? "theholocron/holocron";
|
|
3046
|
+
const dryRun = input.dryRun ?? false;
|
|
3047
|
+
const exec = input.exec ?? defaultExec$1;
|
|
3048
|
+
const packageNames = input.packages ?? discoverPublicPackages(cwd);
|
|
3049
|
+
const npm = "npx";
|
|
3050
|
+
const npmArgs = ["-y", "npm@latest"];
|
|
3051
|
+
print(`Holocron publish --sync-trust${dryRun ? " (dry-run)" : ""}`);
|
|
3052
|
+
print(` ${input.oldFile} -> ${input.newFile} (repo: ${repo})`);
|
|
3053
|
+
print(` ${packageNames.length} public packages`);
|
|
3054
|
+
print("");
|
|
3055
|
+
logger.info({
|
|
3056
|
+
oldFile: input.oldFile,
|
|
3057
|
+
newFile: input.newFile,
|
|
3058
|
+
packages: packageNames.length
|
|
3059
|
+
}, "sync-trust: start");
|
|
3060
|
+
if (packageNames.length === 0) {
|
|
3061
|
+
const message = "nothing to sync (no packages/* and root package.json is private or unnamed)";
|
|
3062
|
+
print(` ✗ ${message}`);
|
|
3063
|
+
return {
|
|
3064
|
+
status: "fail",
|
|
3065
|
+
message,
|
|
3066
|
+
packages: []
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
if (dryRun) {
|
|
3070
|
+
for (const name of packageNames) print(` … would sync ${name}`);
|
|
3071
|
+
return {
|
|
3072
|
+
status: "dry-run",
|
|
3073
|
+
message: "dry-run — no npm calls made",
|
|
3074
|
+
packages: []
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
const results = [];
|
|
3078
|
+
for (const name of packageNames) {
|
|
3079
|
+
print(`=== ${name} ===`);
|
|
3080
|
+
const parsed = parseJson((await exec(npm, [
|
|
3081
|
+
...npmArgs,
|
|
3082
|
+
"trust",
|
|
3083
|
+
"list",
|
|
3084
|
+
name,
|
|
3085
|
+
"--json"
|
|
3086
|
+
], { cwd })).stdout);
|
|
3087
|
+
if (isEotp(parsed)) {
|
|
3088
|
+
const authUrl = parsed.error?.authUrl ?? "";
|
|
3089
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3090
|
+
print(" then re-run; already-migrated packages are skipped.");
|
|
3091
|
+
results.push({
|
|
3092
|
+
name,
|
|
3093
|
+
status: "needs-auth",
|
|
3094
|
+
message: authUrl
|
|
3095
|
+
});
|
|
3096
|
+
logger.warn({
|
|
3097
|
+
package: name,
|
|
3098
|
+
authUrl
|
|
3099
|
+
}, "sync-trust: needs auth, stopping");
|
|
3100
|
+
return {
|
|
3101
|
+
status: "fail",
|
|
3102
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3103
|
+
packages: results
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
const entry = parsed;
|
|
3107
|
+
if (entry.file === input.newFile) {
|
|
3108
|
+
print(` already on ${input.newFile}, skipping`);
|
|
3109
|
+
results.push({
|
|
3110
|
+
name,
|
|
3111
|
+
status: "skipped"
|
|
3112
|
+
});
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
if (entry.id) {
|
|
3116
|
+
print(` revoking existing trust (file=${entry.file ?? "?"}, id=${entry.id})`);
|
|
3117
|
+
const revoke = await exec(npm, [
|
|
3118
|
+
...npmArgs,
|
|
3119
|
+
"trust",
|
|
3120
|
+
"revoke",
|
|
3121
|
+
name,
|
|
3122
|
+
"--id",
|
|
3123
|
+
entry.id,
|
|
3124
|
+
"-y"
|
|
3125
|
+
], { cwd });
|
|
3126
|
+
if (revoke.exitCode !== 0 && isEotp(parseJson(revoke.stdout))) {
|
|
3127
|
+
const authUrl = parseJson(revoke.stdout).error?.authUrl ?? "";
|
|
3128
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3129
|
+
results.push({
|
|
3130
|
+
name,
|
|
3131
|
+
status: "needs-auth",
|
|
3132
|
+
message: authUrl
|
|
3133
|
+
});
|
|
3134
|
+
return {
|
|
3135
|
+
status: "fail",
|
|
3136
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3137
|
+
packages: results
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
} else print(" no existing trust config found");
|
|
3141
|
+
print(` creating trust: ${input.newFile}`);
|
|
3142
|
+
const create = await exec(npm, [
|
|
3143
|
+
...npmArgs,
|
|
3144
|
+
"trust",
|
|
3145
|
+
"github",
|
|
3146
|
+
name,
|
|
3147
|
+
"--repo",
|
|
3148
|
+
repo,
|
|
3149
|
+
"--file",
|
|
3150
|
+
input.newFile,
|
|
3151
|
+
"--allow-publish",
|
|
3152
|
+
"--allow-stage-publish",
|
|
3153
|
+
"-y"
|
|
3154
|
+
], { cwd });
|
|
3155
|
+
if (create.exitCode === 0) {
|
|
3156
|
+
print(" ✓ ok");
|
|
3157
|
+
results.push({
|
|
3158
|
+
name,
|
|
3159
|
+
status: "ok"
|
|
3160
|
+
});
|
|
3161
|
+
} else {
|
|
3162
|
+
const createParsed = parseJson(create.stdout);
|
|
3163
|
+
if (isEotp(createParsed)) {
|
|
3164
|
+
const authUrl = createParsed.error?.authUrl ?? "";
|
|
3165
|
+
print(` needs fresh 2FA — open and approve: ${authUrl}`);
|
|
3166
|
+
results.push({
|
|
3167
|
+
name,
|
|
3168
|
+
status: "needs-auth",
|
|
3169
|
+
message: authUrl
|
|
3170
|
+
});
|
|
3171
|
+
return {
|
|
3172
|
+
status: "fail",
|
|
3173
|
+
message: "npm 2FA grant needed — see printed authUrl",
|
|
3174
|
+
packages: results
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
const message = create.stderr.trim() || create.stdout.trim() || `exit ${create.exitCode}`;
|
|
3178
|
+
print(` ✗ FAILED: ${message}`);
|
|
3179
|
+
results.push({
|
|
3180
|
+
name,
|
|
3181
|
+
status: "fail",
|
|
3182
|
+
message
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
const failed = results.filter((r) => r.status === "fail");
|
|
3187
|
+
logger.info({
|
|
3188
|
+
ok: results.filter((r) => r.status === "ok").length,
|
|
3189
|
+
failed: failed.length
|
|
3190
|
+
}, "sync-trust: done");
|
|
3191
|
+
return {
|
|
3192
|
+
status: failed.length > 0 ? "fail" : "ok",
|
|
3193
|
+
...failed.length > 0 ? { message: `${failed.length} package(s) failed` } : {},
|
|
3194
|
+
packages: results
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
function parseJson(raw) {
|
|
3198
|
+
try {
|
|
3199
|
+
return JSON.parse(raw);
|
|
3200
|
+
} catch {
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
function isEotp(parsed) {
|
|
3205
|
+
return typeof parsed === "object" && parsed !== null && "error" in parsed && parsed.error?.code === "EOTP";
|
|
3206
|
+
}
|
|
3021
3207
|
async function runPublish(input = {}) {
|
|
3022
3208
|
const print = input.print ?? ((line) => console.log(line));
|
|
3023
3209
|
const logger = input.logger ?? getLogger();
|
|
@@ -3180,7 +3366,7 @@ function printNextSteps(print, env, packageNames, repoName) {
|
|
|
3180
3366
|
print("");
|
|
3181
3367
|
print(" → next: configure Trusted Publisher for each package on npm:");
|
|
3182
3368
|
for (const name of packageNames) print(` https://www.npmjs.com/package/${name}/access`);
|
|
3183
|
-
print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow:
|
|
3369
|
+
print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow: delivery.publish.yml`);
|
|
3184
3370
|
if (env.get("NPM_TOKEN")) {
|
|
3185
3371
|
print("");
|
|
3186
3372
|
print(" → cleanup: $NPM_TOKEN was used. Revoke it now (no API for self-revoke; UI-only):");
|
|
@@ -3563,19 +3749,19 @@ var alexrc_default = { allow: [
|
|
|
3563
3749
|
] };
|
|
3564
3750
|
//#endregion
|
|
3565
3751
|
//#region src/templates/configs/alexjs/create-config.ts
|
|
3566
|
-
const { workflowHeader: workflowHeader$
|
|
3752
|
+
const { workflowHeader: workflowHeader$8 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
|
|
3567
3753
|
function createRcConfig() {
|
|
3568
3754
|
return JSON.stringify(alexrc_default, null, 2) + "\n";
|
|
3569
3755
|
}
|
|
3570
|
-
function createIgnoreConfig() {
|
|
3571
|
-
return `${workflowHeader$
|
|
3756
|
+
function createIgnoreConfig$1() {
|
|
3757
|
+
return `${workflowHeader$8()}${alexignore_default}`;
|
|
3572
3758
|
}
|
|
3573
3759
|
//#endregion
|
|
3574
3760
|
//#region src/templates/configs/devmoji/create-config.ts
|
|
3575
|
-
const { workflowHeader: workflowHeader$
|
|
3576
|
-
function createConfig$
|
|
3761
|
+
const { workflowHeader: workflowHeader$7 } = createHeader({ source: "packages/cli/src/templates/configs/devmoji/create-config.ts" });
|
|
3762
|
+
function createConfig$6() {
|
|
3577
3763
|
return [
|
|
3578
|
-
workflowHeader$
|
|
3764
|
+
workflowHeader$7("cjs"),
|
|
3579
3765
|
`/* eslint-disable */`,
|
|
3580
3766
|
`const { defineConfig } = require("@theholocron/devmoji-config");`,
|
|
3581
3767
|
`module.exports = defineConfig();`,
|
|
@@ -3587,9 +3773,9 @@ function createConfig$5() {
|
|
|
3587
3773
|
var editorconfig_default = "root = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = tab\nindent_size = 4\n\n[.gitattributes]\nindent_style = space\nindent_size = 2\n\n[*.{json,yml,yaml}]\nindent_style = space\nindent_size = 2\n\n[*.{md,mdx}]\ntrim_trailing_whitespace = false\n\n[.*{rc,ignore}]\nindent_style = space\nindent_size = 2\n";
|
|
3588
3774
|
//#endregion
|
|
3589
3775
|
//#region src/templates/configs/editorconfig/create-config.ts
|
|
3590
|
-
const { workflowHeader: workflowHeader$
|
|
3591
|
-
function createConfig$
|
|
3592
|
-
return `${workflowHeader$
|
|
3776
|
+
const { workflowHeader: workflowHeader$6 } = createHeader({ source: "packages/cli/src/templates/configs/editorconfig/create-config.ts" });
|
|
3777
|
+
function createConfig$5() {
|
|
3778
|
+
return `${workflowHeader$6()}${editorconfig_default}`;
|
|
3593
3779
|
}
|
|
3594
3780
|
//#endregion
|
|
3595
3781
|
//#region src/templates/configs/editorconfig-checker/editorconfig-checker.json
|
|
@@ -3630,7 +3816,7 @@ var editorconfig_checker_default = {
|
|
|
3630
3816
|
* options here (`Exclude`, `Disable`) are stable across releases, so pinning
|
|
3631
3817
|
* buys nothing.
|
|
3632
3818
|
*/
|
|
3633
|
-
function createConfig$
|
|
3819
|
+
function createConfig$4() {
|
|
3634
3820
|
return JSON.stringify(editorconfig_checker_default, null, 2) + "\n";
|
|
3635
3821
|
}
|
|
3636
3822
|
//#endregion
|
|
@@ -3638,7 +3824,7 @@ function createConfig$3() {
|
|
|
3638
3824
|
var pre_commit_default = "# Run GitLeaks against staged changes only — a full `gitleaks git` history\n# scan re-flags every pre-existing (already-merged) finding on every commit,\n# permanently blocking the repo once any false positive lands in history.\n# CI's super-linter GITLEAKS validator is likewise scoped to changed files.\ngitleaks protect --staged --no-banner\n\n# Exit with non-zero if secrets are detected by GitLeaks\nif [ $? -ne 0 ]; then\n echo \"Secrets detected by GitLeaks, aborting commit!\"\n exit 1\nfi\n\n# Run lint-staged with a custom config\nnpx lint-staged --config @theholocron/lint-staged-config\n";
|
|
3639
3825
|
//#endregion
|
|
3640
3826
|
//#region src/templates/configs/pre-commit/create-config.ts
|
|
3641
|
-
const { workflowHeader: workflowHeader$
|
|
3827
|
+
const { workflowHeader: workflowHeader$5 } = createHeader({ source: "packages/cli/src/templates/configs/pre-commit/create-config.ts" });
|
|
3642
3828
|
/**
|
|
3643
3829
|
* `.husky/pre-commit` — runs GitLeaks (scoped to staged changes) and
|
|
3644
3830
|
* lint-staged before every commit. Installed by `holocron setup` when hooks
|
|
@@ -3651,15 +3837,15 @@ const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cl
|
|
|
3651
3837
|
* doc — every future commit is permanently blocked, regardless of what's
|
|
3652
3838
|
* actually staged.
|
|
3653
3839
|
*/
|
|
3654
|
-
function createConfig$
|
|
3655
|
-
return `${workflowHeader$
|
|
3840
|
+
function createConfig$3() {
|
|
3841
|
+
return `${workflowHeader$5("shebang")}${pre_commit_default}`;
|
|
3656
3842
|
}
|
|
3657
3843
|
//#endregion
|
|
3658
3844
|
//#region src/templates/configs/pre-push/pre-push
|
|
3659
3845
|
var pre_push_default = "# Run the merge-gating checks locally before pushing — the same set CI runs.\n# Bypass once with: git push --no-verify\n\n# CI runners (semantic-release, super-linter auto-commit, bots) push without a\n# built dev environment — the hook has nothing to add there.\n[ -n \"$CI\" ] && exit 0\n\n__HOLOCRON_SCRIPT__ ci\n";
|
|
3660
3846
|
//#endregion
|
|
3661
3847
|
//#region src/templates/configs/pre-push/create-config.ts
|
|
3662
|
-
const { workflowHeader: workflowHeader$
|
|
3848
|
+
const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cli/src/templates/configs/pre-push/create-config.ts" });
|
|
3663
3849
|
/**
|
|
3664
3850
|
* `.husky/pre-push` — runs `holocron ci` (the merge-gating checks, in CI order)
|
|
3665
3851
|
* before every push. Installed by `holocron setup` when hooks are enabled
|
|
@@ -3668,17 +3854,32 @@ const { workflowHeader: workflowHeader$3 } = createHeader({ source: "packages/cl
|
|
|
3668
3854
|
* @param holocronScript - the command that invokes the CLI. Defaults to
|
|
3669
3855
|
* `pnpm exec holocron`; the source repo passes `node packages/cli/dist/cli.mjs`.
|
|
3670
3856
|
*/
|
|
3671
|
-
function createConfig$
|
|
3672
|
-
return `${workflowHeader$
|
|
3857
|
+
function createConfig$2(holocronScript = "pnpm exec holocron") {
|
|
3858
|
+
return `${workflowHeader$4("shebang")}${pre_push_default.replace("__HOLOCRON_SCRIPT__", holocronScript)}`;
|
|
3673
3859
|
}
|
|
3674
3860
|
//#endregion
|
|
3675
3861
|
//#region src/templates/configs/prepare-commit-msg/prepare-commit-msg
|
|
3676
3862
|
var prepare_commit_msg_default = "NAME=$(git config user.name)\nEMAIL=$(git config user.email)\n\nif [ -z \"$NAME\" ]; then\n echo \"empty git config user.name\"\n exit 1\nfi\n\nif [ -z \"$EMAIL\" ]; then\n echo \"empty git config user.email\"\n exit 1\nfi\n\ngit interpret-trailers --if-exists doNothing --trailer \\\n \"Signed-off-by: $NAME <$EMAIL>\" \\\n --in-place \"$1\"\n\nnpx devmoji -e\n";
|
|
3677
3863
|
//#endregion
|
|
3678
3864
|
//#region src/templates/configs/prepare-commit-msg/create-config.ts
|
|
3679
|
-
const { workflowHeader: workflowHeader$
|
|
3865
|
+
const { workflowHeader: workflowHeader$3 } = createHeader({ source: "packages/cli/src/templates/configs/prepare-commit-msg/create-config.ts" });
|
|
3866
|
+
function createConfig$1() {
|
|
3867
|
+
return `${workflowHeader$3("shebang")}${prepare_commit_msg_default}`;
|
|
3868
|
+
}
|
|
3869
|
+
//#endregion
|
|
3870
|
+
//#region src/templates/configs/yamllint/.yamlignore
|
|
3871
|
+
var _yamlignore_default = "pnpm-lock.yaml\n";
|
|
3872
|
+
//#endregion
|
|
3873
|
+
//#region src/templates/configs/yamllint/.yamllint.yml
|
|
3874
|
+
var _yamllint_default = "---\nextends: default\n\nignore-from-file:\n - .gitignore\n - .yamlignore\n\nrules:\n comments:\n min-spaces-from-content: 1\n require-starting-space: true\n\n comments-indentation: {}\n\n document-start:\n ignore: |\n .github/*.yml\n\n indentation:\n spaces: consistent\n indent-sequences: consistent\n\n line-length: disable\n";
|
|
3875
|
+
//#endregion
|
|
3876
|
+
//#region src/templates/configs/yamllint/create-config.ts
|
|
3877
|
+
const { workflowHeader: workflowHeader$2 } = createHeader({ source: "packages/cli/src/templates/configs/yamllint/create-config.ts" });
|
|
3680
3878
|
function createConfig() {
|
|
3681
|
-
return `${workflowHeader$2(
|
|
3879
|
+
return `${workflowHeader$2()}${_yamllint_default}`;
|
|
3880
|
+
}
|
|
3881
|
+
function createIgnoreConfig() {
|
|
3882
|
+
return `${workflowHeader$2()}${_yamlignore_default}`;
|
|
3682
3883
|
}
|
|
3683
3884
|
//#endregion
|
|
3684
3885
|
//#region src/templates/dco.yml
|
|
@@ -3688,7 +3889,7 @@ var dco_default = "allowRemediationCommits:\n individual: true\n";
|
|
|
3688
3889
|
var dependabot_default = "version: 2\nupdates:\n - package-ecosystem: npm\n directory: /\n schedule:\n interval: weekly\n commit-message:\n prefix: \"chore(deps)\"\n prefix-development: \"chore(deps-dev)\"\n groups:\n security-patches:\n applies-to: security-updates\n patterns:\n - \"*\"\n all-dependencies:\n update-types:\n - minor\n - patch\n\n - package-ecosystem: github-actions\n directory: /\n schedule:\n interval: weekly\n commit-message:\n prefix: \"chore(deps)\"\n groups:\n all-actions:\n patterns:\n - \"*\"\n";
|
|
3689
3890
|
//#endregion
|
|
3690
3891
|
//#region src/templates/labeler.yml
|
|
3691
|
-
var labeler_default = "bug:\n -
|
|
3892
|
+
var labeler_default = "bug:\n - \"^fix\"\n\nchore:\n - '^chore(?!\\(deps)'\n\nci:\n - \"^ci\"\n\ndependencies:\n - '^chore\\(deps'\n\ndocumentation:\n - \"^docs\"\n\nenhancement:\n - \"^feat\"\n\nperformance:\n - \"^perf\"\n\nrefactor:\n - \"^refactor\"\n\ntest:\n - \"^test\"\n";
|
|
3692
3893
|
//#endregion
|
|
3693
3894
|
//#region src/commands/setup/agent-prompts-data.ts
|
|
3694
3895
|
/**
|
|
@@ -4503,6 +4704,10 @@ const { workflowHeader: workflowHeader$1 } = createHeader({
|
|
|
4503
4704
|
source: "packages/cli/src/commands/setup/run-setup.ts",
|
|
4504
4705
|
tool: "holocron setup"
|
|
4505
4706
|
});
|
|
4707
|
+
const { workflowHeader: thinCallerHeader } = createHeader({
|
|
4708
|
+
source: "packages/astromech/src/thin-callers.ts",
|
|
4709
|
+
tool: "holocron setup"
|
|
4710
|
+
});
|
|
4506
4711
|
async function runSetup(input) {
|
|
4507
4712
|
const print = input.print ?? ((line) => console.log(line));
|
|
4508
4713
|
const logger = getLogger();
|
|
@@ -4537,7 +4742,7 @@ async function runSetup(input) {
|
|
|
4537
4742
|
}, { skipCodes: SECURITY_SKIP_CODES[method] }));
|
|
4538
4743
|
print(formatStep(steps[steps.length - 1]));
|
|
4539
4744
|
}
|
|
4540
|
-
const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("
|
|
4745
|
+
const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("security.codeScanning");
|
|
4541
4746
|
steps.push(await runStep("source", usesAdvancedCodeQL ? "disableDefaultCodeScanning" : "enableCodeScanning", dryRun, async () => {
|
|
4542
4747
|
if (usesAdvancedCodeQL) await source.disableDefaultCodeScanning();
|
|
4543
4748
|
else return await source.enableCodeScanning();
|
|
@@ -4574,7 +4779,8 @@ async function runSetup(input) {
|
|
|
4574
4779
|
}).thinCallers();
|
|
4575
4780
|
for (const entry of tasks) {
|
|
4576
4781
|
const name = typeof entry === "string" ? entry : entry.name;
|
|
4577
|
-
|
|
4782
|
+
const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
|
|
4783
|
+
if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
|
|
4578
4784
|
steps.push({
|
|
4579
4785
|
capability: "source",
|
|
4580
4786
|
step: `write workflow ${name}`,
|
|
@@ -4584,12 +4790,12 @@ async function runSetup(input) {
|
|
|
4584
4790
|
print(formatStep(steps[steps.length - 1]));
|
|
4585
4791
|
continue;
|
|
4586
4792
|
}
|
|
4587
|
-
const filename =
|
|
4793
|
+
const filename = `${name}.yml`;
|
|
4588
4794
|
const content = files.get(filename);
|
|
4589
4795
|
if (content === void 0) continue;
|
|
4590
|
-
const step =
|
|
4796
|
+
const step = content.includes("workflows/preview.yml@main") ? `write workflow ${name} (with preview)` : `write workflow ${name}`;
|
|
4591
4797
|
steps.push(await runStep("source", step, dryRun, async () => {
|
|
4592
|
-
await source.writeWorkflowFile(filename, `${
|
|
4798
|
+
await source.writeWorkflowFile(filename, `${thinCallerHeader()}${content}`);
|
|
4593
4799
|
}));
|
|
4594
4800
|
print(formatStep(steps[steps.length - 1]));
|
|
4595
4801
|
}
|
|
@@ -4623,33 +4829,41 @@ async function runSetup(input) {
|
|
|
4623
4829
|
}));
|
|
4624
4830
|
print(formatStep(steps[steps.length - 1]));
|
|
4625
4831
|
steps.push(await runStep("source", "write .alexignore", dryRun, async () => {
|
|
4626
|
-
await source.writeRepoFile(".alexignore", createIgnoreConfig());
|
|
4832
|
+
await source.writeRepoFile(".alexignore", createIgnoreConfig$1());
|
|
4833
|
+
}));
|
|
4834
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4835
|
+
steps.push(await runStep("source", "write .yamllint.yml", dryRun, async () => {
|
|
4836
|
+
await source.writeRepoFile(".yamllint.yml", createConfig());
|
|
4837
|
+
}));
|
|
4838
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4839
|
+
steps.push(await runStep("source", "write .yamlignore", dryRun, async () => {
|
|
4840
|
+
await source.writeRepoFile(".yamlignore", createIgnoreConfig());
|
|
4627
4841
|
}));
|
|
4628
4842
|
print(formatStep(steps[steps.length - 1]));
|
|
4629
4843
|
steps.push(await runStep("source", "write .editorconfig", dryRun, async () => {
|
|
4630
|
-
await source.writeRepoFile(".editorconfig", createConfig$
|
|
4844
|
+
await source.writeRepoFile(".editorconfig", createConfig$5());
|
|
4631
4845
|
}));
|
|
4632
4846
|
print(formatStep(steps[steps.length - 1]));
|
|
4633
4847
|
steps.push(await runStep("source", "write .editorconfig-checker.json", dryRun, async () => {
|
|
4634
|
-
await source.writeRepoFile(".editorconfig-checker.json", createConfig$
|
|
4848
|
+
await source.writeRepoFile(".editorconfig-checker.json", createConfig$4());
|
|
4635
4849
|
}));
|
|
4636
4850
|
print(formatStep(steps[steps.length - 1]));
|
|
4637
4851
|
steps.push(await runStep("source", "write devmoji.config.cjs", dryRun, async () => {
|
|
4638
|
-
await source.writeRepoFile("devmoji.config.cjs", createConfig$
|
|
4852
|
+
await source.writeRepoFile("devmoji.config.cjs", createConfig$6());
|
|
4639
4853
|
}));
|
|
4640
4854
|
print(formatStep(steps[steps.length - 1]));
|
|
4641
4855
|
steps.push(await runStep("source", "write .husky/prepare-commit-msg", dryRun, async () => {
|
|
4642
|
-
await source.writeRepoFile(".husky/prepare-commit-msg", createConfig());
|
|
4856
|
+
await source.writeRepoFile(".husky/prepare-commit-msg", createConfig$1());
|
|
4643
4857
|
}));
|
|
4644
4858
|
print(formatStep(steps[steps.length - 1]));
|
|
4645
4859
|
if (hooksEnabled) {
|
|
4646
4860
|
steps.push(await runStep("source", "write .husky/pre-push", dryRun, async () => {
|
|
4647
|
-
await source.writeRepoFile(".husky/pre-push", createConfig$
|
|
4861
|
+
await source.writeRepoFile(".husky/pre-push", createConfig$2(config.holocronScript));
|
|
4648
4862
|
return "runs `holocron ci` before push — bypass with `git push --no-verify`";
|
|
4649
4863
|
}));
|
|
4650
4864
|
print(formatStep(steps[steps.length - 1]));
|
|
4651
4865
|
steps.push(await runStep("source", "write .husky/pre-commit", dryRun, async () => {
|
|
4652
|
-
await source.writeRepoFile(".husky/pre-commit", createConfig$
|
|
4866
|
+
await source.writeRepoFile(".husky/pre-commit", createConfig$3());
|
|
4653
4867
|
return "runs GitLeaks (staged-only) + lint-staged before commit";
|
|
4654
4868
|
}));
|
|
4655
4869
|
print(formatStep(steps[steps.length - 1]));
|
|
@@ -4676,7 +4890,7 @@ async function runSetup(input) {
|
|
|
4676
4890
|
print(formatStep(steps[steps.length - 1]));
|
|
4677
4891
|
}
|
|
4678
4892
|
{
|
|
4679
|
-
const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("
|
|
4893
|
+
const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("verification.unitTests");
|
|
4680
4894
|
const existing = await readFile(join(input.context.repoRoot, "codecov.yml"), "utf8").catch(() => null);
|
|
4681
4895
|
if (!hasTestWorkflow && existing == null) steps.push({
|
|
4682
4896
|
capability: "source",
|
|
@@ -4695,6 +4909,23 @@ async function runSetup(input) {
|
|
|
4695
4909
|
}));
|
|
4696
4910
|
print(formatStep(steps[steps.length - 1]));
|
|
4697
4911
|
}
|
|
4912
|
+
{
|
|
4913
|
+
const content = createAstromech({
|
|
4914
|
+
cwd: input.context.repoRoot,
|
|
4915
|
+
config: { tasks: config.tasks },
|
|
4916
|
+
logger
|
|
4917
|
+
}).turboConfig();
|
|
4918
|
+
if (content === null) steps.push({
|
|
4919
|
+
capability: "source",
|
|
4920
|
+
step: "write turbo.json",
|
|
4921
|
+
status: "skip",
|
|
4922
|
+
message: "no fan-out-eligible tasks configured"
|
|
4923
|
+
});
|
|
4924
|
+
else steps.push(await runStep("source", "write turbo.json", dryRun, async () => {
|
|
4925
|
+
await source.writeRepoFile("turbo.json", content);
|
|
4926
|
+
}));
|
|
4927
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4928
|
+
}
|
|
4698
4929
|
if (source.syncLabels) {
|
|
4699
4930
|
steps.push(await runStep("source", "sync labels", dryRun, async () => {
|
|
4700
4931
|
return source.syncLabels(CANONICAL_LABELS, STALE_LABELS);
|
|
@@ -4833,7 +5064,7 @@ async function runSetup(input) {
|
|
|
4833
5064
|
if (loader.has("deployment")) {
|
|
4834
5065
|
const deploy = loader.get("deployment");
|
|
4835
5066
|
print(style.step("deployment"));
|
|
4836
|
-
const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "deploy");
|
|
5067
|
+
const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "delivery.deploy" || e.name === "knowledge.docs" || e.name === "knowledge.components");
|
|
4837
5068
|
const previewCfg = deployEntry?.with ? extractPreviewConfig(deployEntry.with, {
|
|
4838
5069
|
org: config.org,
|
|
4839
5070
|
domain: config.domain
|
|
@@ -5557,7 +5788,7 @@ async function runSyncWiki(input) {
|
|
|
5557
5788
|
//#endregion
|
|
5558
5789
|
//#region src/commands/sync.ts
|
|
5559
5790
|
const { workflowHeader } = createHeader({
|
|
5560
|
-
source: "packages/
|
|
5791
|
+
source: "packages/astromech/src/thin-callers.ts",
|
|
5561
5792
|
tool: "holocron sync"
|
|
5562
5793
|
});
|
|
5563
5794
|
const SYNC_STEPS = [
|
|
@@ -5571,6 +5802,7 @@ const SYNC_STEPS = [
|
|
|
5571
5802
|
"readme",
|
|
5572
5803
|
"workflows",
|
|
5573
5804
|
"scripts",
|
|
5805
|
+
"turbo",
|
|
5574
5806
|
"wiki"
|
|
5575
5807
|
];
|
|
5576
5808
|
const LOCAL_STEPS = /* @__PURE__ */ new Set([
|
|
@@ -5580,6 +5812,7 @@ const LOCAL_STEPS = /* @__PURE__ */ new Set([
|
|
|
5580
5812
|
"readme",
|
|
5581
5813
|
"workflows",
|
|
5582
5814
|
"scripts",
|
|
5815
|
+
"turbo",
|
|
5583
5816
|
"wiki"
|
|
5584
5817
|
]);
|
|
5585
5818
|
async function runSync(input) {
|
|
@@ -5728,6 +5961,7 @@ async function runSync(input) {
|
|
|
5728
5961
|
"readme",
|
|
5729
5962
|
"workflows",
|
|
5730
5963
|
"scripts",
|
|
5964
|
+
"turbo",
|
|
5731
5965
|
"wiki"
|
|
5732
5966
|
]) {
|
|
5733
5967
|
if (requestedSteps !== void 0 && !requestedSteps.includes(stepName)) continue;
|
|
@@ -5832,7 +6066,8 @@ async function runSync(input) {
|
|
|
5832
6066
|
}).thinCallers();
|
|
5833
6067
|
for (const entry of taskEntries) {
|
|
5834
6068
|
const name = typeof entry === "string" ? entry : entry.name;
|
|
5835
|
-
|
|
6069
|
+
const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
|
|
6070
|
+
if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
|
|
5836
6071
|
steps.push({
|
|
5837
6072
|
capability: "local",
|
|
5838
6073
|
step: `sync workflow ${name}`,
|
|
@@ -5842,10 +6077,10 @@ async function runSync(input) {
|
|
|
5842
6077
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
5843
6078
|
continue;
|
|
5844
6079
|
}
|
|
5845
|
-
const filename =
|
|
6080
|
+
const filename = `${name}.yml`;
|
|
5846
6081
|
const content = files.get(filename);
|
|
5847
6082
|
if (content === void 0) continue;
|
|
5848
|
-
const step = `sync workflow ${name}${
|
|
6083
|
+
const step = `sync workflow ${name}${content.includes("workflows/preview.yml@main") ? " (with preview)" : ""}`;
|
|
5849
6084
|
steps.push(await runSyncStep("local", step, dryRun, async () => {
|
|
5850
6085
|
await writeWorkflowFile(input.context.repoRoot, filename, `${workflowHeader()}${content}`);
|
|
5851
6086
|
}));
|
|
@@ -5877,6 +6112,27 @@ async function runSync(input) {
|
|
|
5877
6112
|
}));
|
|
5878
6113
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
5879
6114
|
}
|
|
6115
|
+
if (stepName === "turbo") {
|
|
6116
|
+
const content = createAstromech({
|
|
6117
|
+
cwd: input.context.repoRoot,
|
|
6118
|
+
config: { tasks: config.tasks },
|
|
6119
|
+
logger
|
|
6120
|
+
}).turboConfig();
|
|
6121
|
+
if (content === null) {
|
|
6122
|
+
steps.push({
|
|
6123
|
+
capability: "local",
|
|
6124
|
+
step: "sync turbo.json",
|
|
6125
|
+
status: "skip",
|
|
6126
|
+
message: "no fan-out-eligible tasks configured"
|
|
6127
|
+
});
|
|
6128
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
6129
|
+
} else {
|
|
6130
|
+
steps.push(await runSyncStep("local", "sync turbo.json", dryRun, async () => {
|
|
6131
|
+
await writeFile(join(input.context.repoRoot, "turbo.json"), content, "utf8");
|
|
6132
|
+
}));
|
|
6133
|
+
print(formatSyncStep(steps[steps.length - 1]));
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
5880
6136
|
if (stepName === "wiki") {
|
|
5881
6137
|
const result = await runSyncWiki({
|
|
5882
6138
|
loaded: input.loaded,
|
|
@@ -7728,9 +7984,31 @@ try {
|
|
|
7728
7984
|
}).option("otp", {
|
|
7729
7985
|
type: "string",
|
|
7730
7986
|
describe: "One-time password from your authenticator (required if npm needs 2FA for writes)"
|
|
7987
|
+
}).option("sync-trust", {
|
|
7988
|
+
type: "array",
|
|
7989
|
+
describe: "Migrate npm Trusted Publisher config to a new workflow filename across every public package: --sync-trust <old-file> <new-file>. See issue #689 — npm requires fresh interactive 2FA per package; re-run after approving the printed authUrl if it stops early."
|
|
7990
|
+
}).option("repo", {
|
|
7991
|
+
type: "string",
|
|
7992
|
+
describe: "owner/repo for Trusted Publisher config (--sync-trust). Defaults to theholocron/holocron."
|
|
7731
7993
|
}), async (argv) => {
|
|
7994
|
+
if (argv.syncTrust) {
|
|
7995
|
+
const [oldFile, newFile] = argv.syncTrust;
|
|
7996
|
+
if (!oldFile || !newFile) {
|
|
7997
|
+
getLogger().error("publish --sync-trust requires exactly two values: <old-file> <new-file>");
|
|
7998
|
+
process.exitCode = 1;
|
|
7999
|
+
return;
|
|
8000
|
+
}
|
|
8001
|
+
if ((await runSyncTrust({
|
|
8002
|
+
cwd: argv.cwd,
|
|
8003
|
+
oldFile,
|
|
8004
|
+
newFile,
|
|
8005
|
+
dryRun: argv.dryRun,
|
|
8006
|
+
...argv.repo ? { repo: argv.repo } : {}
|
|
8007
|
+
})).status === "fail") process.exitCode = 1;
|
|
8008
|
+
return;
|
|
8009
|
+
}
|
|
7732
8010
|
if (!argv.initial) {
|
|
7733
|
-
getLogger().error("publish: only `--initial`
|
|
8011
|
+
getLogger().error("publish: only `--initial` and `--sync-trust` are supported today. Run `holocron publish --initial`.");
|
|
7734
8012
|
process.exitCode = 1;
|
|
7735
8013
|
return;
|
|
7736
8014
|
}
|