@theholocron/astromech 4.17.1 → 4.17.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
@@ -91,6 +91,7 @@ const astromech = createAstromech({ cwd, config, orgContext: { org, domain } });
91
91
  astromech.thinCallers(); // Map<"<name>.yml", yaml> — one per templated, ci-enabled task
92
92
  astromech.packageScripts(); // { holocron: "holocron", lint: "holocron run lint", … }
93
93
  astromech.requiredChecks(); // ["Lint / Conclusion", "codecov/patch", …] — branch-protection contexts
94
+ astromech.codecovConfig(existing); // codecov.yml content — merges into `existing`, or scaffolds fresh when null
94
95
  astromech.ci({ scope: "required" }); // CiReport — run the gating checks locally, in CI order
95
96
  ```
96
97
 
@@ -122,6 +123,18 @@ only.
122
123
  `holocron run` itself does not read the config yet — that (and
123
124
  `holocron ci`) come in later phases (epic #581).
124
125
 
126
+ ### `codecov.yml`
127
+
128
+ `codecovConfig(existing)` generates this repo's `codecov.yml` — component
129
+ `paths` derived from `packages/*`, same manifest-derived category as
130
+ `thinCallers()`. Pass the current file's content (or `null`) and it either
131
+ merges the `individual_components` list in (preserving everything else in the
132
+ file — thresholds, flags, custom rules) or scaffolds a fresh file from the
133
+ base template. `holocron setup` writes the result via the `source` capability;
134
+ this method never touches the filesystem beyond reading `packages/*` under
135
+ `cwd`. Moved here from `@theholocron/cli` (#650) — the coverage setup tracks
136
+ the `test` task, the same way required checks track `tasks`.
137
+
125
138
  ## Lint parity
126
139
 
127
140
  One linter list drives both CI and local — no asymmetry. Source: the
package/dist/index.d.mts CHANGED
@@ -363,9 +363,53 @@ interface Astromech {
363
363
  * the list; this method is policy-free (manifest only).
364
364
  */
365
365
  requiredChecks(): string[];
366
+ /**
367
+ * This repo's `codecov.yml` — component `paths` derived from `packages/*`.
368
+ * Pass the current file's content (`null` if none exists yet) to either
369
+ * merge the component list in or scaffold a fresh file. Policy-free same
370
+ * as {@link requiredChecks} — `holocron setup` owns writing the result.
371
+ */
372
+ codecovConfig(existing: string | null): string;
366
373
  }
367
374
  declare function createAstromech(options: AstromechOptions): Astromech;
368
375
  //#endregion
376
+ //#region src/codecov.d.ts
377
+ /**
378
+ * `codecov.yml` generation — a manifest-derived artifact (component `paths`
379
+ * come from workspace packages; the status targets track the `test` task's
380
+ * coverage setup), the same category as the reusable `test.yml` this package
381
+ * also generates. Moved out of `@theholocron/cli` (theholocron/holocron#650)
382
+ * — `holocron setup`'s codecov step now calls {@link codecovConfig} instead
383
+ * of importing this logic directly.
384
+ */
385
+ interface WorkspacePackage {
386
+ slug: string;
387
+ name: string;
388
+ }
389
+ declare function codecovComponentBlock(packages: WorkspacePackage[]): string;
390
+ /**
391
+ * Ensure every codecov status default carries `if_not_found: success`, so an
392
+ * infra-only PR (dep bump, config, lockfile — no coverable change) passes
393
+ * instead of hanging forever on a required check codecov never posts.
394
+ *
395
+ * Injects the key on the line after each `target:` that sits inside a status
396
+ * block (`coverage.status.*.default` or a `statuses:` list item — indent ≥ 8,
397
+ * or ≥ 8 for a `- type:` list entry), when the immediate sibling lines do not
398
+ * already declare it.
399
+ */
400
+ declare function ensureIfNotFound(yaml: string): string;
401
+ declare function mergeCodecovComponents(existing: string, packages: WorkspacePackage[]): string;
402
+ /** Every public `packages/*` workspace, sorted by slug. `[]` when there's no `packages/` dir. */
403
+ declare function readWorkspacePackages(repoRoot: string): WorkspacePackage[];
404
+ /** A brand-new `codecov.yml` — the scaffold header + the base template + this repo's component list. */
405
+ declare function createCodecovConfig(packages: WorkspacePackage[]): string;
406
+ /**
407
+ * The full pipeline `holocron setup` needs: read this repo's public
408
+ * `packages/*`, then either merge them into an `existing` `codecov.yml` or
409
+ * scaffold a new one.
410
+ */
411
+ declare function codecovConfig(repoRoot: string, existing: string | null): string;
412
+ //#endregion
369
413
  //#region src/linters.d.ts
370
414
  /**
371
415
  * The linter registry — one list of linter names (from `config.tasks`'
@@ -539,4 +583,4 @@ declare const WORKFLOW_TEMPLATE_PROPERTIES: Record<string, string>;
539
583
  */
540
584
  declare function reusableTemplates(): Map<string, string>;
541
585
  //#endregion
542
- export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, type SuperLinterConfig, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, baselineSuperLinterEnv, createAstromech, deriveDeployPaths, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, normalizeWorkflowWith, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
586
+ export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, type SuperLinterConfig, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, baselineSuperLinterEnv, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
package/dist/index.mjs CHANGED
@@ -931,6 +931,108 @@ function taskName(item) {
931
931
  return typeof item === "string" ? item : item.name;
932
932
  }
933
933
  //#endregion
934
+ //#region src/templates/codecov/codecov.yml
935
+ var codecov_default = "codecov:\n require_ci_to_pass: true\n\ncoverage:\n precision: 2\n round: down\n status:\n project:\n default:\n target: auto\n threshold: 2%\n # Infra-only PRs (dep bumps, config, lockfile) touch no coverable code,\n # so codecov produces no report to compare. Pass instead of blocking\n # forever on a required check that will never post.\n if_not_found: success\n patch:\n default:\n target: 80%\n if_not_found: success\n\ncomment:\n layout: \"reach,diff,flags,components\"\n behavior: default\n require_changes: true\n\ncomponent_management:\n default_rules:\n statuses:\n - type: patch\n target: 80%\n if_not_found: success\n individual_components:\n";
936
+ //#endregion
937
+ //#region src/codecov.ts
938
+ /**
939
+ * `codecov.yml` generation — a manifest-derived artifact (component `paths`
940
+ * come from workspace packages; the status targets track the `test` task's
941
+ * coverage setup), the same category as the reusable `test.yml` this package
942
+ * also generates. Moved out of `@theholocron/cli` (theholocron/holocron#650)
943
+ * — `holocron setup`'s codecov step now calls {@link codecovConfig} instead
944
+ * of importing this logic directly.
945
+ */
946
+ const INDIVIDUAL_COMPONENTS_MARKER = " individual_components:";
947
+ function scaffoldHeader() {
948
+ return [
949
+ `# Scaffolded by holocron setup — edit this file freely.`,
950
+ `# Source: theholocron/holocron · packages/astromech/src/codecov.ts`,
951
+ ``
952
+ ].join("\n");
953
+ }
954
+ function codecovComponentBlock(packages) {
955
+ if (packages.length === 0) return "\n []\n";
956
+ return "\n" + packages.flatMap(({ slug }) => [
957
+ ` - component_id: ${slug}`,
958
+ ` name: "${slug}"`,
959
+ ` paths:`,
960
+ ` - packages/${slug}/**`,
961
+ ``
962
+ ]).join("\n");
963
+ }
964
+ /**
965
+ * Ensure every codecov status default carries `if_not_found: success`, so an
966
+ * infra-only PR (dep bump, config, lockfile — no coverable change) passes
967
+ * instead of hanging forever on a required check codecov never posts.
968
+ *
969
+ * Injects the key on the line after each `target:` that sits inside a status
970
+ * block (`coverage.status.*.default` or a `statuses:` list item — indent ≥ 8,
971
+ * or ≥ 8 for a `- type:` list entry), when the immediate sibling lines do not
972
+ * already declare it.
973
+ */
974
+ function ensureIfNotFound(yaml) {
975
+ const lines = yaml.split("\n");
976
+ const out = [];
977
+ for (let i = 0; i < lines.length; i++) {
978
+ const line = lines[i];
979
+ out.push(line);
980
+ const m = /^(\s{8,})target:\s+\S+\s*$/.exec(line);
981
+ if (!m) continue;
982
+ const indent = m[1];
983
+ let alreadySet = false;
984
+ for (let j = i + 1; j < lines.length; j++) {
985
+ const sib = lines[j];
986
+ if (sib.trim() === "") continue;
987
+ const sibIndent = sib.match(/^\s*/)[0].length;
988
+ if (sibIndent < indent.length) break;
989
+ if (sibIndent === indent.length && /^\s*if_not_found\s*:/.test(sib)) {
990
+ alreadySet = true;
991
+ break;
992
+ }
993
+ if (sibIndent === indent.length && /^\s*(- |\w)/.test(sib) && !/^\s*(threshold|flags|paths|branches)\s*:/.test(sib)) break;
994
+ }
995
+ if (!alreadySet) out.push(`${indent}if_not_found: success`);
996
+ }
997
+ return out.join("\n");
998
+ }
999
+ function mergeCodecovComponents(existing, packages) {
1000
+ const idx = existing.indexOf(INDIVIDUAL_COMPONENTS_MARKER);
1001
+ if (idx === -1) return ensureIfNotFound(existing);
1002
+ return ensureIfNotFound(existing.slice(0, idx + 24)) + codecovComponentBlock(packages);
1003
+ }
1004
+ /** Every public `packages/*` workspace, sorted by slug. `[]` when there's no `packages/` dir. */
1005
+ function readWorkspacePackages(repoRoot) {
1006
+ const packagesDir = join(repoRoot, "packages");
1007
+ if (!existsSync(packagesDir)) return [];
1008
+ const packages = [];
1009
+ for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
1010
+ if (!entry.isDirectory()) continue;
1011
+ try {
1012
+ const raw = readFileSync(join(packagesDir, entry.name, "package.json"), "utf8");
1013
+ const pkg = JSON.parse(raw);
1014
+ if (typeof pkg.name === "string") packages.push({
1015
+ slug: entry.name,
1016
+ name: pkg.name
1017
+ });
1018
+ } catch {}
1019
+ }
1020
+ return packages.sort((a, b) => a.slug.localeCompare(b.slug));
1021
+ }
1022
+ /** A brand-new `codecov.yml` — the scaffold header + the base template + this repo's component list. */
1023
+ function createCodecovConfig(packages) {
1024
+ return `${scaffoldHeader()}${codecov_default.trimEnd()}${codecovComponentBlock(packages)}`;
1025
+ }
1026
+ /**
1027
+ * The full pipeline `holocron setup` needs: read this repo's public
1028
+ * `packages/*`, then either merge them into an `existing` `codecov.yml` or
1029
+ * scaffold a new one.
1030
+ */
1031
+ function codecovConfig(repoRoot, existing) {
1032
+ const packages = readWorkspacePackages(repoRoot);
1033
+ return existing != null ? mergeCodecovComponents(existing, packages) : createCodecovConfig(packages);
1034
+ }
1035
+ //#endregion
934
1036
  //#region src/required-checks.ts
935
1037
  /**
936
1038
  * `requiredChecks(config)` — the branch-protection required-status-check list,
@@ -1238,8 +1340,9 @@ function createAstromech(options) {
1238
1340
  rootFiles: rootFiles()
1239
1341
  }),
1240
1342
  reusableTemplates: () => reusableTemplates(),
1241
- requiredChecks: () => requiredChecks(options.config ?? {})
1343
+ requiredChecks: () => requiredChecks(options.config ?? {}),
1344
+ codecovConfig: (existing) => codecovConfig(options.cwd, existing)
1242
1345
  };
1243
1346
  }
1244
1347
  //#endregion
1245
- export { CI_ORDER, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, TASKS, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, baselineSuperLinterEnv, createAstromech, deriveDeployPaths, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, normalizeWorkflowWith, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
1348
+ export { CI_ORDER, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, TASKS, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, baselineSuperLinterEnv, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/astromech",
3
- "version": "4.17.1",
3
+ "version": "4.17.2",
4
4
  "description": "The Holocron task runner — one task manifest drives `holocron run`, `holocron ci`, the CI workflows, package.json scripts, linters, and required checks.",
5
5
  "keywords": [
6
6
  "ci",
@@ -37,7 +37,7 @@
37
37
  "dist"
38
38
  ],
39
39
  "dependencies": {
40
- "@theholocron/datapad": "4.17.1"
40
+ "@theholocron/datapad": "4.17.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@theholocron/eslint-config": "^8.0.0",
@@ -53,7 +53,7 @@
53
53
  "tsdown": "^0.22.14",
54
54
  "typescript": "^5.9.3",
55
55
  "vitest": "^4.1.11",
56
- "@theholocron/rollup-plugin-transform-template": "4.17.1"
56
+ "@theholocron/rollup-plugin-transform-template": "4.17.2"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=22"