@avi2dg/checks 0.51.0 → 0.52.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.
- package/.bun-version +1 -0
- package/CHANGELOG.md +14 -0
- package/README.md +24 -249
- package/commitlint.config.ts +0 -1
- package/dist/presets/knip.d.ts +1 -1
- package/dist/presets/knip.js +1 -1
- package/dist/templates/how-to.md +1 -1
- package/dist/templates/readme.md +1 -1
- package/dist/templates/tutorial.md +1 -1
- package/docs/bins.md +67 -0
- package/docs/gates/checks-advisories.md +2 -2
- package/docs/gates/checks-browser.md +5 -1
- package/docs/gates/checks-ci-wiring.md +18 -9
- package/docs/gates/checks-commit-identity.md +3 -3
- package/docs/gates/checks-copy.md +4 -0
- package/docs/gates/checks-docs.md +24 -3
- package/docs/gates/checks-exports.md +1 -1
- package/docs/gates/checks-frontend-syntax.md +5 -1
- package/docs/gates/checks-lint.md +2 -2
- package/docs/gates/checks-mutation-baseline.md +9 -5
- package/docs/gates/checks-mutation-compare.md +3 -3
- package/docs/gates/checks-mutation.md +9 -5
- package/docs/gates/checks-night-run.md +1 -1
- package/docs/gates/checks-secrets.md +2 -2
- package/docs/gates/checks-unused.md +1 -1
- package/docs/how-to/set-up-a-repository.md +142 -0
- package/docs/how-to/turn-on-the-css-families.md +35 -0
- package/docs/how-to/upgrade-the-kit.md +42 -0
- package/docs/shipped-files.md +36 -0
- package/package.json +11 -1
- package/src/delivery/bun-pin.ts +19 -5
- package/src/delivery/ci-wiring.ts +11 -77
- package/src/delivery/commit-identity.ts +6 -5
- package/src/delivery/runner-policy.ts +385 -0
- package/src/delivery/workflow-references.ts +1 -1
- package/src/docs/doc-templates.ts +1 -1
- package/src/docs/doc-versions.ts +137 -0
- package/src/docs/docs.ts +7 -0
- package/src/testing/mutation-baseline-full.ts +23 -47
- package/src/testing/mutation-baseline-saved.ts +1 -1
- package/src/testing/mutation-baseline.ts +10 -8
- package/src/testing/mutation-scope-graph.ts +10 -19
- package/src/testing/mutation-scope.ts +8 -41
|
@@ -12,6 +12,7 @@ It also fails when a doc names a file, a heading or a script that does not exist
|
|
|
12
12
|
It holds each doc file a change touches to the template for its kind.
|
|
13
13
|
It lists every other doc file that does not match its template yet, and does not fail on it.
|
|
14
14
|
It holds each line a change adds or edits in a living doc or an agent file to the prose rules, as [The prose rules](#the-prose-rules) says.
|
|
15
|
+
It fails when such a line copies a version `package.json` or `.bun-version` pins, as [Pinned versions](#pinned-versions) says.
|
|
15
16
|
It fails when a living doc or an agent file names a path, link or command that does not resolve, and the range added or broke it.
|
|
16
17
|
It fails when an agent file holds more than 3,000 characters or a `## Maintaining this file` section, whatever the range touches, as [Agent files](#agent-files) says.
|
|
17
18
|
It fails when an entry in an agent file names no tracked path, link or `bun run` command, whatever the range touches.
|
|
@@ -119,6 +120,24 @@ The cap is 20 words in an ordered list item and 25 words elsewhere.
|
|
|
119
120
|
The package exports it as `@avi2dg/checks/scripts/prose-matchers.ts`.
|
|
120
121
|
A host such as a write-time hook can copy that one file into a directory with no `node_modules`, and refuse the same lines.
|
|
121
122
|
|
|
123
|
+
## Pinned versions
|
|
124
|
+
|
|
125
|
+
A version a manifest pins has one home, and a doc that copies it by hand goes stale when the pin moves.
|
|
126
|
+
Prose and code are judged alike.
|
|
127
|
+
A line a change adds or edits in a living doc or an agent file fails when it carries one of these:
|
|
128
|
+
|
|
129
|
+
- A package `package.json` lists in `dependencies`, `devDependencies`, `peerDependencies` or `optionalDependencies` at one version, written as `name@version` or beside the name in prose or a table.
|
|
130
|
+
- The `version`, `packageManager` or an `engines` version from `package.json`, or the version in `.bun-version`, written on its own.
|
|
131
|
+
|
|
132
|
+
The doc names the file that holds the version instead, as in `Bun, at the version .bun-version pins`.
|
|
133
|
+
A command that needs the exact version sits in a generated block, between a `<!-- generated <name>: ... -->` line and its `<!-- end generated <name> -->` line, which a build writes from the manifest.
|
|
134
|
+
No line in a complete same-named generated block is judged.
|
|
135
|
+
A version the manifests do not pin passes, such as a sample's `lodash@4.17.20`.
|
|
136
|
+
So does a longer version that opens with a pin.
|
|
137
|
+
A manifest range with no single version pins nothing, as in `>=1.2.3 <2` in `package.json`.
|
|
138
|
+
A doc line that carries a pinned version still fails when that version sits inside a range.
|
|
139
|
+
It reads the root `package.json` and `.bun-version`, so a nested package's pins are not checked.
|
|
140
|
+
|
|
122
141
|
## Paths, links and commands
|
|
123
142
|
|
|
124
143
|
Each reference a living doc or an agent file names has to resolve at the head commit:
|
|
@@ -177,6 +196,7 @@ It reads the lines the range adds or edits from the diff, with renames detected,
|
|
|
177
196
|
It reads the files tracked at both ends of the range, and the `scripts` of each `package.json` a living doc or an agent file sits under.
|
|
178
197
|
It compares each code span a living doc or an agent file names with the text git tracks outside the docs at both ends of the range.
|
|
179
198
|
It reads the repository's own name and its direct dependencies from the root `package.json` at the head commit.
|
|
199
|
+
It reads the versions the root `package.json` and `.bun-version` pin at the head commit.
|
|
180
200
|
It reads each agent file at the head commit for the ceiling, its sections and its entries, whatever the range touches.
|
|
181
201
|
A name that an installed direct dependency still holds counts as present.
|
|
182
202
|
From the working tree it reads the ignore files git reads, `node_modules/.bin`, and the directory of each direct dependency under `node_modules`.
|
|
@@ -195,16 +215,17 @@ With one it is that commit against its parent, or against the empty tree for a r
|
|
|
195
215
|
|
|
196
216
|
| Code | When |
|
|
197
217
|
| --- | --- |
|
|
198
|
-
| 0 | every doc file the range touches holds to its template, every line it adds to a living doc or an agent file holds to the prose rules, it adds or breaks no reference that does not resolve, every agent file holds to the ceiling and holds no `## Maintaining this file`, every entry in an agent file names a tracked path, a link or a command, and no code span a living doc or an agent file names vanished from every file outside the docs |
|
|
199
|
-
| 1 | a doc file the range touches does not hold to its template, a line the range adds to a living doc or an agent file breaks a prose rule, the range adds or breaks a reference that does not resolve, an agent file is over the ceiling or holds `## Maintaining this file`, an entry in an agent file names no tracked path, link or command, or the range removes a name a living doc or an agent file still carries |
|
|
218
|
+
| 0 | every doc file the range touches holds to its template, every line it adds to a living doc or an agent file holds to the prose rules and copies no pinned version, it adds or breaks no reference that does not resolve, every agent file holds to the ceiling and holds no `## Maintaining this file`, every entry in an agent file names a tracked path, a link or a command, and no code span a living doc or an agent file names vanished from every file outside the docs |
|
|
219
|
+
| 1 | a doc file the range touches does not hold to its template, a line the range adds to a living doc or an agent file breaks a prose rule or copies a pinned version, the range adds or breaks a reference that does not resolve, an agent file is over the ceiling or holds `## Maintaining this file`, an entry in an agent file names no tracked path, link or command, or the range removes a name a living doc or an agent file still carries |
|
|
200
220
|
| 2 | a `package.json` does not decode, a ref does not resolve, or `grep` cannot read an installed direct dependency |
|
|
201
221
|
|
|
202
222
|
## Sample output
|
|
203
223
|
|
|
204
224
|
```
|
|
205
|
-
docs:
|
|
225
|
+
docs: 7 violation(s):
|
|
206
226
|
README.md:1: lacks `## Before you begin`
|
|
207
227
|
README.md:12: carries `;`, a semicolon. Use two sentences
|
|
228
|
+
README.md:13: carries `1.3.5`, a version .bun-version pins. Name .bun-version instead, or let a build write the line between generated markers
|
|
208
229
|
README.md:14: carries `former`, a report about the past. Say what is true now, and leave what changed to the changelog, a commit message or a decision record
|
|
209
230
|
README.md:20: names `scripts/bild.ts`, which is not in the repository
|
|
210
231
|
docs/parts.md: is a page under docs/ with no mode; add kind: tutorial, how-to, reference, explanation in YAML front matter
|
|
@@ -34,7 +34,7 @@ import { defineConfig } from "@avi2dg/checks/knip";
|
|
|
34
34
|
export default defineConfig({ entry: ["src/index.ts"] });
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts
|
|
37
|
+
The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts`, `dependency-cruiser.config.ts` and `oxlint.config.ts` to the entries.
|
|
38
38
|
The gate resolves the Knip binary from the installed kit, so a consumer installs nothing beyond the kit.
|
|
39
39
|
|
|
40
40
|
## Arguments
|
|
@@ -133,10 +133,14 @@ A config not built with `defineConfig` names its own `customSyntax` for markup,
|
|
|
133
133
|
|
|
134
134
|
The repository installs the optional peers the gate loads, at the versions the kit pins:
|
|
135
135
|
|
|
136
|
+
<!-- generated install: bun run build writes it from package.json and scripts/doc-blocks.ts -->
|
|
137
|
+
|
|
136
138
|
```sh
|
|
137
|
-
bun add -d
|
|
139
|
+
bun add -d postcss-html@2.0.0 postcss-value-parser@4.2.0 stylelint@17.16.0 stylelint-config-standard@40.0.0 stylelint-plugin-use-baseline@1.4.7
|
|
138
140
|
```
|
|
139
141
|
|
|
142
|
+
<!-- end generated install -->
|
|
143
|
+
|
|
140
144
|
## Arguments
|
|
141
145
|
|
|
142
146
|
```sh
|
|
@@ -8,7 +8,7 @@ audience: consumers
|
|
|
8
8
|
|
|
9
9
|
## What it checks
|
|
10
10
|
|
|
11
|
-
It runs the gates under [What runs](
|
|
11
|
+
It runs the gates under [What runs](../bins.md#what-runs), each in its own process.
|
|
12
12
|
Gates requiring tracked TypeScript files begin running when the repository tracks TypeScript.
|
|
13
13
|
`checks-lint-coverage` and `checks-unused` also begin running when the repository tracks an `.astro` file.
|
|
14
14
|
`checks-advisories` begins running when the repository tracks `bun.lock`.
|
|
@@ -62,5 +62,5 @@ It never runs `checks-browser`, which a product runs after it builds its site.
|
|
|
62
62
|
|
|
63
63
|
## Related topics
|
|
64
64
|
|
|
65
|
-
- [What runs](
|
|
65
|
+
- [What runs](../bins.md#what-runs)
|
|
66
66
|
- [checks-ci-wiring](checks-ci-wiring.md)
|
|
@@ -22,12 +22,15 @@ With `--full` it lists up to 50 `schedule` runs and up to 50 `workflow_dispatch`
|
|
|
22
22
|
It tries those runs newest first, and restores the first `mutation-baseline-full` artifact that has not expired, downloads and holds the report.
|
|
23
23
|
It tries no run after that one.
|
|
24
24
|
It takes the report from `mutation.json` at the artifact's root, or else from `mutation/mutation.json`, and copies it to the destination.
|
|
25
|
-
With an incremental destination it also copies `stryker-incremental.json` when the artifact holds one, so the next full run retests the mutants its changes reach, while
|
|
25
|
+
With an incremental destination it also copies `stryker-incremental.json` when the artifact holds one, so the next full run retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
|
|
26
26
|
The restore then drops each test that reads a changed file and each mutant result for a changed source from the copied state.
|
|
27
27
|
It drops non-killed results when any test goes stale.
|
|
28
|
-
It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version,
|
|
28
|
+
It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version, a file the active config reaches through a relative import at either revision, or the bunfig preload set or a file it reaches through a relative import changed.
|
|
29
29
|
It walks the config and preload dependencies at both revisions, so a file only the older revision resolved still drops the state when it changes.
|
|
30
|
+
A file a config or preload reaches only through a package main escapes invalidation until the next 28-day refresh.
|
|
30
31
|
A state that does not parse fails the restore instead of running on unknown results.
|
|
32
|
+
It drops the whole state when the restored run started before the current 28-day refresh window, and the windows count from the Unix epoch.
|
|
33
|
+
Every state a window keeps was built inside it, so each reused result is at most 28 days old, and a weekly full baseline retests every mutant every fourth week.
|
|
31
34
|
Without the state file the report still restores.
|
|
32
35
|
|
|
33
36
|
It creates the directories each destination needs.
|
|
@@ -53,9 +56,10 @@ It reads `RUNNER_ENVIRONMENT` and `HOME` to place the runner cache, and `GITHUB_
|
|
|
53
56
|
`--save` and `--prune` read `GITHUB_RUN_ID`.
|
|
54
57
|
With `--full` and an incremental destination it reads the run's head through `gh run view <run> --json headSha`.
|
|
55
58
|
It diffs that head against `HEAD`, which needs the checkout's full history.
|
|
56
|
-
It shows `bunfig.toml
|
|
59
|
+
It shows `bunfig.toml` and the files the active Stryker config imports at both revisions.
|
|
57
60
|
It reads every tracked `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, and `.cjs` file from the working tree for their relative imports.
|
|
58
|
-
Inputs a test reads at runtime without a static import, such as markdown files, escape invalidation
|
|
61
|
+
Inputs a test reads at runtime without a static import, such as markdown files, escape invalidation until the next refresh window.
|
|
62
|
+
It reads the restored run's `createdAt` from the run list to place that run in a refresh window.
|
|
59
63
|
|
|
60
64
|
## The runner cache
|
|
61
65
|
|
|
@@ -178,7 +182,7 @@ The saved baseline uses none of the account's Actions storage, which every priva
|
|
|
178
182
|
That storage accrues by the hour over the month, so an artifact deleted early still counts until the month ends.
|
|
179
183
|
A baseline job on a GitHub-hosted runner uploads the same two files as a `mutation-baseline` artifact with `retention-days: 7` instead, since the restore reads only the newest `main` run.
|
|
180
184
|
|
|
181
|
-
A full baseline job restores the previous full state before its incremental Stryker run, so it retests the mutants its changes reach, while
|
|
185
|
+
A full baseline job restores the previous full state before its incremental Stryker run, so it retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
|
|
182
186
|
The checkout behind that step fetches the full history, so the restore can diff the restored run against `HEAD`:
|
|
183
187
|
|
|
184
188
|
```yaml
|
|
@@ -136,9 +136,9 @@ A changed unit test that `main` edited after that baseline widens the scope to e
|
|
|
136
136
|
Start a full baseline with `gh workflow run mutation`, as [checks-mutation](checks-mutation.md#running-it-in-ci) shows.
|
|
137
137
|
A run with `--mutate` is scoped, so the preset lets it through without `CI=true`.
|
|
138
138
|
The base worktree's path carries the run's id and attempt, and the last step removes it even when a run fails, so a runner kept between jobs starts each job clean.
|
|
139
|
-
A public repository keeps `runs-on: ubuntu-latest`, as [checks-ci-wiring](checks-ci-wiring.md#runners)
|
|
140
|
-
A private repository sets `runs-on
|
|
141
|
-
The job never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the comparison waiting for
|
|
139
|
+
A public repository keeps `runs-on: ubuntu-latest`, as [checks-ci-wiring](checks-ci-wiring.md#runners) requires of a job a fork can reach.
|
|
140
|
+
A private repository sets `runs-on` to the labels it declares in `package.json` under `checks.mutationRunner` instead, which is where it sends its mutation runs.
|
|
141
|
+
The job never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the comparison waiting for those labels.
|
|
142
142
|
[checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names it in `runs-on`.
|
|
143
143
|
|
|
144
144
|
## Related topics
|
|
@@ -82,7 +82,7 @@ An `--incremental` run with no report stops before instrumenting with the missin
|
|
|
82
82
|
A repository runs full baselines from the mutation workflow on `workflow_dispatch`.
|
|
83
83
|
A repository that picks a branch scope from a baseline starts that workflow weekly with `gh workflow run mutation`, because a GitHub schedule can start hours late.
|
|
84
84
|
Run scoped checks locally during development.
|
|
85
|
-
A first baseline with no state costs a full Stryker run, and each later one retests the mutants its changes reach, while
|
|
85
|
+
A first baseline with no state costs a full Stryker run, and each later one retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
|
|
86
86
|
|
|
87
87
|
## Running it in CI
|
|
88
88
|
|
|
@@ -97,7 +97,7 @@ jobs:
|
|
|
97
97
|
permissions:
|
|
98
98
|
contents: read
|
|
99
99
|
actions: read
|
|
100
|
-
runs-on: [self-hosted,
|
|
100
|
+
runs-on: [self-hosted, gpu, x64]
|
|
101
101
|
steps:
|
|
102
102
|
- uses: actions/checkout@v5
|
|
103
103
|
with:
|
|
@@ -121,7 +121,11 @@ jobs:
|
|
|
121
121
|
|
|
122
122
|
A first baseline with no state tests every mutant.
|
|
123
123
|
Each later restore drops the tests that read a changed file and the mutant results for a changed source from the restored state, and drops non-killed results when any test goes stale.
|
|
124
|
-
It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version,
|
|
124
|
+
It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version, a file the active config reaches through a relative import at either revision, or the bunfig preload set or a file it reaches through a relative import changed.
|
|
125
|
+
It fails the restore when the state does not parse.
|
|
126
|
+
The run retests every mutant those changes reach and reuses the rest.
|
|
127
|
+
A result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
|
|
128
|
+
A file a config or preload reaches only through a package main escapes invalidation until the next 28-day refresh.
|
|
125
129
|
The checkout fetches the full history, so the restore can tell which files changed since the previous full run.
|
|
126
130
|
Its report names every test that covers each covered mutant and the first test that killed each killed mutant.
|
|
127
131
|
[checks-mutation-scope](checks-mutation-scope.md#what-it-reads) says why that one killer is enough.
|
|
@@ -131,8 +135,8 @@ The restore reads only a successful run of `mutation.yml` on `main`, so a sweep
|
|
|
131
135
|
The artifact keeps 30 days, so the newest full baseline outlives the week between full runs.
|
|
132
136
|
The upload holds the slimmed report and the slimmed state without each mutant's `statusReason`, as [checks-mutation-baseline](checks-mutation-baseline.md#the-saved-baselines) says, so the one directory holds about 10 MB in place of about 500 MB.
|
|
133
137
|
|
|
134
|
-
The job runs on the
|
|
135
|
-
It never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the sweep waiting for
|
|
138
|
+
The job runs on the self-hosted labels the repository declares in `package.json` under `checks.mutationRunner`, which is where a repository sends its full sweeps.
|
|
139
|
+
It never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the sweep waiting for those labels.
|
|
136
140
|
[checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names it in `runs-on`.
|
|
137
141
|
The `name: mutation` line is what `gh workflow run mutation` looks up.
|
|
138
142
|
GitHub sets `CI=true` on every runner, so the preset lets the full run through there.
|
|
@@ -177,7 +177,7 @@ The concurrency group queues a run started by hand behind the scheduled one, so
|
|
|
177
177
|
A dispatch on a branch runs both jobs and lists its findings in the report job's log, and `if-no-files-found: ignore` lets that run upload no record.
|
|
178
178
|
`issues: write` on the report job is the one permission beyond reading the repository and its runs.
|
|
179
179
|
A public repository keeps `runs-on: ubuntu-latest` on the mutation job.
|
|
180
|
-
A private repository sets `runs-on
|
|
180
|
+
A private repository sets `runs-on` on the mutation job to the labels declared under `checks.mutationRunner` in `package.json`, and [checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names `CI_RUNS_ON`.
|
|
181
181
|
The report job reads `CI_RUNS_ON`, because it runs no mutant.
|
|
182
182
|
A successful run uploads `night-run`, so the next night compares with it, and a failed run uploads nothing a later night reads.
|
|
183
183
|
The artifact keeps 90 days, so a gap of nights still leaves a previous night to compare with.
|
|
@@ -46,8 +46,8 @@ It reads the commits of the range from git, not the working tree.
|
|
|
46
46
|
It needs git 2.36 or newer, which diffs a merge against the merge git would make on its own, and it exits 2 on an older git.
|
|
47
47
|
It reads its rules from `src/delivery/gitleaks.toml` in the installed package.
|
|
48
48
|
|
|
49
|
-
It runs gitleaks
|
|
50
|
-
On first use it downloads the archive from the scanner's GitHub release and unpacks the binary into `~/.cache/avi2dg-checks/gitleaks
|
|
49
|
+
It runs the gitleaks release `src/delivery/gitleaks.ts` pins, by the SHA-256 of each platform's archive and of the binary inside it.
|
|
50
|
+
On first use it downloads the archive from the scanner's GitHub release and unpacks the binary into `~/.cache/avi2dg-checks/gitleaks/<version>/`.
|
|
51
51
|
It checks the binary's SHA-256 again on every run and exits 2 on a copy that differs.
|
|
52
52
|
Builds are pinned for macOS and Linux, each on x64 and arm64.
|
|
53
53
|
On any other platform it exits 2, and no setting runs a scanner other than the pinned build.
|
|
@@ -26,7 +26,7 @@ import { defineConfig } from "@avi2dg/checks/knip";
|
|
|
26
26
|
export default defineConfig({ entry: ["src/index.ts"] });
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts
|
|
29
|
+
The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts`, `dependency-cruiser.config.ts` and `oxlint.config.ts` to the entries.
|
|
30
30
|
In a repository that depends on `astro`, Knip reads `.astro` imports, so a file only an `.astro` entry imports counts as used and an `.astro` file no entry reaches is named.
|
|
31
31
|
The gate resolves the Knip binary from the installed kit, so a consumer installs nothing beyond the kit.
|
|
32
32
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: how-to
|
|
3
|
+
audience: consumers
|
|
4
|
+
---
|
|
5
|
+
# Set up a repository
|
|
6
|
+
|
|
7
|
+
A repository owner follows these steps once, after [Install](../../README.md#install), so each gate `checks-lint` runs finds the file it reads.
|
|
8
|
+
|
|
9
|
+
## Before you begin
|
|
10
|
+
|
|
11
|
+
- The kit in `devDependencies` and the kit's `.bun-version` copied, as [Install](../../README.md#install) says.
|
|
12
|
+
|
|
13
|
+
## Write the tool configs
|
|
14
|
+
|
|
15
|
+
To give each tool the config it reads:
|
|
16
|
+
|
|
17
|
+
1. Write `oxlint.config.ts` with the kit's builder, saying whether the sources are Effect programs:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineConfig } from "@avi2dg/checks/oxlint";
|
|
21
|
+
|
|
22
|
+
export default defineConfig({ effect: true });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
1. Keep oxlint out of `node_modules/` through `.gitignore`:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
node_modules/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
1. Add the Bun types, which the kit's config builders need to typecheck:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
bun add -d @types/bun
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
1. Extend the tsconfig fragment in `tsconfig.json`, naming the Bun types:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"extends": "@avi2dg/checks/tsconfig.effect.json",
|
|
42
|
+
"compilerOptions": { "types": ["bun"] }
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
1. Copy the bunfig preset:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
cp node_modules/@avi2dg/checks/bunfig.toml bunfig.toml
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
1. Name the files nothing imports in `knip.config.ts`:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { defineConfig } from "@avi2dg/checks/knip";
|
|
56
|
+
|
|
57
|
+
export default defineConfig({ entry: ["src/index.ts"] });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
1. Write `dependency-cruiser.config.ts` only when the kit's import rules need an entry point or a boundary of the repository's own:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { defineConfig } from "@avi2dg/checks/dependency-cruiser";
|
|
64
|
+
|
|
65
|
+
export default defineConfig({ orphans: ["^src/bin[.]ts$"] });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Run the checks on every pull request
|
|
69
|
+
|
|
70
|
+
To run the gates in CI:
|
|
71
|
+
|
|
72
|
+
1. Add scripts to `package.json`, replacing the build entry with the repository's own build command:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
"build": "bun build src/index.ts --outdir dist --target node && checks-effect-scope",
|
|
76
|
+
"lint": "oxlint && checks-lint",
|
|
77
|
+
"typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict",
|
|
78
|
+
"test": "checks-test"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
1. Run the scripts on every pull request in `.github/workflows/ci.yml`, fetching the whole history the range needs:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
on:
|
|
85
|
+
pull_request:
|
|
86
|
+
jobs:
|
|
87
|
+
checks:
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v5
|
|
91
|
+
with:
|
|
92
|
+
fetch-depth: 0
|
|
93
|
+
- uses: oven-sh/setup-bun@v2
|
|
94
|
+
with:
|
|
95
|
+
bun-version-file: .bun-version
|
|
96
|
+
- run: bun install --frozen-lockfile
|
|
97
|
+
- run: bun run build
|
|
98
|
+
- run: git diff --exit-code
|
|
99
|
+
- run: bun run lint
|
|
100
|
+
- run: bun run typecheck
|
|
101
|
+
- run: bun run test
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Lint the pull request title in the same job with one step:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
- id: title
|
|
108
|
+
name: lint the title
|
|
109
|
+
env:
|
|
110
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
111
|
+
GH_TOKEN: ${{ github.token }}
|
|
112
|
+
run: bunx checks-commit-title
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A repository whose branch protection requires a `commitlint` status reports the title step under that name using the following step.
|
|
116
|
+
The same job reports the title step's outcome under that name on every run, which needs `statuses: write`:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
- name: report the title as commitlint
|
|
120
|
+
if: always()
|
|
121
|
+
env:
|
|
122
|
+
GH_TOKEN: ${{ github.token }}
|
|
123
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
124
|
+
OUTCOME: ${{ steps.title.outcome }}
|
|
125
|
+
run: |
|
|
126
|
+
state=failure
|
|
127
|
+
if [ "$OUTCOME" = success ]; then state=success; fi
|
|
128
|
+
gh api "repos/$GITHUB_REPOSITORY/statuses/${HEAD_SHA:-$GITHUB_SHA}" -f state="$state" -f context=commitlint -f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" --silent
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
A private repository sets `runs-on: ${{ vars.CI_RUNS_ON || 'ubuntu-latest' }}` on each job, as [checks-ci-wiring](../gates/checks-ci-wiring.md#runners) requires.
|
|
132
|
+
`checks-effect-scope` writes the Effect paths of `oxlint.config.ts` and the kit's severities into `tsconfig.json`, as [The Effect rules](../configs/effect-rules.md#language-service) says.
|
|
133
|
+
`bun run lint` then ends with `checks-lint: <count> gate(s) pass`.
|
|
134
|
+
|
|
135
|
+
## Related topics
|
|
136
|
+
|
|
137
|
+
- [The bins](../bins.md)
|
|
138
|
+
- [Native settings](../configs/native-settings.md)
|
|
139
|
+
- [The Effect rules](../configs/effect-rules.md)
|
|
140
|
+
- [The TypeScript rules](../configs/typescript-rules.md)
|
|
141
|
+
- [The dependency rules](../configs/dependency-rules.md)
|
|
142
|
+
- [The commit message lint](../configs/commit-messages.md)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: how-to
|
|
3
|
+
audience: consumers
|
|
4
|
+
---
|
|
5
|
+
# Turn on the CSS families
|
|
6
|
+
|
|
7
|
+
A repository owner follows these steps to judge the repository's CSS with the stylelint families it needs.
|
|
8
|
+
|
|
9
|
+
## Turn them on
|
|
10
|
+
|
|
11
|
+
To turn on the CSS families:
|
|
12
|
+
|
|
13
|
+
1. Install the peers [checks-frontend-syntax](../gates/checks-frontend-syntax.md#what-it-reads) loads, with the `bun add` line on its page.
|
|
14
|
+
1. Declare `frontend-syntax.json`, which turns the gate on, as its page says.
|
|
15
|
+
1. Write `stylelint.config.ts` with the families the repository needs:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { defineConfig } from "@avi2dg/checks/stylelint";
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
baseline: "widely",
|
|
22
|
+
tokens: ["src/styles/tokens.css"],
|
|
23
|
+
bem: true,
|
|
24
|
+
flatSelectors: true,
|
|
25
|
+
noPageHas: true,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
[checks-frontend-syntax](../gates/checks-frontend-syntax.md#what-it-reads) says what each key takes and which family it turns on.
|
|
30
|
+
`noPageHas` sees a `:has` nested under a page selector only together with `flatSelectors`, which refuses the nesting.
|
|
31
|
+
|
|
32
|
+
## Related topics
|
|
33
|
+
|
|
34
|
+
- [checks-frontend-syntax](../gates/checks-frontend-syntax.md)
|
|
35
|
+
- [checks-css-size](../gates/checks-css-size.md)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: how-to
|
|
3
|
+
audience: consumers
|
|
4
|
+
---
|
|
5
|
+
# Upgrade the kit
|
|
6
|
+
|
|
7
|
+
A repository owner follows these steps to move a repository the kit holds to a newer release.
|
|
8
|
+
|
|
9
|
+
## Before you begin
|
|
10
|
+
|
|
11
|
+
- A repository set up as [Set up a repository](set-up-a-repository.md) says.
|
|
12
|
+
|
|
13
|
+
## Upgrade
|
|
14
|
+
|
|
15
|
+
To move a repository to a newer release of the kit:
|
|
16
|
+
|
|
17
|
+
1. Rerun the [install line](../../README.md#install), which moves the kit to its newest release.
|
|
18
|
+
A repository that lists a required peer explicitly also reruns the upgrade line below.
|
|
19
|
+
The upgrade line keeps each declaration the repository imports.
|
|
20
|
+
The upgrade line writes the lockfile, so the checks below run on the new pins.
|
|
21
|
+
|
|
22
|
+
<!-- generated install: bun run build writes it from package.json and scripts/doc-blocks.ts -->
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
bun add -d @effect/tsgo@0.45.0 @swc/core@1.16.2 dependency-cruiser@18.4.0 effect@4.0.0 jscpd@5.3.2 oxlint@1.83.0 oxlint-tsgolint@7.0.2002 typescript@7.0.2
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
<!-- end generated install -->
|
|
29
|
+
|
|
30
|
+
A repository that opted in to `checks-browser`, `checks-frontend-syntax` or `checks-copy` also reruns the `bun add` line on its page.
|
|
31
|
+
1. Run `bun run build`, whose `checks-effect-scope` rewrites the Effect override and the kit's severities in `tsconfig.json` when a release changes them.
|
|
32
|
+
A severity key the release drops stays in `tsconfig.json` until the repository removes it.
|
|
33
|
+
1. Copy `node_modules/@avi2dg/checks/bunfig.toml` over `bunfig.toml` again, since `checks-test-layout` compares the copy with the installed preset.
|
|
34
|
+
1. Run `bun run lint`, `bun run typecheck` and `bun run test`.
|
|
35
|
+
|
|
36
|
+
The repository's lockfile pins the kit, so a repository moves only when it runs these steps.
|
|
37
|
+
[CHANGELOG.md](../../CHANGELOG.md), shipped in the package, lists what each release changed.
|
|
38
|
+
|
|
39
|
+
## Related topics
|
|
40
|
+
|
|
41
|
+
- [The shipped files](../shipped-files.md)
|
|
42
|
+
- [CHANGELOG.md](../../CHANGELOG.md)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: reference
|
|
3
|
+
audience: consumers
|
|
4
|
+
---
|
|
5
|
+
# The shipped files
|
|
6
|
+
|
|
7
|
+
The package ships these paths, and a repository reads or copies them from its install.
|
|
8
|
+
|
|
9
|
+
## Where things are
|
|
10
|
+
|
|
11
|
+
Every path is relative to the installed package, `node_modules/@avi2dg/checks/`.
|
|
12
|
+
|
|
13
|
+
<!-- generated shipped: bun run build writes it from package.json and scripts/doc-blocks.ts -->
|
|
14
|
+
|
|
15
|
+
| Path | What it holds |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `CHANGELOG.md` | every release, and what it changed |
|
|
18
|
+
| `docs/` | the how-to guides, a reference page per bin and per shared config, and why the kit is shaped this way |
|
|
19
|
+
| `bunfig.toml` | the bunfig preset a repository copies |
|
|
20
|
+
| `.bun-version` | the fleet bun version the bun pin check reads |
|
|
21
|
+
| `commitlint.config.ts` | the shared commitlint config |
|
|
22
|
+
| `dependency-cruiser.config.js` | the kit's dependency-cruiser rules as a base a `.dependency-cruiser.cjs` extends by path, which the build writes |
|
|
23
|
+
| `knip-base.json` | the Knip `include` setting, for a configuration that spreads it |
|
|
24
|
+
| `src/` | every bin, which a package script calls by its `checks-` name, the modules the bins import, and the Effect language service severities under `src/quality/presets/` |
|
|
25
|
+
| `dist/` | the compiled oxlint plugins, the config builders `@avi2dg/checks/oxlint`, `@avi2dg/checks/knip`, `@avi2dg/checks/dependency-cruiser` and `@avi2dg/checks/stylelint` resolve to, and the doc templates, one template per kind of doc file |
|
|
26
|
+
| `oxlintrc.json` | the oxlint `base` as a config a `.oxlintrc.json` extends by path, which the build writes |
|
|
27
|
+
| `stryker.preset.js` | the Stryker mutation-testing preset, which refuses a full run outside CI |
|
|
28
|
+
| `tsconfig.effect.json` | the tsconfig fragment with the shared compiler options and the Effect language-service block |
|
|
29
|
+
| `ts-reset.d.ts` | the two ts-reset rules `tsconfig.effect.json` lists in `files` |
|
|
30
|
+
|
|
31
|
+
<!-- end generated shipped -->
|
|
32
|
+
|
|
33
|
+
## Related topics
|
|
34
|
+
|
|
35
|
+
- [Set up a repository](how-to/set-up-a-repository.md)
|
|
36
|
+
- [Upgrade the kit](how-to/upgrade-the-kit.md)
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avi2dg/checks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Deterministic checks shared across a set of TypeScript repositories",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "avi2d",
|
|
7
|
+
"email": "avi2dg@gmail.com"
|
|
8
|
+
},
|
|
5
9
|
"license": "MIT",
|
|
6
10
|
"repository": {
|
|
7
11
|
"type": "git",
|
|
@@ -13,10 +17,14 @@
|
|
|
13
17
|
"type": "module",
|
|
14
18
|
"files": [
|
|
15
19
|
"CHANGELOG.md",
|
|
20
|
+
"docs/bins.md",
|
|
16
21
|
"docs/configs/",
|
|
17
22
|
"docs/design.md",
|
|
18
23
|
"docs/gates/",
|
|
24
|
+
"docs/how-to/",
|
|
25
|
+
"docs/shipped-files.md",
|
|
19
26
|
"bunfig.toml",
|
|
27
|
+
".bun-version",
|
|
20
28
|
"commitlint.config.ts",
|
|
21
29
|
"dependency-cruiser.config.js",
|
|
22
30
|
"knip-base.json",
|
|
@@ -60,6 +68,7 @@
|
|
|
60
68
|
"src/delivery/workflow-references.ts",
|
|
61
69
|
"src/delivery/bun-pin.ts",
|
|
62
70
|
"src/delivery/mutation-job.ts",
|
|
71
|
+
"src/delivery/runner-policy.ts",
|
|
63
72
|
"src/delivery/secrets.ts",
|
|
64
73
|
"src/delivery/gitleaks.ts",
|
|
65
74
|
"src/delivery/gitleaks.toml",
|
|
@@ -95,6 +104,7 @@
|
|
|
95
104
|
"src/docs/doc-snapshot.ts",
|
|
96
105
|
"src/docs/doc-templates.ts",
|
|
97
106
|
"src/docs/doc-names.ts",
|
|
107
|
+
"src/docs/doc-versions.ts",
|
|
98
108
|
"dist/templates/",
|
|
99
109
|
"src/quality/presets/effect.language-service.json",
|
|
100
110
|
"src/quality/presets/oxlint.ts",
|
package/src/delivery/bun-pin.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { Effect, FileSystem, Path, Schema } from "effect";
|
|
|
2
2
|
import { within } from "./mutation-job.ts";
|
|
3
3
|
import { isRecord, locate, workflowJobs, type Workflow } from "./workflow-references.ts";
|
|
4
4
|
|
|
5
|
-
export const FLEET_BUN_VERSION = "1.4.3";
|
|
6
5
|
const ROOT_MANIFEST = "package.json";
|
|
7
6
|
|
|
8
7
|
export type BunPinPolicy = {
|
|
9
8
|
readonly files: ReadonlyMap<string, string>;
|
|
10
9
|
readonly manifests: ReadonlyMap<string, unknown>;
|
|
10
|
+
readonly fleet: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export type BunPinFault = {
|
|
@@ -94,12 +94,12 @@ function bunPinRequest(policy: BunPinPolicy, step: Readonly<Record<string, unkno
|
|
|
94
94
|
function bunPinFault(policy: BunPinPolicy, step: Readonly<Record<string, unknown>>): string | undefined {
|
|
95
95
|
const request = bunPinRequest(policy, step);
|
|
96
96
|
if (request.expression !== undefined) {
|
|
97
|
-
return `requests bun ${request.expression} from ${request.source}, which cannot be shown equal to ${
|
|
97
|
+
return `requests bun ${request.expression} from ${request.source}, which cannot be shown equal to ${policy.fleet} before the run`;
|
|
98
98
|
}
|
|
99
99
|
if (request.version === undefined) {
|
|
100
|
-
return `pins no bun version from ${request.source}, so setup-bun installs latest, want ${
|
|
100
|
+
return `pins no bun version from ${request.source}, so setup-bun installs latest, want ${policy.fleet}`;
|
|
101
101
|
}
|
|
102
|
-
return request.version ===
|
|
102
|
+
return request.version === policy.fleet ? undefined : `requests bun ${request.version} from ${request.source}, want ${policy.fleet}`;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function unknownRefFault(ref: string): string {
|
|
@@ -156,6 +156,20 @@ const readJsonFile = Effect.fn("readJsonFile")(function* (file: string) {
|
|
|
156
156
|
);
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
export class BunPinError extends Schema.TaggedError<BunPinError>()("BunPinError", {
|
|
160
|
+
message: Schema.String,
|
|
161
|
+
}) {}
|
|
162
|
+
|
|
163
|
+
// The kit ships its own .bun-version, so the check reads the fleet pin from that file rather than a constant.
|
|
164
|
+
const kitBunVersion = Effect.gen(function* () {
|
|
165
|
+
const file = (yield* Path.Path).join(import.meta.dir, "..", "..", ".bun-version");
|
|
166
|
+
const version = (yield* (yield* FileSystem.FileSystem).readFileString(file).pipe(
|
|
167
|
+
Effect.mapError((cause) => new BunPinError({ message: `cannot read the kit .bun-version at ${file}: ${cause.message}` })),
|
|
168
|
+
)).trim();
|
|
169
|
+
if (version === "") return yield* new BunPinError({ message: `the kit .bun-version at ${file} pins no version` });
|
|
170
|
+
return version;
|
|
171
|
+
});
|
|
172
|
+
|
|
159
173
|
export const readBunPinPolicy = Effect.fn("readBunPinPolicy")(function* (root: string, workflows: readonly Workflow[]) {
|
|
160
174
|
const path = yield* Path.Path;
|
|
161
175
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -170,5 +184,5 @@ export const readBunPinPolicy = Effect.fn("readBunPinPolicy")(function* (root: s
|
|
|
170
184
|
if (text !== undefined) files.set(name, text);
|
|
171
185
|
}
|
|
172
186
|
}
|
|
173
|
-
return { files, manifests };
|
|
187
|
+
return { files, manifests, fleet: yield* kitBunVersion };
|
|
174
188
|
});
|