@theholocron/cli 4.18.0 → 4.18.2

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/README.md CHANGED
@@ -157,10 +157,13 @@ export default defineConfig({
157
157
  ```
158
158
 
159
159
  `holocron setup` writes `.husky/pre-push` (runs `holocron ci` before every
160
- push) and sets `package.json#scripts.prepare` to `husky`. On by default for
160
+ push) and `.husky/pre-commit` (runs `gitleaks protect --staged` + `lint-staged`
161
+ before every commit — scoped to staged changes, not the whole history, so a
162
+ past false positive can't permanently block future commits) and sets
163
+ `package.json#scripts.prepare` to `husky`. Both on by default for
161
164
  `protection: "strict"`; `false` / `{ prePush: false }` opts out. `holocron
162
165
  setup --hooks` / `--no-hooks` override per run. Bypass one push with `git push
163
- --no-verify`.
166
+ --no-verify`; bypass one commit with `git commit --no-verify`.
164
167
 
165
168
  ### Task scripts
166
169
 
package/dist/cli.mjs CHANGED
@@ -3563,19 +3563,19 @@ var alexrc_default = { allow: [
3563
3563
  ] };
3564
3564
  //#endregion
3565
3565
  //#region src/templates/configs/alexjs/create-config.ts
3566
- const { workflowHeader: workflowHeader$6 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
3566
+ const { workflowHeader: workflowHeader$7 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
3567
3567
  function createRcConfig() {
3568
3568
  return JSON.stringify(alexrc_default, null, 2) + "\n";
3569
3569
  }
3570
3570
  function createIgnoreConfig() {
3571
- return `${workflowHeader$6()}${alexignore_default}`;
3571
+ return `${workflowHeader$7()}${alexignore_default}`;
3572
3572
  }
3573
3573
  //#endregion
3574
3574
  //#region src/templates/configs/devmoji/create-config.ts
3575
- const { workflowHeader: workflowHeader$5 } = createHeader({ source: "packages/cli/src/templates/configs/devmoji/create-config.ts" });
3576
- function createConfig$4() {
3575
+ const { workflowHeader: workflowHeader$6 } = createHeader({ source: "packages/cli/src/templates/configs/devmoji/create-config.ts" });
3576
+ function createConfig$5() {
3577
3577
  return [
3578
- workflowHeader$5("cjs"),
3578
+ workflowHeader$6("cjs"),
3579
3579
  `/* eslint-disable */`,
3580
3580
  `const { defineConfig } = require("@theholocron/devmoji-config");`,
3581
3581
  `module.exports = defineConfig();`,
@@ -3587,9 +3587,9 @@ function createConfig$4() {
3587
3587
  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
3588
  //#endregion
3589
3589
  //#region src/templates/configs/editorconfig/create-config.ts
3590
- const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cli/src/templates/configs/editorconfig/create-config.ts" });
3591
- function createConfig$3() {
3592
- return `${workflowHeader$4()}${editorconfig_default}`;
3590
+ const { workflowHeader: workflowHeader$5 } = createHeader({ source: "packages/cli/src/templates/configs/editorconfig/create-config.ts" });
3591
+ function createConfig$4() {
3592
+ return `${workflowHeader$5()}${editorconfig_default}`;
3593
3593
  }
3594
3594
  //#endregion
3595
3595
  //#region src/templates/configs/editorconfig-checker/editorconfig-checker.json
@@ -3630,10 +3630,31 @@ var editorconfig_checker_default = {
3630
3630
  * options here (`Exclude`, `Disable`) are stable across releases, so pinning
3631
3631
  * buys nothing.
3632
3632
  */
3633
- function createConfig$2() {
3633
+ function createConfig$3() {
3634
3634
  return JSON.stringify(editorconfig_checker_default, null, 2) + "\n";
3635
3635
  }
3636
3636
  //#endregion
3637
+ //#region src/templates/configs/pre-commit/pre-commit
3638
+ 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
+ //#endregion
3640
+ //#region src/templates/configs/pre-commit/create-config.ts
3641
+ const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cli/src/templates/configs/pre-commit/create-config.ts" });
3642
+ /**
3643
+ * `.husky/pre-commit` — runs GitLeaks (scoped to staged changes) and
3644
+ * lint-staged before every commit. Installed by `holocron setup` when hooks
3645
+ * are enabled (default for `protection: "strict"`). `git commit --no-verify`
3646
+ * bypasses it.
3647
+ *
3648
+ * `gitleaks protect --staged` (not `gitleaks git`) is deliberate: the latter
3649
+ * scans the entire commit history on every invocation, so once any false
3650
+ * positive lands in a past commit — a fake test token, an example key in a
3651
+ * doc — every future commit is permanently blocked, regardless of what's
3652
+ * actually staged.
3653
+ */
3654
+ function createConfig$2() {
3655
+ return `${workflowHeader$4("shebang")}${pre_commit_default}`;
3656
+ }
3657
+ //#endregion
3637
3658
  //#region src/templates/configs/pre-push/pre-push
3638
3659
  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";
3639
3660
  //#endregion
@@ -4484,6 +4505,7 @@ const { workflowHeader: workflowHeader$1 } = createHeader({
4484
4505
  });
4485
4506
  async function runSetup(input) {
4486
4507
  const print = input.print ?? ((line) => console.log(line));
4508
+ const logger = getLogger();
4487
4509
  const loader = input.loader ?? new PluginLoader(input.loaded.resolved, input.context);
4488
4510
  await withSpinner("Loading plugins…", () => loader.load());
4489
4511
  assertPluginsResolvable(loader, "setup");
@@ -4530,7 +4552,8 @@ async function runSetup(input) {
4530
4552
  if (config.extraRequiredChecks) tasksConfig.extraRequiredChecks = config.extraRequiredChecks;
4531
4553
  const requiredChecks = effectivePreset === "strict" ? ["DCO", ...createAstromech({
4532
4554
  cwd: input.context.repoRoot,
4533
- config: tasksConfig
4555
+ config: tasksConfig,
4556
+ logger
4534
4557
  }).requiredChecks()] : [];
4535
4558
  steps.push(await upsertBranchProtection(source, dryRun, requiredChecks));
4536
4559
  print(formatStep(steps[steps.length - 1]));
@@ -4546,7 +4569,8 @@ async function runSetup(input) {
4546
4569
  orgContext: {
4547
4570
  org: config.org,
4548
4571
  domain: config.domain
4549
- }
4572
+ },
4573
+ logger
4550
4574
  }).thinCallers();
4551
4575
  for (const entry of tasks) {
4552
4576
  const name = typeof entry === "string" ? entry : entry.name;
@@ -4603,15 +4627,15 @@ async function runSetup(input) {
4603
4627
  }));
4604
4628
  print(formatStep(steps[steps.length - 1]));
4605
4629
  steps.push(await runStep("source", "write .editorconfig", dryRun, async () => {
4606
- await source.writeRepoFile(".editorconfig", createConfig$3());
4630
+ await source.writeRepoFile(".editorconfig", createConfig$4());
4607
4631
  }));
4608
4632
  print(formatStep(steps[steps.length - 1]));
4609
4633
  steps.push(await runStep("source", "write .editorconfig-checker.json", dryRun, async () => {
4610
- await source.writeRepoFile(".editorconfig-checker.json", createConfig$2());
4634
+ await source.writeRepoFile(".editorconfig-checker.json", createConfig$3());
4611
4635
  }));
4612
4636
  print(formatStep(steps[steps.length - 1]));
4613
4637
  steps.push(await runStep("source", "write devmoji.config.cjs", dryRun, async () => {
4614
- await source.writeRepoFile("devmoji.config.cjs", createConfig$4());
4638
+ await source.writeRepoFile("devmoji.config.cjs", createConfig$5());
4615
4639
  }));
4616
4640
  print(formatStep(steps[steps.length - 1]));
4617
4641
  steps.push(await runStep("source", "write .husky/prepare-commit-msg", dryRun, async () => {
@@ -4624,6 +4648,11 @@ async function runSetup(input) {
4624
4648
  return "runs `holocron ci` before push — bypass with `git push --no-verify`";
4625
4649
  }));
4626
4650
  print(formatStep(steps[steps.length - 1]));
4651
+ steps.push(await runStep("source", "write .husky/pre-commit", dryRun, async () => {
4652
+ await source.writeRepoFile(".husky/pre-commit", createConfig$2());
4653
+ return "runs GitLeaks (staged-only) + lint-staged before commit";
4654
+ }));
4655
+ print(formatStep(steps[steps.length - 1]));
4627
4656
  if (config.syncScripts !== false) {
4628
4657
  steps.push(await runStep("source", "set package.json prepare script", dryRun, async () => {
4629
4658
  return await ensurePrepareScript(input.context.repoRoot) ? "prepare now runs husky" : "already runs husky";
@@ -4638,6 +4667,13 @@ async function runSetup(input) {
4638
4667
  message: "git hooks disabled"
4639
4668
  });
4640
4669
  print(formatStep(steps[steps.length - 1]));
4670
+ steps.push({
4671
+ capability: "source",
4672
+ step: "write .husky/pre-commit",
4673
+ status: "skip",
4674
+ message: "git hooks disabled"
4675
+ });
4676
+ print(formatStep(steps[steps.length - 1]));
4641
4677
  }
4642
4678
  {
4643
4679
  const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("test");
@@ -4649,7 +4685,10 @@ async function runSetup(input) {
4649
4685
  message: "no test workflow configured"
4650
4686
  });
4651
4687
  else steps.push(await runStep("source", "write codecov.yml", dryRun, async () => {
4652
- const content = createAstromech({ cwd: input.context.repoRoot }).codecovConfig(existing);
4688
+ const content = createAstromech({
4689
+ cwd: input.context.repoRoot,
4690
+ logger
4691
+ }).codecovConfig(existing);
4653
4692
  await source.writeRepoFile("codecov.yml", content);
4654
4693
  const packageCount = readWorkspacePackages(input.context.repoRoot).length;
4655
4694
  return packageCount > 0 ? `${packageCount} components` : "no components";
@@ -5788,7 +5827,8 @@ async function runSync(input) {
5788
5827
  orgContext: {
5789
5828
  org: config.org,
5790
5829
  domain: config.domain
5791
- }
5830
+ },
5831
+ logger
5792
5832
  }).thinCallers();
5793
5833
  for (const entry of taskEntries) {
5794
5834
  const name = typeof entry === "string" ? entry : entry.name;
@@ -5826,7 +5866,8 @@ async function runSync(input) {
5826
5866
  if (config.holocronScript !== void 0) tasksConfig.holocronScript = config.holocronScript;
5827
5867
  const desired = createAstromech({
5828
5868
  cwd: input.context.repoRoot,
5829
- config: tasksConfig
5869
+ config: tasksConfig,
5870
+ logger
5830
5871
  }).packageScripts();
5831
5872
  steps.push(await runSyncStep("local", "sync scripts", dryRun, async () => {
5832
5873
  const changed = await mergePackageJsonScripts(input.context.repoRoot, desired);