@avi2dg/checks 0.6.0 → 0.9.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/README.md +256 -97
- package/oxlintrc.json +2 -2
- package/package.json +15 -6
- package/scripts/backtest.ts +25 -36
- package/scripts/ci-wiring.ts +138 -62
- package/scripts/comment-gate.ts +23 -41
- package/scripts/comment-matchers.ts +309 -0
- package/scripts/comments.ts +15 -292
- package/scripts/commit-identity.ts +91 -87
- package/scripts/gates.ts +59 -0
- package/scripts/git.ts +41 -0
- package/scripts/lint-coverage.sh +11 -5
- package/scripts/lint.ts +179 -0
- package/scripts/main.ts +28 -0
- package/scripts/mutation-compare.ts +43 -57
- package/scripts/suppressions-ratchet.ts +64 -76
- package/scripts/test-layout.ts +67 -44
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# checks
|
|
2
2
|
|
|
3
3
|
Deterministic checks shared across my TypeScript repos. One package,
|
|
4
|
-
`@avi2dg/checks`: the
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
JavaScript.
|
|
4
|
+
`@avi2dg/checks`: the `checks-lint` entry point that runs every lint gate
|
|
5
|
+
below over a range it resolves itself, the oxlint base config, the
|
|
6
|
+
tsconfig fragment with the Effect language-service block, the shared
|
|
7
|
+
commitlint config, the shared dependency-cruiser base, the test-layout
|
|
8
|
+
check with its bunfig preset, the commit-identity check, the comment
|
|
9
|
+
gate with its backtest, the oxlint suppressions ratchet, the Stryker
|
|
10
|
+
mutation-testing preset with its no-regression comparator, the CI-wiring
|
|
11
|
+
check, and the Effect error-channel plugin compiled to JavaScript.
|
|
12
12
|
|
|
13
13
|
Published as `@avi2dg/checks` on the public npm registry.
|
|
14
14
|
|
|
@@ -17,7 +17,7 @@ Published as `@avi2dg/checks` on the public npm registry.
|
|
|
17
17
|
From the consuming repo:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
bun add -d @avi2dg/checks oxlint@1.83.0 oxlint-tsgolint@7.0.2002 @effect/tsgo@0.45.0 typescript@7.0.2 dependency-cruiser@18.4.0 @swc/core@1.16.2
|
|
20
|
+
bun add -d @avi2dg/checks effect@4.0.0-rc.115 oxlint@1.83.0 oxlint-tsgolint@7.0.2002 @effect/tsgo@0.45.0 typescript@7.0.2 dependency-cruiser@18.4.0 @swc/core@1.16.2
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
`.oxlintrc.json`:
|
|
@@ -52,14 +52,19 @@ cp node_modules/@avi2dg/checks/bunfig.toml bunfig.toml
|
|
|
52
52
|
`package.json` gains three scripts:
|
|
53
53
|
|
|
54
54
|
```json
|
|
55
|
-
"lint": "oxlint --type-aware && checks-lint
|
|
55
|
+
"lint": "oxlint --type-aware && checks-lint",
|
|
56
56
|
"typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict",
|
|
57
57
|
"test": "bun test --randomize"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
`checks-lint` runs every kit gate a lint needs; see "Lint entry point"
|
|
61
|
+
below. Three of them:
|
|
62
|
+
|
|
60
63
|
`lint-coverage.sh` fails when oxlint silently skips a tracked `.ts` or
|
|
61
64
|
`.tsx` file, for example through a stray `.gitignore` entry. It compares
|
|
62
65
|
`git ls-files` against oxlint's own file walk and names the missing files.
|
|
66
|
+
It exits 2 when oxlint cannot walk the tree, as when it is not on `PATH`
|
|
67
|
+
or its config does not parse.
|
|
63
68
|
|
|
64
69
|
`test-layout.ts` decides the test layout described below.
|
|
65
70
|
|
|
@@ -81,6 +86,100 @@ export default {
|
|
|
81
86
|
The registry version is pinned by the consumer's lockfile; bump
|
|
82
87
|
`@avi2dg/checks` to adopt a new release.
|
|
83
88
|
|
|
89
|
+
## Lint entry point
|
|
90
|
+
|
|
91
|
+
`checks-lint` runs each of the kit's lint gates in turn and names every
|
|
92
|
+
one that fails, rather than stopping at the first. A repository whose
|
|
93
|
+
tracked files give a gate nothing to check can leave it out through
|
|
94
|
+
`ciWiring.lintGates`; see "Gate selection" under "CI wiring".
|
|
95
|
+
|
|
96
|
+
| Gate | Reads |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `checks-lint-coverage` | the working tree |
|
|
99
|
+
| `checks-test-layout` | the working tree |
|
|
100
|
+
| `checks-commit-identity` | the range |
|
|
101
|
+
| `checks-comment-gate` | the range |
|
|
102
|
+
| `checks-suppressions-ratchet` | the range |
|
|
103
|
+
| `checks-ci-wiring` | the working tree |
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
checks-lint
|
|
107
|
+
checks-lint <base-ref> <head-ref>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
It resolves the range once and hands the same one to every range gate.
|
|
111
|
+
Locally, and on any event other than a pull request, the range ends at
|
|
112
|
+
`HEAD` and starts where `HEAD` branched from the origin default branch:
|
|
113
|
+
`origin/HEAD`, or when `origin/HEAD` is not set, as in an
|
|
114
|
+
`actions/checkout` clone, `origin/<ciWiring.defaultBranch>` from the
|
|
115
|
+
repository's `package.json` (see "CI wiring"), and `origin/main` when
|
|
116
|
+
that is not declared. In a GitHub Actions pull request, where
|
|
117
|
+
`GITHUB_EVENT_NAME` is `pull_request`, it ends
|
|
118
|
+
at the event's head sha and starts where that branched from
|
|
119
|
+
`origin/<base branch>`, so GitHub's merge commit is never in it. The
|
|
120
|
+
base branch is read from the fetch, not from the event's recorded base
|
|
121
|
+
sha, which GitHub leaves stale once the base branch advances after the
|
|
122
|
+
pull request opens. Explicit base and head arguments override both.
|
|
123
|
+
|
|
124
|
+
The range always starts at the merge base, never at the base branch's
|
|
125
|
+
tip: commits the base branch gained after the head branched off would
|
|
126
|
+
otherwise count against the head. When the head is the merge base, as
|
|
127
|
+
on a push to the default branch or a local run on it, the range would be
|
|
128
|
+
empty, so each range gate is handed that tip commit alone and checks it
|
|
129
|
+
against its parent, or against the empty tree when it is a repository's
|
|
130
|
+
first commit:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
checks-lint: tip 10ba7d8935b73ed72624120a1542e51bd21ca7c7 from HEAD against origin/main
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A clone with no remote-tracking refs at all, such as a freshly
|
|
137
|
+
initialised repository with no remote or one whose remote was never
|
|
138
|
+
fetched, has no default branch to start from, so each range gate is
|
|
139
|
+
handed `HEAD` alone the same way:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
checks-lint: tip 10ba7d8935b73ed72624120a1542e51bd21ca7c7 from HEAD alone, as the clone has no remote-tracking refs
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Once any ref sits under `refs/remotes/`, a missing
|
|
146
|
+
`origin/<default branch>` exits 2 instead, since that is the shape of a
|
|
147
|
+
shallow CI checkout, where `HEAD` alone would leave the commits before
|
|
148
|
+
it unchecked.
|
|
149
|
+
|
|
150
|
+
It prints the range, the declared selection if there is one, each
|
|
151
|
+
gate's own report, then its verdict:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
checks-lint: range 2504acf098d120e73a8ece3c96f22b934f35c6a8..10ba7d8935b73ed72624120a1542e51bd21ca7c7 from HEAD against origin/main
|
|
155
|
+
...
|
|
156
|
+
checks-lint: 3 of 6 gate(s) failed: checks-commit-identity, checks-comment-gate, checks-suppressions-ratchet
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
It exits 1 when any gate found a violation, and 2 when the range or the
|
|
160
|
+
selection does not resolve, or no failing gate could decide. ci-wiring
|
|
161
|
+
always runs, so a repository on `checks-lint` declares `ciWiring.gates`
|
|
162
|
+
(see "CI wiring"), and it holds the test layout unless its selection
|
|
163
|
+
leaves out `checks-test-layout`.
|
|
164
|
+
|
|
165
|
+
CI runs it through `lint`. The checkout fetches the whole history, which
|
|
166
|
+
the merge base needs:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
on:
|
|
170
|
+
pull_request:
|
|
171
|
+
jobs:
|
|
172
|
+
lint:
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/checkout@v5
|
|
176
|
+
with:
|
|
177
|
+
fetch-depth: 0
|
|
178
|
+
- uses: oven-sh/setup-bun@v2
|
|
179
|
+
- run: bun install --frozen-lockfile
|
|
180
|
+
- run: bun run lint
|
|
181
|
+
```
|
|
182
|
+
|
|
84
183
|
## Effect rules
|
|
85
184
|
|
|
86
185
|
The base config loads the `effect-channel` plugin and turns on
|
|
@@ -123,6 +222,47 @@ oxlint resolves `files` against the directory of the config that holds
|
|
|
123
222
|
the override, so a config passed with `-c` from outside the repository
|
|
124
223
|
matches nothing and reports nothing.
|
|
125
224
|
|
|
225
|
+
This repository's own override for `scripts/**` adds `node/no-sync`,
|
|
226
|
+
`oxc/no-async-await`, `promise/avoid-new` and `unicorn/no-process-exit`,
|
|
227
|
+
which need the `node`, `promise` and `unicorn` plugins in the override's
|
|
228
|
+
`plugins`. `unicorn/no-process-exit` passes over any file that opens with
|
|
229
|
+
a shebang, so a bin also needs `no-restricted-properties` on
|
|
230
|
+
`process.exit`. Sites standing when the override lands go in oxlint's own
|
|
231
|
+
baseline, `oxlint --suppress-all`, so their count can only fall. A later
|
|
232
|
+
override lifts `effect-channel/no-throw` from `scripts/comment-matchers.ts`,
|
|
233
|
+
the one file there a host loads without `node_modules`.
|
|
234
|
+
|
|
235
|
+
The language service holds the same paths to Effect-native IO through
|
|
236
|
+
`overrides` in the plugin block of `tsconfig.json`. effect-tsgo keeps the
|
|
237
|
+
severities `tsconfig.effect.json` sets when the child config restates the
|
|
238
|
+
plugin with only its overrides:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"extends": "@avi2dg/checks/tsconfig.effect.json",
|
|
243
|
+
"compilerOptions": {
|
|
244
|
+
"plugins": [
|
|
245
|
+
{
|
|
246
|
+
"name": "@effect/language-service",
|
|
247
|
+
"overrides": [
|
|
248
|
+
{
|
|
249
|
+
"include": ["src/**/*.ts"],
|
|
250
|
+
"options": {
|
|
251
|
+
"diagnosticSeverity": {
|
|
252
|
+
"nodeBuiltinImport": "error",
|
|
253
|
+
"asyncFunction": "error",
|
|
254
|
+
"newPromise": "error",
|
|
255
|
+
"extendsNativeError": "error"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
126
266
|
## Test layout
|
|
127
267
|
|
|
128
268
|
`checks-test-layout` fails unless the repo holds this shape, and names the
|
|
@@ -146,7 +286,7 @@ file and the path to move it to when it does not:
|
|
|
146
286
|
with swc and reads import specifiers and identifier use, so a test that
|
|
147
287
|
only carries `"node:child_process"` as a string is not a violation.
|
|
148
288
|
- `scripts.test` is exactly `bun test --randomize` and `scripts.lint` runs
|
|
149
|
-
this check.
|
|
289
|
+
this check, itself or through `checks-lint` called by its bare bin name.
|
|
150
290
|
- `bunfig.toml` carries every `[test]` key of the shipped preset with the
|
|
151
291
|
same value, and `[test].pathIgnorePatterns` is always
|
|
152
292
|
`["**/tests/quarantine/**"]`: the check pins it itself, so this repo,
|
|
@@ -262,11 +402,10 @@ bun run checks-commit-identity <base-ref> <head-ref>
|
|
|
262
402
|
bun run checks-commit-identity <ref>
|
|
263
403
|
```
|
|
264
404
|
|
|
265
|
-
With one argument it checks that commit alone
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
commit and reason. Other trailers and prose mentioning an address in the
|
|
405
|
+
With one argument it checks that commit alone. It refuses a commit
|
|
406
|
+
whose author or committer is outside the allowlist, and one whose
|
|
407
|
+
trailer block carries a `Co-authored-by:` trailer as git parses it, and
|
|
408
|
+
it names the offending commit and reason. Other trailers and prose mentioning an address in the
|
|
270
409
|
body are left alone. `GitHub <noreply@github.com>` is allowed as
|
|
271
410
|
committer only, since that is who writes a squash merge.
|
|
272
411
|
|
|
@@ -279,31 +418,7 @@ owners restates it in `package.json`:
|
|
|
279
418
|
}
|
|
280
419
|
```
|
|
281
420
|
|
|
282
|
-
|
|
283
|
-
current tip to the head is visible:
|
|
284
|
-
|
|
285
|
-
```yaml
|
|
286
|
-
on:
|
|
287
|
-
pull_request:
|
|
288
|
-
types: [opened, edited, synchronize, reopened]
|
|
289
|
-
jobs:
|
|
290
|
-
commit-identity:
|
|
291
|
-
runs-on: ubuntu-latest
|
|
292
|
-
steps:
|
|
293
|
-
- uses: actions/checkout@v5
|
|
294
|
-
with:
|
|
295
|
-
fetch-depth: 0
|
|
296
|
-
- uses: oven-sh/setup-bun@v2
|
|
297
|
-
- run: bun install --frozen-lockfile
|
|
298
|
-
- run: bunx checks-commit-identity "origin/$BASE_REF" "$HEAD_SHA"
|
|
299
|
-
env:
|
|
300
|
-
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
301
|
-
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
The checkout fetches the full history and the range starts at the
|
|
305
|
-
fetched base branch, not the event's recorded base sha, which GitHub
|
|
306
|
-
leaves stale once the base branch advances after the pull request opens.
|
|
421
|
+
`checks-lint` runs it over each pull request's range; see "Lint entry point".
|
|
307
422
|
|
|
308
423
|
## Comment gate
|
|
309
424
|
|
|
@@ -316,37 +431,25 @@ bun run checks-comment-gate <ref>
|
|
|
316
431
|
```
|
|
317
432
|
|
|
318
433
|
With two arguments it diffs the base against the head. With one it diffs
|
|
319
|
-
that commit against its parent,
|
|
320
|
-
|
|
321
|
-
|
|
434
|
+
that commit against its parent, or against the empty tree for a
|
|
435
|
+
repository's first commit, which has none. It exits 2 when the parent
|
|
436
|
+
exists but is not in the clone rather than widening to the whole tree,
|
|
437
|
+
so a shallow checkout running the one-argument form needs
|
|
438
|
+
`fetch-depth: 2`. Only added lines
|
|
322
439
|
are checked, so a violation in a file the diff never touches stays
|
|
323
440
|
silent, and a refusal counts when any line of the comment carrying it was
|
|
324
441
|
added. The check refuses a machine-read directive, a record or ticket
|
|
325
442
|
pointer, a doc block, and a file opening with a rationale block over
|
|
326
|
-
three lines, licence headers excepted
|
|
327
|
-
scanner the gate and the backtest share.
|
|
443
|
+
three lines, licence headers excepted.
|
|
328
444
|
|
|
329
|
-
|
|
330
|
-
|
|
445
|
+
`scripts/comment-matchers.ts` holds the scanner, the comment syntaxes
|
|
446
|
+
and a synchronous `refused()`, and imports nothing, so a host such as a
|
|
447
|
+
hook bundle can copy it alone into a directory with no `node_modules`
|
|
448
|
+
and import it as `@avi2dg/checks/scripts/comment-matchers.ts`. The repo's
|
|
449
|
+
cruise fails when it gains an import. `scripts/comments.ts` wraps the
|
|
450
|
+
same matchers in Effect for the gate and the backtest.
|
|
331
451
|
|
|
332
|
-
|
|
333
|
-
on:
|
|
334
|
-
pull_request:
|
|
335
|
-
types: [opened, edited, synchronize, reopened]
|
|
336
|
-
jobs:
|
|
337
|
-
comment-gate:
|
|
338
|
-
runs-on: ubuntu-latest
|
|
339
|
-
steps:
|
|
340
|
-
- uses: actions/checkout@v5
|
|
341
|
-
with:
|
|
342
|
-
fetch-depth: 0
|
|
343
|
-
- uses: oven-sh/setup-bun@v2
|
|
344
|
-
- run: bun install --frozen-lockfile
|
|
345
|
-
- run: bunx checks-comment-gate "origin/$BASE_REF" "$HEAD_SHA"
|
|
346
|
-
env:
|
|
347
|
-
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
348
|
-
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
349
|
-
```
|
|
452
|
+
`checks-lint` runs it over each pull request's range; see "Lint entry point".
|
|
350
453
|
|
|
351
454
|
## Suppressions ratchet
|
|
352
455
|
|
|
@@ -362,8 +465,9 @@ bun run checks-suppressions-ratchet <base-ref> <head-ref>
|
|
|
362
465
|
bun run checks-suppressions-ratchet <ref>
|
|
363
466
|
```
|
|
364
467
|
|
|
365
|
-
With one argument it compares that commit with its parent,
|
|
366
|
-
|
|
468
|
+
With one argument it compares that commit with its parent, or with the
|
|
469
|
+
empty tree for a repository's first commit, and exits 2 when the parent
|
|
470
|
+
exists but is not in the clone. With two it reads the base where
|
|
367
471
|
the head branched off, at their merge-base, so a count the base branch
|
|
368
472
|
lowered since does not read as a rise at the head. A count that fell and
|
|
369
473
|
an entry that went both pass. A commit without the file counts as empty,
|
|
@@ -378,27 +482,7 @@ suppressions-ratchet: 2 count(s) in oxlint-suppressions.json rose or appeared; f
|
|
|
378
482
|
src/dispatch.ts typescript/no-non-null-assertion rose from 12 to 13
|
|
379
483
|
```
|
|
380
484
|
|
|
381
|
-
|
|
382
|
-
current tip to the head is visible:
|
|
383
|
-
|
|
384
|
-
```yaml
|
|
385
|
-
on:
|
|
386
|
-
pull_request:
|
|
387
|
-
types: [opened, edited, synchronize, reopened]
|
|
388
|
-
jobs:
|
|
389
|
-
suppressions-ratchet:
|
|
390
|
-
runs-on: ubuntu-latest
|
|
391
|
-
steps:
|
|
392
|
-
- uses: actions/checkout@v5
|
|
393
|
-
with:
|
|
394
|
-
fetch-depth: 0
|
|
395
|
-
- uses: oven-sh/setup-bun@v2
|
|
396
|
-
- run: bun install --frozen-lockfile
|
|
397
|
-
- run: bunx checks-suppressions-ratchet "origin/$BASE_REF" "$HEAD_SHA"
|
|
398
|
-
env:
|
|
399
|
-
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
400
|
-
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
401
|
-
```
|
|
485
|
+
`checks-lint` runs it over each pull request's range; see "Lint entry point".
|
|
402
486
|
|
|
403
487
|
## CI wiring
|
|
404
488
|
|
|
@@ -408,14 +492,14 @@ that: a workflow whose lint step became a no-op leaves `bun run lint`
|
|
|
408
492
|
green.
|
|
409
493
|
|
|
410
494
|
The repository declares its gates once, in `package.json`, and `lint`
|
|
411
|
-
runs the check
|
|
495
|
+
runs the check through `checks-lint`:
|
|
412
496
|
|
|
413
497
|
```json
|
|
414
498
|
"ciWiring": {
|
|
415
499
|
"gates": ["bun run lint", "bun run typecheck", "bun run test"]
|
|
416
500
|
},
|
|
417
501
|
"scripts": {
|
|
418
|
-
"lint": "oxlint --type-aware && checks-lint
|
|
502
|
+
"lint": "oxlint --type-aware && checks-lint"
|
|
419
503
|
}
|
|
420
504
|
```
|
|
421
505
|
|
|
@@ -450,8 +534,17 @@ supported way to wire a gate: it is not read, so a gate must run as a
|
|
|
450
534
|
`run:` step, such as `bunx checks-comment-gate`, in the repo's own
|
|
451
535
|
workflows.
|
|
452
536
|
|
|
537
|
+
A step running `checks-lint` also counts for a declared gate that calls
|
|
538
|
+
one of the gates `checks-lint` runs by its bare bin name, when the step
|
|
539
|
+
calls `checks-lint` the same way: `bunx checks-lint` counts for
|
|
540
|
+
`bunx checks-comment-gate "origin/$BASE_REF" "$HEAD_SHA"`. A step running `bun run lint` counts only for the `bun run lint` gate,
|
|
541
|
+
since the check never reads what a package script runs. So once `lint`
|
|
542
|
+
runs `checks-lint`, the per-gate entries can leave `ciWiring.gates`
|
|
543
|
+
along with the workflows that ran them.
|
|
544
|
+
|
|
453
545
|
The default branch is `main`; a repo with another one sets
|
|
454
|
-
`"defaultBranch"` beside `"gates"
|
|
546
|
+
`"defaultBranch"` beside `"gates"`, which `checks-lint` also reads when
|
|
547
|
+
`origin/HEAD` is not set.
|
|
455
548
|
|
|
456
549
|
It exits 1 naming each gap, with every step that runs the gate and why
|
|
457
550
|
that step does not count:
|
|
@@ -466,6 +559,55 @@ It exits 2 when `package.json` declares no gates, a gate is not one
|
|
|
466
559
|
plain command, or a workflow does not parse. Whether a workflow is
|
|
467
560
|
well formed is actionlint's question, not this one's.
|
|
468
561
|
|
|
562
|
+
### Gate selection
|
|
563
|
+
|
|
564
|
+
A repository with no TypeScript source gives `checks-lint-coverage` and
|
|
565
|
+
`checks-test-layout` nothing to check, and test-layout still refuses its
|
|
566
|
+
missing `bun test` script and `bunfig.toml`. It declares the gates
|
|
567
|
+
`checks-lint` runs as `lintGates`, beside `gates`:
|
|
568
|
+
|
|
569
|
+
```json
|
|
570
|
+
"ciWiring": {
|
|
571
|
+
"gates": ["bun run lint"],
|
|
572
|
+
"lintGates": [
|
|
573
|
+
"checks-commit-identity",
|
|
574
|
+
"checks-comment-gate",
|
|
575
|
+
"checks-suppressions-ratchet",
|
|
576
|
+
"checks-ci-wiring"
|
|
577
|
+
]
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
`checks-lint` runs exactly those, in the "Lint entry point" table's
|
|
582
|
+
order, and all six when `lintGates` is absent. A step running
|
|
583
|
+
`checks-lint` then counts only for a declared gate that `lintGates`
|
|
584
|
+
keeps.
|
|
585
|
+
|
|
586
|
+
A selection may leave out only a gate that does not apply:
|
|
587
|
+
|
|
588
|
+
| Gate | Applies when the repository |
|
|
589
|
+
| --- | --- |
|
|
590
|
+
| `checks-lint-coverage` | tracks a `.ts` or `.tsx` file |
|
|
591
|
+
| `checks-test-layout` | tracks a `.ts` or `.tsx` file |
|
|
592
|
+
| `checks-commit-identity` | always |
|
|
593
|
+
| `checks-comment-gate` | always |
|
|
594
|
+
| `checks-suppressions-ratchet` | always |
|
|
595
|
+
| `checks-ci-wiring` | always |
|
|
596
|
+
|
|
597
|
+
Both bins exit 2 on a `lintGates` that names an unknown gate or leaves
|
|
598
|
+
out one that always applies. ci-wiring exits 1 when the
|
|
599
|
+
selection leaves out a gate the repository's tracked files make
|
|
600
|
+
applicable, and names the gate and the files:
|
|
601
|
+
|
|
602
|
+
```
|
|
603
|
+
ci-wiring: ciWiring.lintGates leaves out 2 gate(s) this repository's contents make applicable:
|
|
604
|
+
checks-lint-coverage: the repository tracks TypeScript source (src/widget.ts)
|
|
605
|
+
checks-test-layout: the repository tracks TypeScript source (src/widget.ts)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
It reads the files tracked at the checkout, so the pull request that
|
|
609
|
+
adds the first TypeScript file is the one refused.
|
|
610
|
+
|
|
469
611
|
### Limits
|
|
470
612
|
|
|
471
613
|
The check reads workflow files and never runs them, so it deliberately
|
|
@@ -577,12 +719,28 @@ The shared Stryker preset's `json` reporter writes
|
|
|
577
719
|
not resolve `extends` in a JSON config, but a `.mjs` config that
|
|
578
720
|
spreads an imported object consumes it. Keys the consumer sets after
|
|
579
721
|
the spread win.
|
|
722
|
+
- The `.ts` bins are written in Effect, so `effect` is a peer dependency
|
|
723
|
+
and `@effect/platform-bun`, which only the bins use, is a dependency.
|
|
724
|
+
`@effect/platform-node-shared` is a direct dependency at the same exact
|
|
725
|
+
version only to pin it: `@effect/platform-bun` asks for it with a `^`
|
|
726
|
+
range, and a newer rc peers on a newer `effect` than consumers install,
|
|
727
|
+
so all three move together.
|
|
580
728
|
- Each runnable script ships a `checks-` bin entry, so consumer
|
|
581
729
|
`package.json` scripts call the short name, which the package manager
|
|
582
730
|
puts on `PATH` only there; a shell runs it through `bun run`, which
|
|
583
731
|
never falls back to the registry the way `bunx` does. The `.ts` checks
|
|
584
732
|
keep a `bun` shebang, which needs no build step and no `dist/`
|
|
585
733
|
entry, unlike the oxlint plugin that node loads.
|
|
734
|
+
- `checks-lint` runs each gate as its own bin in a child process rather
|
|
735
|
+
than importing it, so a gate behaves the same called alone or through
|
|
736
|
+
the entry point, and `lint-coverage.sh` stays a shell script. The
|
|
737
|
+
gates run one at a time with their output passed straight through, so
|
|
738
|
+
each report reads whole and in the table's order.
|
|
739
|
+
- A gate selection is checked against the repository's contents rather
|
|
740
|
+
than trusted, so it cannot skip a gate that applies. ci-wiring does
|
|
741
|
+
that check, which is why a selection without it, or without another
|
|
742
|
+
gate that applies everywhere, is refused as `checks-lint` reads it:
|
|
743
|
+
nothing would check the selection otherwise.
|
|
586
744
|
- `checks-ci-wiring` runs inside `lint`, not in a workflow of its own:
|
|
587
745
|
deleting the step that runs a check is the violation it catches, so the
|
|
588
746
|
local `lint` is where it has to fail.
|
|
@@ -593,10 +751,11 @@ The shared Stryker preset's `json` reporter writes
|
|
|
593
751
|
`@types/bun`, which would otherwise make every runtime `bun` import look
|
|
594
752
|
like a dev-only dependency.
|
|
595
753
|
- The pull request merge commit GitHub builds is authored by `GitHub
|
|
596
|
-
<noreply@github.com>`, which commit-identity refuses as an author.
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
754
|
+
<noreply@github.com>`, which commit-identity refuses as an author.
|
|
755
|
+
`checks-lint` ends a pull request's range at the event's head sha, so
|
|
756
|
+
the merge commit is never in it. A `lint` that calls
|
|
757
|
+
`checks-commit-identity HEAD` itself checks out
|
|
758
|
+
`github.event.pull_request.head.sha` instead of the default merge ref.
|
|
600
759
|
- `no-deep-imports` judges the import specifier, never the resolved file.
|
|
601
760
|
The base honours `exports` maps, so a subpath the map publishes resolves
|
|
602
761
|
and passes, one it omits fails to resolve and is reported, and a package
|
|
@@ -622,7 +781,7 @@ a tag off `main` and reruns the build, `dist/` check, lint, typecheck
|
|
|
622
781
|
and tests before it publishes:
|
|
623
782
|
|
|
624
783
|
```sh
|
|
625
|
-
git tag v0.
|
|
784
|
+
git tag v0.9.0 && git push origin v0.9.0
|
|
626
785
|
```
|
|
627
786
|
|
|
628
787
|
The `release` workflow publishes the tagged version through npm
|
package/oxlintrc.json
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"typescript/no-unnecessary-type-parameters": "error",
|
|
15
15
|
"typescript/use-unknown-in-catch-callback-variable": "error",
|
|
16
16
|
"eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_", "ignoreRestSiblings": true }],
|
|
17
|
-
"typescript/no-unsafe-type-assertion": "
|
|
18
|
-
"typescript/no-non-null-assertion": "
|
|
17
|
+
"typescript/no-unsafe-type-assertion": "error",
|
|
18
|
+
"typescript/no-non-null-assertion": "error",
|
|
19
19
|
"typescript/consistent-return": "off"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avi2dg/checks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Deterministic checks shared across the captain's TypeScript repos",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,10 +16,15 @@
|
|
|
16
16
|
"commitlint.config.js",
|
|
17
17
|
"dependency-cruiser.config.js",
|
|
18
18
|
"scripts/lint-coverage.sh",
|
|
19
|
+
"scripts/main.ts",
|
|
20
|
+
"scripts/git.ts",
|
|
21
|
+
"scripts/gates.ts",
|
|
22
|
+
"scripts/lint.ts",
|
|
19
23
|
"scripts/test-layout.ts",
|
|
20
24
|
"scripts/commit-identity.ts",
|
|
21
25
|
"scripts/mutation-compare.ts",
|
|
22
26
|
"scripts/ci-wiring.ts",
|
|
27
|
+
"scripts/comment-matchers.ts",
|
|
23
28
|
"scripts/comments.ts",
|
|
24
29
|
"scripts/comment-gate.ts",
|
|
25
30
|
"scripts/suppressions-ratchet.ts",
|
|
@@ -34,10 +39,12 @@
|
|
|
34
39
|
"./commitlint.config.js": "./commitlint.config.js",
|
|
35
40
|
"./dependency-cruiser.config.js": "./dependency-cruiser.config.js",
|
|
36
41
|
"./scripts/lint-coverage.sh": "./scripts/lint-coverage.sh",
|
|
42
|
+
"./scripts/lint.ts": "./scripts/lint.ts",
|
|
37
43
|
"./scripts/test-layout.ts": "./scripts/test-layout.ts",
|
|
38
44
|
"./scripts/commit-identity.ts": "./scripts/commit-identity.ts",
|
|
39
45
|
"./scripts/mutation-compare.ts": "./scripts/mutation-compare.ts",
|
|
40
46
|
"./scripts/ci-wiring.ts": "./scripts/ci-wiring.ts",
|
|
47
|
+
"./scripts/comment-matchers.ts": "./scripts/comment-matchers.ts",
|
|
41
48
|
"./scripts/comments.ts": "./scripts/comments.ts",
|
|
42
49
|
"./scripts/comment-gate.ts": "./scripts/comment-gate.ts",
|
|
43
50
|
"./scripts/suppressions-ratchet.ts": "./scripts/suppressions-ratchet.ts",
|
|
@@ -48,6 +55,7 @@
|
|
|
48
55
|
"./dist/index.js": "./dist/index.js"
|
|
49
56
|
},
|
|
50
57
|
"bin": {
|
|
58
|
+
"checks-lint": "scripts/lint.ts",
|
|
51
59
|
"checks-lint-coverage": "scripts/lint-coverage.sh",
|
|
52
60
|
"checks-test-layout": "scripts/test-layout.ts",
|
|
53
61
|
"checks-commit-identity": "scripts/commit-identity.ts",
|
|
@@ -59,7 +67,7 @@
|
|
|
59
67
|
},
|
|
60
68
|
"scripts": {
|
|
61
69
|
"build": "bun build effect-channel/index.ts --outdir dist --target node --format esm",
|
|
62
|
-
"lint": "oxlint --type-aware &&
|
|
70
|
+
"lint": "oxlint --type-aware && bun scripts/lint.ts && depcruise --config .dependency-cruiser.cjs .",
|
|
63
71
|
"typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict",
|
|
64
72
|
"test": "bun test --randomize"
|
|
65
73
|
},
|
|
@@ -70,15 +78,14 @@
|
|
|
70
78
|
"bun run lint",
|
|
71
79
|
"bun run typecheck",
|
|
72
80
|
"bun run test",
|
|
73
|
-
"./node_modules/.bin/commitlint"
|
|
74
|
-
"bun .checks/scripts/commit-identity.ts",
|
|
75
|
-
"bun .checks/scripts/comment-gate.ts"
|
|
81
|
+
"./node_modules/.bin/commitlint"
|
|
76
82
|
]
|
|
77
83
|
},
|
|
78
84
|
"peerDependencies": {
|
|
79
85
|
"@swc/core": "1.16.2",
|
|
80
86
|
"dependency-cruiser": "18.4.0",
|
|
81
87
|
"@effect/tsgo": "0.45.0",
|
|
88
|
+
"effect": "4.0.0-rc.115",
|
|
82
89
|
"oxlint": "1.83.0",
|
|
83
90
|
"oxlint-tsgolint": "7.0.2002"
|
|
84
91
|
},
|
|
@@ -95,6 +102,8 @@
|
|
|
95
102
|
},
|
|
96
103
|
"dependencies": {
|
|
97
104
|
"@commitlint/cli": "21.2.3",
|
|
98
|
-
"@commitlint/config-conventional": "21.2.3"
|
|
105
|
+
"@commitlint/config-conventional": "21.2.3",
|
|
106
|
+
"@effect/platform-bun": "4.0.0-rc.115",
|
|
107
|
+
"@effect/platform-node-shared": "4.0.0-rc.115"
|
|
99
108
|
}
|
|
100
109
|
}
|