@avi2dg/checks
v0.42.0
Published
Deterministic checks shared across a set of TypeScript repositories
Readme
checks
@avi2dg/checks is the kit of deterministic checks a TypeScript repository installs to hold its code, tests, commits, CI wiring and docs to one shared standard.
It ships the lint gates checks-lint runs over each pull request, the test runners, and a defineConfig for oxlint, Knip and dependency-cruiser.
It also ships the configs a repository extends for tsc, commitlint, bun and Stryker.
Each repository owns its workflows and native tool configs, as Native settings maps.
Before you begin
- A git repository, whose history the range gates read.
- Bun 1.4.3, which runs every bin.
- The peer dependencies, at the exact versions the kit pins:
@effect/tsgo0.45.0@swc/core1.16.2dependency-cruiser18.4.0effect4.0.0jscpd5.3.2oxlint1.83.0oxlint-tsgolint7.0.2002typescript7.0.2
- The optional peers, which only an opt-in check loads, at the exact versions the kit pins:
@axe-core/playwright4.13.0html-validate11.16.0playwright-core1.63.0postcss-html2.0.0stylelint17.16.0
Install
To consume the kit from a repository:
Add the kit and its peers:
bun add -d @avi2dg/checks @effect/[email protected] @swc/[email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]Write
oxlint.config.tswith the kit's builder, saying whether the sources are Effect programs:import { defineConfig } from "@avi2dg/checks/oxlint"; export default defineConfig({ effect: true });Keep oxlint out of
node_modules/through.gitignore:node_modules/Extend the tsconfig fragment in
tsconfig.json:{ "extends": "@avi2dg/checks/tsconfig.effect.json" }Copy the bunfig preset:
cp node_modules/@avi2dg/checks/bunfig.toml bunfig.tomlName the files nothing imports in
knip.config.ts:import { defineConfig } from "@avi2dg/checks/knip"; export default defineConfig({ entry: ["src/index.ts"] });Write
dependency-cruiser.config.tsonly when the kit's import rules need an entry point or a boundary of the repository's own:import { defineConfig } from "@avi2dg/checks/dependency-cruiser"; export default defineConfig({ orphans: ["^src/bin[.]ts$"] });Add scripts to
package.json, replacing the build entry with the repository's own build command:"build": "bun build src/index.ts --outdir dist --target node && checks-effect-scope", "lint": "oxlint && checks-lint", "typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict", "test": "checks-test"Write the Bun version Before you begin names to
.bun-version, which checks-ci-wiring holds every setup-bun step to.Run the scripts on every pull request in
.github/workflows/ci.yml, fetching the whole history the range needs:on: pull_request: jobs: checks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: oven-sh/setup-bun@v2 with: bun-version-file: .bun-version - run: bun install --frozen-lockfile - run: bun run build - run: git diff --exit-code - run: bun run lint - run: bun run typecheck - run: bun run test
Lint the pull request title in the same job with one step:
- id: title
name: lint the title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
GH_TOKEN: ${{ github.token }}
run: bunx checks-commit-titleA repository whose branch protection requires commitlint keeps that required status name while commitlint.yml goes away.
The same job reports the title step's outcome under that name on every run, which needs statuses: write:
- name: report the title as commitlint
if: always()
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
OUTCOME: ${{ steps.title.outcome }}
run: |
state=failure
if [ "$OUTCOME" = success ]; then state=success; fi
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" --silentA private repository sets runs-on: ${{ vars.CI_RUNS_ON || 'ubuntu-latest' }} on each job, as checks-ci-wiring requires.
checks-effect-scope writes the Effect paths of oxlint.config.ts and the kit's severities into tsconfig.json, as The Effect rules says.
bun run lint then ends with checks-lint: <count> gate(s) pass.
What runs
checks-lint runs each of these gates that applies to the repository, and names every one that fails.
A gate reads either the working tree or the range checks-lint resolves.
The table groups the gates by vector, the part of a repository each one judges.
| Vector | Gate | Reads | Runs in |
| --- | --- | --- | --- |
| complexity | checks-suppressions-ratchet | the range | every repository |
| complexity | checks-repetition | the range | a repository tracking *.ts or *.tsx |
| complexity | checks-unused | the working tree | a repository tracking *.ts or *.tsx or *.astro |
| complexity | checks-exports | the range | a repository tracking *.ts or *.tsx |
| quality | checks-lint-coverage | the working tree | a repository tracking *.ts or *.tsx or *.astro |
| quality | checks-comment-gate | the range | every repository |
| quality | checks-effect-scope | the working tree | a repository tracking oxlint.config.ts |
| quality | checks-frontend-syntax | the working tree | a repository tracking frontend-syntax.json |
| testing | checks-test-layout | the working tree | a repository tracking *.ts or *.tsx |
| testing | checks-quarantine-clock | the range | every repository |
| docs | checks-docs | the range, and every agent file at the head commit | every repository |
| delivery | checks-commit-identity | the range | every repository |
| delivery | checks-ci-wiring | the working tree | every repository |
| delivery | checks-secrets | the range | every repository |
| dependencies | checks-imports | the working tree | a repository tracking *.ts or *.tsx |
| dependencies | checks-advisories | the range | a repository tracking bun.lock |
These bins run on their own:
checks-testruns the suite asscripts.testand refuses a skip without a reason at its test site.checks-flakeruns the suite on a schedule and records the seeds a flaky test fails with.checks-mutationruns Stryker for scoped checks and refuses a full run outside CI.checks-mutation-comparecompares the mutants of two reports and lists each one that regresses.checks-mutation-baselinerestores the newest mutation baseline frommain, saves it on a self-hosted runner's disk in place of an artifact, and deletes superseded baselines after amainrun.checks-subsumed-testslists each test another test subsumes in a mutation run.checks-changelogwrites the pending release intoCHANGELOG.mdfrom the conventional commits since the last release.checks-release-noteswrites oneCHANGELOG.mdsection to a file for a GitHub release.checks-release-reporttells whether the history holds unreleased features or fixes since the last tag.checks-release-propens or refreshes the pull request that releases the next version, and dispatches its checks.checks-release-tagtags a landed release commit with its version and dispatches the release workflow on the tag.checks-vendorpins each library itspreparearguments name to a shared read-only clone and links it underrepos/.checks-browseropens a product's built pages in Chrome and fails on the layout, keyboard, motion, accessibility, nesting and asset checks itsbrowser-checks.jsondeclares.checks-commit-titlelints the pull request title in one step of thecijob.checks-docs-onlytells whether a change touches only top-level Markdown and Markdown underdocs/, so a docs-only change skips the full run.
checks-lint has its own page, which says which range it resolves.
oxlint runs through its own tool, as the pages under Related topics say.
Upgrade
To move a repository to a newer release of the kit:
- Run the install line again, which moves the kit to its newest release and the peers to the versions it pins.
A repository that opted in to
checks-browserorchecks-frontend-syntaxalso reruns thebun addline on its page. - Run
bun run build, whosechecks-effect-scoperewrites the Effect override and the kit's severities intsconfig.jsonwhen a release changes them. A severity key the release drops stays intsconfig.jsonuntil the repository removes it. - Copy
node_modules/@avi2dg/checks/bunfig.tomloverbunfig.tomlagain, sincechecks-test-layoutcompares the copy with the installed preset. - Run
bun run lint,bun run typecheckandbun run test.
The repository's lockfile pins the kit, so a repository moves only when it runs these steps. CHANGELOG.md, shipped in the package, lists what each release changed.
Where things are
Every path is relative to the installed package, node_modules/@avi2dg/checks/.
| Path | What it holds |
| --- | --- |
| CHANGELOG.md | every release, and what it changed |
| docs/ | a reference page per bin and per shared config, and why the kit is shaped this way |
| bunfig.toml | the bunfig preset a repository copies |
| commitlint.config.js | the shared commitlint config |
| dependency-cruiser.config.js | the kit's dependency-cruiser rules as a base a .dependency-cruiser.cjs extends by path, which the build writes |
| knip-base.json | the Knip include setting, for a configuration that spreads it |
| 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/ |
| dist/ | the compiled oxlint plugins, the config builders @avi2dg/checks/oxlint, @avi2dg/checks/knip and @avi2dg/checks/dependency-cruiser resolve to, and the doc templates, one template per kind of doc file |
| oxlintrc.json | the oxlint base as a config a .oxlintrc.json extends by path, which the build writes |
| stryker.preset.js | the Stryker mutation-testing preset, which refuses a full run outside CI |
| tsconfig.effect.json | the tsconfig fragment with the shared compiler options and the Effect language-service block |
| ts-reset.d.ts | the two ts-reset rules tsconfig.effect.json lists in files |
