@homeflare/config 0.5.1 → 0.7.0

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.
Files changed (50) hide show
  1. package/README.md +46 -0
  2. package/bin/hooks.ts +19 -0
  3. package/dist/hooks/gates.d.ts +19 -0
  4. package/dist/hooks/gates.d.ts.map +1 -0
  5. package/dist/hooks/install.d.ts +20 -0
  6. package/dist/hooks/install.d.ts.map +1 -0
  7. package/dist/hooks/report.d.ts +30 -0
  8. package/dist/hooks/report.d.ts.map +1 -0
  9. package/dist/hooks/staged.d.ts +19 -0
  10. package/dist/hooks/staged.d.ts.map +1 -0
  11. package/dist/hooks.d.ts +15 -0
  12. package/dist/hooks.d.ts.map +1 -0
  13. package/dist/hooks.js +196 -0
  14. package/dist/hooks.js.map +14 -0
  15. package/dist/repo-shape/ci.d.ts +20 -0
  16. package/dist/repo-shape/ci.d.ts.map +1 -0
  17. package/dist/repo-shape/companions.d.ts +39 -0
  18. package/dist/repo-shape/companions.d.ts.map +1 -0
  19. package/dist/repo-shape/drift.d.ts +29 -0
  20. package/dist/repo-shape/drift.d.ts.map +1 -0
  21. package/dist/repo-shape/refresh.d.ts +18 -0
  22. package/dist/repo-shape/refresh.d.ts.map +1 -0
  23. package/dist/repo-shape/render.d.ts +39 -0
  24. package/dist/repo-shape/render.d.ts.map +1 -0
  25. package/dist/repo-shape/security.d.ts +19 -0
  26. package/dist/repo-shape/security.d.ts.map +1 -0
  27. package/dist/repo-shape/shape.d.ts +138 -0
  28. package/dist/repo-shape/shape.d.ts.map +1 -0
  29. package/dist/repo-shape/yaml.d.ts +18 -0
  30. package/dist/repo-shape/yaml.d.ts.map +1 -0
  31. package/dist/repo-shape.d.ts +43 -0
  32. package/dist/repo-shape.d.ts.map +1 -0
  33. package/dist/repo-shape.js +611 -0
  34. package/dist/repo-shape.js.map +17 -0
  35. package/docs/repo-shape.md +199 -0
  36. package/package.json +11 -1
  37. package/src/hooks/gates.ts +102 -0
  38. package/src/hooks/install.ts +95 -0
  39. package/src/hooks/report.ts +72 -0
  40. package/src/hooks/staged.ts +61 -0
  41. package/src/hooks.ts +48 -0
  42. package/src/repo-shape/ci.ts +215 -0
  43. package/src/repo-shape/companions.ts +151 -0
  44. package/src/repo-shape/drift.ts +130 -0
  45. package/src/repo-shape/refresh.ts +113 -0
  46. package/src/repo-shape/render.ts +88 -0
  47. package/src/repo-shape/security.ts +109 -0
  48. package/src/repo-shape/shape.ts +214 -0
  49. package/src/repo-shape/yaml.ts +96 -0
  50. package/src/repo-shape.ts +69 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `.github/workflows/security.yml`, rendered.
3
+ *
4
+ * ⛔ THIS FILE IS WHY THE RENDERER EXISTS. Measured across 13 repositories on 2026-09-22,
5
+ * every one of these was a per-repo accident rather than a decision:
6
+ * · `push: branches: [main]` still present in 5, removed in 8;
7
+ * · the `concurrency:` block missing entirely in 5 — so a superseded scan ran to
8
+ * completion holding one of the mini's 3 slots for a result nobody would read;
9
+ * · `pull-requests: read` missing in 2, which makes every pull-request scan fail 403
10
+ * before it inspects a single line;
11
+ * · `gitleaks/gitleaks-action` pinned at `@v2` in 5 and `@v3` in 8, after GitHub
12
+ * removed the Node 20 runtime v2 needs (2026-09-16);
13
+ * · the weekly cron at three different minutes.
14
+ * One renderer makes all thirteen the same file, and the next such fix is one commit.
15
+ */
16
+ import type { RepoShape } from './shape.ts';
17
+ /** The whole `security.yml` for a shape. */
18
+ export declare function renderSecurity(shape: RepoShape): string;
19
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/repo-shape/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAmE5C,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAyBvD"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `RepoShape` — one HomeFlare repository's tooling, as a value.
3
+ *
4
+ * ★ WHY THIS TYPE EXISTS. Measured across the estate on 2026-09-22: 13 repositories each
5
+ * carry a hand-written `.github/workflows/ci.yml`, `security.yml`, `.github/actionlint.yaml`
6
+ * and `.changeset/config.json`. The changeset configs were byte-identical apart from the
7
+ * repository name in 11 of 13. The security workflows were not: five still carried
8
+ * `push: branches: [main]` that the other eight had removed, five were missing the
9
+ * `concurrency:` block entirely, two were missing `pull-requests: read` (whose absence
10
+ * makes every pull-request scan fail 403), the gitleaks action was pinned at `@v2` in
11
+ * five and `@v3` in eight, and the weekly cron minute took three different values. Not
12
+ * one of those differences was a decision. They are what happens when the same edit is
13
+ * applied by hand thirteen times.
14
+ *
15
+ * ⛔ SO: A DIFFERENCE IS EITHER AN INPUT OR AN EXCEPTION. Nothing else. A repository that
16
+ * needs something the standard does not give it either widens this type — and every
17
+ * repository gets the widening — or declares an exception with a stated reason. There
18
+ * is no third option where a file is quietly edited, because `drift.ts` fails on it.
19
+ *
20
+ * ⚠️ THE REASON IS ENFORCED BY THE COMPILER, NOT BY REVIEW. `except()` and `extraJob()`
21
+ * below refuse an empty reason and refuse a reason read out of a variable, so the text
22
+ * has to be written at the exception. See `Stated`.
23
+ */
24
+ /** Where a repository's jobs run. */
25
+ export type RepoRunner =
26
+ /**
27
+ * `[self-hosted, homeflare-mini]` — the interim Linux arm64 runner on the Mac mini.
28
+ * ⚠️ GitHub-hosted runners are refused for this account (billing lock, 2026-09-22).
29
+ */
30
+ 'mini'
31
+ /** `ubuntu-latest`. Public repositories, where GitHub-hosted minutes are free. */
32
+ | 'github';
33
+ /** One step in a rendered job. Either a `uses:` or a `run:`, never both. */
34
+ export type JobStep = {
35
+ readonly name?: string;
36
+ readonly env?: Readonly<Record<string, string>>;
37
+ } & ({
38
+ readonly uses: string;
39
+ readonly with?: Readonly<Record<string, string | number>>;
40
+ readonly run?: never;
41
+ } | {
42
+ readonly run: string;
43
+ readonly uses?: never;
44
+ readonly with?: never;
45
+ });
46
+ /**
47
+ * ⛔ A REASON MUST BE WRITTEN, NOT COMPUTED. `'' extends R` is true for the empty literal
48
+ * AND for the wide `string` type, so both collapse to `never` and fail to typecheck:
49
+ * `reason: ''` is refused, and so is `reason: someVariable`. Only a string literal
50
+ * written at the call site survives — which is the whole point, because a reason
51
+ * assembled at runtime is a reason nobody reads in the diff.
52
+ * Verified against TypeScript 7.0.2 on 2026-09-22; `tests/repo-shape-reason.test.ts`
53
+ * compiles the refusals and asserts they still fail.
54
+ */
55
+ export type Stated<R extends string> = '' extends R ? never : R;
56
+ /** Paths this package renders. A deviation names one of these, not an arbitrary file. */
57
+ export type RenderedPath = '.changeset/config.json' | '.github/actionlint.yaml' | '.github/dependabot.yml' | '.github/workflows/ci.yml' | '.github/workflows/security.yml';
58
+ export interface RepoShapeException {
59
+ /** The rendered file this repository does not take from the renderer. */
60
+ readonly file: RenderedPath;
61
+ /** Why — written at the exception, in a sentence a stranger can act on. */
62
+ readonly reason: string;
63
+ /** `YYYY-MM-DD` the exception was taken, so a stale one is visible. */
64
+ readonly since: string;
65
+ }
66
+ interface ExceptionInput {
67
+ readonly file: RenderedPath;
68
+ readonly reason: string;
69
+ readonly since: string;
70
+ }
71
+ /**
72
+ * Declare that this repository keeps its own copy of a rendered file.
73
+ *
74
+ * except({
75
+ * file: '.github/workflows/ci.yml',
76
+ * reason: 'Payload needs Node >=24.15, which the rendered job does not install',
77
+ * since: '2026-09-22',
78
+ * })
79
+ *
80
+ * ⛔ THE DRIFT CHECK STOPS CHECKING THAT FILE. That is the trade: an exception buys the
81
+ * freedom to hand-edit one file and pays for it by losing the guarantee on that file.
82
+ * Prefer widening `RepoShape` — then every repository benefits and nothing is lost.
83
+ */
84
+ export declare function except<const E extends ExceptionInput>(exception: E & {
85
+ readonly reason: Stated<E['reason']>;
86
+ }): RepoShapeException;
87
+ export interface ExtraJob {
88
+ /** The job key in `jobs:`, e.g. `package`. Lower-case, dashes allowed. */
89
+ readonly id: string;
90
+ /**
91
+ * The job's `name:`, which is also the status-check context. It joins the `ci`
92
+ * aggregate's `needs`, so the branch ruleset still requires exactly one check.
93
+ */
94
+ readonly name: string;
95
+ /** Why this repository has a job the other twelve do not. */
96
+ readonly reason: string;
97
+ /** Steps after checkout. The prologue (checkout, bun, install) is rendered for you. */
98
+ readonly steps: readonly JobStep[];
99
+ /** Job ids this one waits for. Default: none, so it runs beside `check`. */
100
+ readonly needs?: readonly string[];
101
+ /** `false` skips the rendered bun prologue — for a job that needs another toolchain. */
102
+ readonly bun?: boolean;
103
+ }
104
+ interface ExtraJobInput extends Omit<ExtraJob, 'needs' | 'bun'> {
105
+ readonly needs?: readonly string[];
106
+ readonly bun?: boolean;
107
+ }
108
+ /**
109
+ * Declare a job this repository needs and the standard does not have.
110
+ * ⛔ Same rule as `except`: the reason is a written literal or it does not compile.
111
+ */
112
+ export declare function extraJob<const J extends ExtraJobInput>(job: J & {
113
+ readonly reason: Stated<J['reason']>;
114
+ }): ExtraJob;
115
+ export interface RepoShape {
116
+ /** Repository owner — a user or organization login. */
117
+ readonly owner: string;
118
+ /** Repository name, which is also the checkout directory name. */
119
+ readonly repository: string;
120
+ /** Where its jobs run. */
121
+ readonly runner: RepoRunner;
122
+ /**
123
+ * `true` when the repository publishes an npm tarball. It decides `access` in the
124
+ * changeset config and whether `privatePackages` is written at all — the two keys
125
+ * that differ between `homeflare-kit` and every other repository.
126
+ */
127
+ readonly publishes: boolean;
128
+ /** Jobs beyond `check` and `workflows`. Each carries its own stated reason. */
129
+ readonly extraJobs?: readonly ExtraJob[];
130
+ /** Rendered files this repository keeps its own copy of, each with a reason. */
131
+ readonly exceptions?: readonly RepoShapeException[];
132
+ }
133
+ /** `runs-on:` for a runner. */
134
+ export declare function runsOn(runner: RepoRunner): string;
135
+ /** Whether a rendered path is excepted by this shape. */
136
+ export declare function isExcepted(shape: RepoShape, file: RenderedPath): boolean;
137
+ export {};
138
+ //# sourceMappingURL=shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../../src/repo-shape/shape.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The smallest YAML writer that renders a job's steps, and nothing else.
3
+ *
4
+ * ★ NOT A YAML LIBRARY, DELIBERATELY. A general serializer would take a dependency every
5
+ * consumer of `@homeflare/config` inherits, and it would strip the comments — which in
6
+ * this house are the product. The rendered workflows are written as text with holes;
7
+ * only the step lists, whose shape varies per repository, go through here.
8
+ *
9
+ * ⚠️ Bun can PARSE YAML natively (`Bun.YAML.parse`) but does not stringify it, measured
10
+ * against Bun 1.4.0 on 2026-09-22. The tests parse what this writes with `Bun.YAML.parse`
11
+ * and compare structures, so a malformed emission fails rather than shipping.
12
+ */
13
+ import type { JobStep } from './shape.ts';
14
+ /** One step, as the lines of a `steps:` list item at `depth`. */
15
+ export declare function renderStep(step: JobStep, depth: number): string[];
16
+ /** A whole `steps:` list, already indented for a job at `depth`. */
17
+ export declare function renderSteps(steps: readonly JobStep[], depth: number): string;
18
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/repo-shape/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAsD1C,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBjE;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5E"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `@homeflare/config/repo-shape` — the standard HomeFlare repository, as one declaration.
3
+ *
4
+ * A repository keeps a `repo-shape.ts` at its root:
5
+ *
6
+ * import { except, repoShapeCli, type RepoShape } from '@homeflare/config/repo-shape';
7
+ *
8
+ * export const shape: RepoShape = {
9
+ * owner: 'taslabs-net',
10
+ * repository: 'homeflare-proxmox',
11
+ * runner: 'mini',
12
+ * publishes: false,
13
+ * };
14
+ *
15
+ * if (import.meta.main) process.exit(await repoShapeCli(import.meta.dir, shape, Bun.argv.slice(2)));
16
+ *
17
+ * That one file gives the repository:
18
+ *
19
+ * · its FILES — `bun run repo-shape:refresh` writes ci.yml, security.yml,
20
+ * actionlint.yaml, dependabot.yml and the changeset config;
21
+ * · its DRIFT GATE — a `bun:test` calling `driftInRepoShape` fails on a hand edit;
22
+ * · its SETTINGS — `renderRepoShape(shape).policy` is the options object
23
+ * `@homeflare/alchemy`'s `declareRepoPolicy` takes, so the ruleset
24
+ * requires exactly the checks these workflows report.
25
+ *
26
+ * ⛔ A DEVIATION IS DECLARED OR IT FAILS. `except({ file, reason, since })` refuses an
27
+ * empty reason and refuses a reason read from a variable — the text has to be written
28
+ * at the exception, in the diff, where someone will read it. Same for `extraJob()`.
29
+ *
30
+ * ★ WHY IT LIVES IN `@homeflare/config` RATHER THAN `@homeflare/alchemy`. Measured
31
+ * 2026-09-22: `@homeflare/config` is a dependency of 13 of 14 estate repositories and
32
+ * `@homeflare/alchemy` of 4. The drift check has to run in every repository's own CI,
33
+ * including the ones with no Alchemy stack, so it belongs to the package they all
34
+ * already have — and it takes no dependency on Alchemy or Effect to get there.
35
+ */
36
+ export { renderCi, ACTIONLINT_VERSION, BUN_VERSION } from './repo-shape/ci.ts';
37
+ export { renderActionlintConfig, renderChangesetConfig, renderDependabot, } from './repo-shape/companions.ts';
38
+ export { type DriftReport, type Problem, driftInRepoShape, exceptionSummary, REFRESH_COMMAND, } from './repo-shape/drift.ts';
39
+ export { type RefreshResult, refreshRepoShape, repoShapeCli } from './repo-shape/refresh.ts';
40
+ export { type RenderedRepo, type RepoShapePolicy, RENDERED_PATHS, renderRepoShape, } from './repo-shape/render.ts';
41
+ export { renderSecurity } from './repo-shape/security.ts';
42
+ export { type ExtraJob, type JobStep, type RenderedPath, type RepoRunner, type RepoShape, type RepoShapeException, type Stated, except, extraJob, isExcepted, runsOn, } from './repo-shape/shape.ts';
43
+ //# sourceMappingURL=repo-shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-shape.d.ts","sourceRoot":"","sources":["../src/repo-shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,cAAc,EACd,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACX,MAAM,EACN,QAAQ,EACR,UAAU,EACV,MAAM,GACP,MAAM,uBAAuB,CAAC"}