@theholocron/astromech 5.0.0-alpha.20 → 5.0.0-alpha.201

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
@@ -1,7 +1,7 @@
1
1
  # `@theholocron/astromech`
2
2
 
3
3
  The Holocron task runner. One **task manifest** per repo, and every
4
- derived surface comes from it: `holocron run` (local), `holocron ci` (the
4
+ derived surface comes from it: `holocron run` (local), `holocron run ci` (the
5
5
  CI suite run locally), the generated GitHub Actions workflows, the
6
6
  `package.json` scripts, and the branch-protection required-checks list.
7
7
 
@@ -33,7 +33,7 @@ const report = astromech.run("verification.unitTests", { passthrough: ["--watch"
33
33
  `holocron run verification.unitTests` runs your tests — you don't tell it
34
34
  turbo vs pnpm vs npm, or which runner:
35
35
 
36
- ```
36
+ ```text
37
37
  1. turbo.json defines the task → turbo run <task>
38
38
  2. package.json has a <task> script → <detected pm> run <task>
39
39
  (a "holocron run …" thin caller is skipped — no recursion)
@@ -48,33 +48,35 @@ turbo vs pnpm vs npm, or which runner:
48
48
 
49
49
  Tasks are named by **intent**, not by tool — a repo declares
50
50
  `verification.unitTests`, not `vitest`. The table below is the full
51
- registry (`TASKS` in `src/registry.ts`): every task name astromech knows,
51
+ registry (`TASKS` in `src/registry/registry.ts`): every task name astromech knows,
52
52
  what it actually runs, and why. Tool names never appear in
53
53
  `holocron.config.ts` — they're an implementation detail this table
54
54
  documents, not a naming convention repos need to follow.
55
55
 
56
- | Task | Runs | Notes |
57
- | ---------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
58
- | `verification.unitTests` | vitest | carries the `--coverage` org default |
59
- | `verification.typeSafety` | tsc | `tsc --noEmit` |
60
- | `verification.performance` | Lighthouse CI | only runs with a `lighthouse.config.*` present |
61
- | `sourceQuality.staticAnalysis` | eslint, actionlint, git-merge-conflict-markers | a `linterGroup` — see below |
62
- | `sourceQuality.formatting` | prettier, editorconfig-checker, markdownlint-cli2 | a `linterGroup` |
63
- | `sourceQuality.structuredDataValidation` | yamllint | |
64
- | `sourceQuality.deadCodeAnalysis` | knip | |
65
- | `security.secretDetection` | gitleaks | |
66
- | `security.codeScanning` | CodeQL | no local equivalent — CI only |
67
- | `security.dependencyReview` | GitHub's native Dependabot alerts/graph | a capability-model method (`Source.enableVulnerabilityAlerts()`), not a task |
68
- | `delivery.build` | tsdown / vite / rollup / tsc, detected from the repo's own config file | |
69
- | `delivery.publish` | semantic-release | no local equivalent — CI only; carries `preview` (npm dist-tags) |
70
- | `delivery.deploy` | Cloudflare Pages / Vercel | no local equivalent — CI only; carries `preview` |
71
- | `delivery.bundleSize` | bundle-size upload to Codecov | no local equivalent — CI only |
72
- | `platform.repoSync` | `holocron sync` | keeps generated files current |
73
- | `platform.commitStandards` | commitlint | no local equivalent — enforced by the `commit-msg` hook locally, over the PR's commit range in CI |
74
- | `platform.repoValidation` | `scripts/validate-adrs.mjs`, `scripts/validate-registry.mjs`, `scripts/validate-docs-presence.mjs` | a job-bearing task — spec/ADR frontmatter, registry-doc completeness, and docs-presence, not linters |
75
- | `knowledge.wiki` | Fern | publishes `docs/wiki/*.md` — not a sync, a publish; carries `preview` |
76
- | `knowledge.docs` | Astro build | the docs site itself; carries `preview` (defaults on) |
77
- | `knowledge.components` | Storybook build | a browsable component catalog — "Storybook" is the tool, not the intent; carries `preview` (defaults on) |
56
+ | Task | Runs | Notes |
57
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
58
+ | `verification.unitTests` | vitest | carries the `--coverage` org default |
59
+ | `verification.typeSafety` | tsc | `tsc --noEmit` |
60
+ | `verification.performance` | Lighthouse CI | only runs with a `lighthouse.config.*` present |
61
+ | `sourceQuality.staticAnalysis` | eslint, actionlint, git-merge-conflict-markers | a `linterGroup` — see below |
62
+ | `sourceQuality.formatting` | prettier, editorconfig-checker, markdownlint-cli2 | a `linterGroup` |
63
+ | `sourceQuality.structuredDataValidation` | yamllint | |
64
+ | `sourceQuality.deadCodeAnalysis` | knip | |
65
+ | `security.secretDetection` | gitleaks | |
66
+ | `security.codeScanning` | CodeQL | no local equivalent — CI only |
67
+ | `security.dependencyReview` | GitHub's native Dependabot alerts/graph | a capability-model method (`Source.enableVulnerabilityAlerts()`), not a task |
68
+ | `delivery.build` | tsdown / vite / rollup / tsc, detected from the repo's own config file, or `package.json`'s own `dependencies`/`devDependencies` if that config file isn't there | |
69
+ | `delivery.publish` | semantic-release | no local equivalent — CI only; carries `preview` (npm dist-tags) |
70
+ | `delivery.deploy` | Cloudflare Pages / Vercel | no local equivalent — CI only; carries `preview` |
71
+ | `delivery.bundleSize` | bundle-size upload to Codecov | no local equivalent — CI only |
72
+ | `delivery.previewCheck` | `holocron monitoring check <url>` against the PR's own Vercel preview URL | no local equivalent — CI only; triggered by `deployment_status`, not `push`/`pull_request` |
73
+ | `platform.notifyCheck` | _(none — manual workflow)_ | posts a labelled test message to every notification channel; fails per channel that does not work |
74
+ | `platform.repoSync` | `holocron repo sync` | keeps generated files current |
75
+ | `platform.commitStandards` | commitlint | no local equivalent — enforced by the `commit-msg` hook locally, over the PR's commit range in CI |
76
+ | `platform.repoValidation` | `scripts/validate-adrs.mjs`, `scripts/validate-registry.mjs`, `scripts/validate-docs-presence.mjs` | a job-bearing task — spec/ADR frontmatter, registry-doc completeness, and docs-presence, not linters |
77
+ | `knowledge.wiki` | Fern | publishes `docs/wiki/*.md` — not a sync, a publish; carries `preview` |
78
+ | `knowledge.docs` | Astro build | the docs site itself; carries `preview` (defaults on) |
79
+ | `knowledge.components` | Storybook build | a browsable component catalog — "Storybook" is the tool, not the intent; carries `preview` (defaults on) |
78
80
 
79
81
  **`preview` is a cross-cutting feature, not a namespace.** npm has staging
80
82
  dist-tags, Cloudflare/Vercel do per-PR preview deploys, Fern previews
@@ -83,10 +85,12 @@ publish/deploy-shaped task can carry, toggled via that task's `with:`.
83
85
 
84
86
  **`linterGroup` tasks** (`sourceQuality.staticAnalysis`,
85
87
  `sourceQuality.formatting`) bundle more than one tool under a single
86
- required check. Each tool is still gated by its own detection rule (e.g.
87
- `eslint` only runs if `eslint.config.*` is present) — the
88
+ required check. Each tool is still gated by its own detection rule — e.g.
89
+ `eslint` runs when either a local `eslint.config.*` is present, or the
90
+ package has no local file but `@theholocron/eslint-config` resolves
91
+ (the same shared-config splice the `tool`/`detect` runner path uses) — the
88
92
  `tool name → detection rule → local binary` mapping lives in one place,
89
- `src/linters.ts`. A repo's choice of which of these run is just which
93
+ `src/tasks/run/linters/linters.ts`. A repo's choice of which of these run is based on which
90
94
  tasks it includes in `tasks: [...]`, same as any other task.
91
95
 
92
96
  **No super-linter.** Each task above runs its own tool directly, through
@@ -116,10 +120,13 @@ export default defineConfig({
116
120
  `loadTasksConfig(cwd)` resolves the manifest: the `tasks` key of
117
121
  `holocron.config.*` (a bare item array), then a dedicated
118
122
  `astromech.config.*` merged on top (dedicated wins; arrays concatenate).
123
+ `mergeTasksLayers(parentTasks, dedicated)` is that merge step on its own,
124
+ for callers that load the two sources some other way (Sentinel reads them
125
+ through the GitHub API).
119
126
 
120
127
  | `TaskEntry` field | Effect |
121
128
  | ------------------------ | ----------------------------------------------------------- |
122
- | `ci` (default `true`) | emit the CI workflow; include in `holocron ci` |
129
+ | `ci` (default `true`) | emit the CI workflow; include in `holocron run ci` |
123
130
  | `local` (default `true`) | write the `package.json` script; `holocron run` resolves it |
124
131
  | `local: false` | `holocron run <name>` → "CI-only task", exit 0 |
125
132
  | `required` | the task's check context is a required status check |
@@ -146,7 +153,7 @@ the same resolution as `run()`, in `CI_ORDER`, and returns `{ status, jobs }`.
146
153
  A `required` task whose local runner can't run is a failure — **except** a
147
154
  task that's genuinely CI-only (`local: null`, or a `linterGroup` whose every
148
155
  member lacks a local binary entirely, like `platform.commitStandards`),
149
- which is reported skipped even when required. `holocron ci` sets the
156
+ which is reported skipped even when required. `holocron run ci` sets the
150
157
  process exit code from `status`.
151
158
 
152
159
  `thinCallers()` returns the raw `.github/workflows/*.yml` content (no
@@ -169,7 +176,7 @@ de-duplicated. Most tasks are single always-run jobs now, so their context
169
176
  names that job directly (`"Typecheck / tsc --noEmit"`) — the `… /
170
177
  Conclusion` fan-in aggregate is only used where a task genuinely has
171
178
  several conditionally-run jobs feeding one check
172
- (`verification.unitTests`, `platform.repoValidation`). `holocron setup`
179
+ (`verification.unitTests`, `platform.repoValidation`). `holocron repo setup`
173
180
  prepends `"DCO"` and applies the list for `protection: "strict"` repos.
174
181
  Policy-free — manifest only.
175
182
 
@@ -183,10 +190,31 @@ came from), same manifest-derived category as `thinCallers()`. Pass the
183
190
  current file's content (or `null`) and it either merges the
184
191
  `individual_components` list in (preserving everything else in the file —
185
192
  thresholds, flags, custom rules) or scaffolds a fresh file from the base
186
- template. `holocron setup` writes the result via the `source` capability;
193
+ template. `holocron repo setup` writes the result via the `source` capability;
187
194
  this method never touches the filesystem beyond reading `packages/*` and
188
195
  `apps/*` under `cwd`.
189
196
 
197
+ ## Release-time deploys — `deployOnRelease`
198
+
199
+ Deploys the workspace packages whose manifest declares it (holocron#930):
200
+
201
+ ```ts
202
+ // packages/<app>/holocron.config.ts
203
+ tasks: [{ name: "delivery.deploy", with: { on: "release", channel: "alpha" } }];
204
+ ```
205
+
206
+ `holocron deploy on-release --channel=<alpha|main> --from=<previous release commit> --to=<release commit>`
207
+ (or `deployOnRelease()` from `@theholocron/astromech`) deploys each such package
208
+ when the release is on its `channel` (default `main`, the stable release;
209
+ set `alpha` for prereleases) and changes the package or one of its `workspace:*` dependencies
210
+ (`with.paths` overrides that), once npm serves the release's new dependency
211
+ versions, by running `pnpm --filter <package> delivery.deploy`. A package
212
+ that doesn't apply to the release is skipped; a deploy that fails exits
213
+ non-zero. The shared `delivery.publish` workflow runs it as a separate `deploy`
214
+ job after the release job (opt in with `deploy-on-release: true`), so a deploy
215
+ is its own re-runnable check. A `delivery.deploy` task with `on: "release"`
216
+ generates no workflow.
217
+
190
218
  ## Development
191
219
 
192
220
  | Script | Description |
@@ -203,3 +231,19 @@ Automated via semantic-release. See [CHANGELOG.md](../../CHANGELOG.md).
203
231
  ## Documentation
204
232
 
205
233
  <https://theholocron.github.io/holocron/>
234
+
235
+ ## Lint — `@theholocron/astromech/lint`
236
+
237
+ The lint tools' own data and checks, in a subpath so the runner and the
238
+ config surface don't pull `@commitlint/*` in:
239
+
240
+ - `lintCommitMsgFile(file, cwd)` / `lintCommitMessage(message, config)` —
241
+ what `holocron run lint commit-msg <file>` (the `commit-msg` hook) runs.
242
+ - `ALEX_CONFIG`, `ALEX_IGNORE_PATTERNS`, `PRETTIER_IGNORE_PATTERNS`,
243
+ `EDITORCONFIG_CHECKER_EXCLUDE` and `isEditorConfigExcluded(path)` — read
244
+ from the same templates `holocron repo setup` writes, so Sentinel's central
245
+ checks skip exactly what each repo's own CI skips.
246
+
247
+ The root entry also exports the config-file generators `holocron repo setup`
248
+ writes (`createEditorconfig`, `createAlexrc`, `createPrePush`, …) and
249
+ `createHeader`.
@@ -1,5 +1,18 @@
1
- import { i as normalizeTaskEntry, n as TaskEntry, r as TasksConfig, t as TaskConfigItem } from "../schema-D01VYg43.mjs";
2
- //#region src/config/define.d.ts
1
+ import { n as TaskEntry, r as TasksConfig, t as TaskConfigItem } from "../schema.type-BJmfOafW.mjs";
2
+ import { Loaded } from "@theholocron/datapad";
3
+ //#region src/config/schema/schema.d.ts
4
+ /**
5
+ * The `tasks` config shape — the single manifest of what a repo runs.
6
+ * Read from a dedicated `astromech.config.*` file or the `tasks` key of
7
+ * `holocron.config.*` (see {@link loadTasksConfig}).
8
+ *
9
+ * Runtime validation is the loader's job, not this type's — the type only
10
+ * describes what an author writes.
11
+ */
12
+ /** Normalise a `TaskConfigItem` to a full {@link TaskEntry} with defaults applied. */
13
+ declare function normalizeTaskEntry(item: TaskConfigItem): Required<Pick<TaskEntry, "name" | "ci" | "local">> & TaskEntry;
14
+ //#endregion
15
+ //#region src/config/define/define.d.ts
3
16
  /**
4
17
  * Typed identity helper for `astromech.config.ts`:
5
18
  *
@@ -13,13 +26,47 @@ import { i as normalizeTaskEntry, n as TaskEntry, r as TasksConfig, t as TaskCon
13
26
  */
14
27
  declare const defineConfig: <C extends TasksConfig>(config: C) => C;
15
28
  //#endregion
16
- //#region src/config/load.d.ts
29
+ //#region src/config/load/load.type.d.ts
30
+ /**
31
+ * The `tasks` value in each source. A dedicated `astromech.config.*`
32
+ * default-exports the full {@link TasksConfig} object; the `tasks` key of
33
+ * `holocron.config.*` is the bare item array (it mirrors the old
34
+ * `workflows` key). Either form is accepted from either source.
35
+ */
36
+ type TasksInput = TasksConfig | TaskConfigItem[];
37
+ interface LoadTasksConfigOptions {
38
+ /**
39
+ * The `holocron.config.*` the caller has already loaded (`null`: it looked and
40
+ * there is none), so it isn't found and loaded a second time. Leave it unset
41
+ * to have `loadTasksConfig` find it itself.
42
+ */
43
+ holocron?: Loaded<object> | null;
44
+ }
45
+ //#endregion
46
+ //#region src/config/load/load.d.ts
17
47
  /**
18
48
  * Resolve the task manifest for a repo: the `tasks` key of
19
49
  * `holocron.config.*`, then a dedicated `astromech.config.*` merged on
20
50
  * top (dedicated wins; item arrays concatenate). Returns `{}` when
21
51
  * neither source is present.
52
+ *
53
+ * `walkUp: true` — `holocron run`/`ci` are usable from any subdirectory of
54
+ * a repo, not only its exact root (datapad walks up to the nearest `.git`
55
+ * boundary; see `@theholocron/datapad`'s own doc comment).
56
+ *
57
+ * A caller that has already loaded `holocron.config.*` passes it as
58
+ * `options.holocron` so it is not found and loaded again.
59
+ */
60
+ declare function loadTasksConfig(cwd: string, options?: LoadTasksConfigOptions): Promise<TasksConfig>;
61
+ //#endregion
62
+ //#region src/config/merge-tasks-layers/merge-tasks-layers.d.ts
63
+ /**
64
+ * The same layering as {@link loadTasksConfig}, for callers that load the two
65
+ * sources some other way (Sentinel reads them from the GitHub API, not
66
+ * from disk, holocron#916): `holocron.config.*`'s `tasks` value first,
67
+ * then a dedicated `astromech.config.*`'s default export merged on top.
68
+ * Either may be `undefined` (source absent); returns `{}` when both are.
22
69
  */
23
- declare function loadTasksConfig(cwd: string): Promise<TasksConfig>;
70
+ declare function mergeTasksLayers(parentTasks: TasksInput | undefined, dedicated: TasksInput | undefined): TasksConfig;
24
71
  //#endregion
25
- export { type TaskConfigItem, type TaskEntry, type TasksConfig, defineConfig, loadTasksConfig, normalizeTaskEntry };
72
+ export { type TaskConfigItem, type TaskEntry, type TasksConfig, defineConfig, loadTasksConfig, mergeTasksLayers, normalizeTaskEntry };
@@ -1,6 +1,6 @@
1
- import { t as normalizeTaskEntry } from "../schema-Cf5dfaDO.mjs";
2
- import { createDefineConfig, loadConfigFile, mergeConfig } from "@theholocron/datapad";
3
- //#region src/config/define.ts
1
+ import { n as coerceTasks, r as normalizeTaskEntry, t as loadTasksConfig } from "../load-DdHEVFlG.mjs";
2
+ import { createDefineConfig, mergeLayers } from "@theholocron/datapad";
3
+ //#region src/config/define/define.ts
4
4
  /**
5
5
  * Typed identity helper for `astromech.config.ts`:
6
6
  *
@@ -14,26 +14,16 @@ import { createDefineConfig, loadConfigFile, mergeConfig } from "@theholocron/da
14
14
  */
15
15
  const defineConfig = createDefineConfig();
16
16
  //#endregion
17
- //#region src/config/load.ts
17
+ //#region src/config/merge-tasks-layers/merge-tasks-layers.ts
18
18
  /**
19
- * Resolve the task manifest for a repo: the `tasks` key of
20
- * `holocron.config.*`, then a dedicated `astromech.config.*` merged on
21
- * top (dedicated wins; item arrays concatenate). Returns `{}` when
22
- * neither source is present.
19
+ * The same layering as {@link loadTasksConfig}, for callers that load the two
20
+ * sources some other way (Sentinel reads them from the GitHub API, not
21
+ * from disk, holocron#916): `holocron.config.*`'s `tasks` value first,
22
+ * then a dedicated `astromech.config.*`'s default export merged on top.
23
+ * Either may be `undefined` (source absent); returns `{}` when both are.
23
24
  */
24
- async function loadTasksConfig(cwd) {
25
- const dedicated = await loadConfigFile({
26
- cwd,
27
- name: "astromech"
28
- });
29
- return [coerce((await loadConfigFile({
30
- cwd,
31
- name: "holocron"
32
- }))?.config.tasks), coerce(dedicated?.config)].filter((layer) => layer !== void 0).reduce((acc, layer) => mergeConfig(acc, layer), {});
33
- }
34
- function coerce(value) {
35
- if (value == null) return void 0;
36
- return Array.isArray(value) ? { tasks: value } : value;
25
+ function mergeTasksLayers(parentTasks, dedicated) {
26
+ return mergeLayers([coerceTasks(parentTasks), coerceTasks(dedicated)]) ?? {};
37
27
  }
38
28
  //#endregion
39
- export { defineConfig, loadTasksConfig, normalizeTaskEntry };
29
+ export { defineConfig, loadTasksConfig, mergeTasksLayers, normalizeTaskEntry };
@@ -0,0 +1,41 @@
1
+ //#region src/templates/configs/alexjs/alexrc.json
2
+ var alexrc_default = { allow: [
3
+ "dead",
4
+ "execute",
5
+ "execution",
6
+ "executes",
7
+ "failure",
8
+ "failures",
9
+ "hook",
10
+ "hooks",
11
+ "husky",
12
+ "period"
13
+ ] };
14
+ //#endregion
15
+ //#region src/templates/configs/editorconfig-checker/editorconfig-checker.json
16
+ var editorconfig_checker_default = {
17
+ Verbose: false,
18
+ Format: "",
19
+ Debug: false,
20
+ IgnoreDefaults: false,
21
+ SpacesAfterTabs: false,
22
+ NoColor: false,
23
+ Exclude: [
24
+ "(^|.+/)LICENSE$",
25
+ "^public/.*",
26
+ "\\.md$",
27
+ "\\.mdx$"
28
+ ],
29
+ AllowedContentTypes: [],
30
+ PassedFiles: [],
31
+ Disable: {
32
+ "EndOfLine": false,
33
+ "Indentation": false,
34
+ "InsertFinalNewline": false,
35
+ "TrimTrailingWhitespace": false,
36
+ "IndentSize": false,
37
+ "MaxLineLength": false
38
+ }
39
+ };
40
+ //#endregion
41
+ export { alexrc_default as n, editorconfig_checker_default as t };