Why this matters
NixOS/nixpkgs is the largest non-trivial OSS monorepo on GitHub — ~150-180k files at full checkout, ~80,000 package builds across x86_64-linux / aarch64-linux / x86_64-darwin / aarch64-darwin, 20,678 by-name package directories, a 30,841-line maintainer registry. The other 20 case studies in alint’s catalogue all sit below ~80k files; pytorch was the largest at ~80k. nixpkgs is the case where alint’s “any size repo” pitch becomes empirically defensible by measurement rather than assertion.
This is the scale-validation flagship.
Headline catch
At 39,101 files and 20,678 by-name package directories, alint’s full 79-rule structural check pass over the entire sparse tree completes in 273 ms wall-clock.
That’s ~100× faster than
nix-build ci -A parse(the fastest existing structural-validation step in nixpkgs CI) and ~300× faster than the treefmt umbrella. Thefor_each_dirprimitive, swept across 20,678 package directories with onefile_existsrequire each, is not visibly the slow part of a hot-cache pass.Proof — not assertion — that alint scales gracefully to the largest reasonable-shape OSS monorepo without per-repo perf tuning.
Where alint earns its keep here
for_each_dirover 20,678 directories scales gracefully. The headline rule (nixpkgs-by-name-prefix-dirs-have-package) iterates everypkgs/by-name/<2-letter>/<pkg>/and checkspackage.nixpresence. The dominant cost is the gitignore-respecting walk (already parallelised), not the per-iteration require dispatch.- The bundled rulesets work without modification at this scale.
oss-baseline + ci/github-actions + hygiene/no-tracked-artifacts + tooling/editorconfigneed noscope_filterdiscipline at nixpkgs’s tree size. They surface 2 legitimate violations (real Ruby bundler caches underpkgs/by-name/{pt,re}/{pt,redis-dump}/.bundle/) with zero false positives. - alint complements rather than replaces the in-tree
ci/Nix-evaluation framework. nixpkgs’s existing tooling owns the attribute-set side:nix-instantiate --parse, attribute-walk, hash mismatch detection, the Hydra eval matrix, the merge-bot,nixpkgs-vet’s by-name attribute resolution. alint owns the file-shape side and acts as a sub-second fast-fail PR-time signal beneath the slower Nix-eval passes. - Two registries point at the strongest demand signal in the v0.10 candidate list. Every package’s
meta.maintainersfield references handles defined inmaintainers/maintainer-list.nix(30,841 lines); everymeta.licensefield references SPDX identifiers inlib/licenses/licenses.nix(1,674 lines). Plusci/OWNERSdeclaring team-routing patterns. This is the 6th confirmation ofregistry_paths_resolveas v0.10’s strongest demand signal — and the strongest single-repo example of the registry-cross-reference shape. nested_configs: trueis shovel-ready at this scale. Each by-name package directory is effectively its own subtree; per-package.alint.ymlfiles would let package-level assertions layer on top of the root config without bloating it, and the LSP-server cache-invalidation story (an v0.10 design consideration) becomes much cleaner.
Future story angles
- Once
registry_paths_resolveships in v0.10, nixpkgs becomes the showcase for cross-file registry validation at scale: bothmeta.maintainersandmeta.licenseresolve through declarative rules rather than Nix evaluation. - nixpkgs is the empirical anchor for the v0.10 LSP-server design memo: 273 ms is fine on every editor save; on every keystroke would feel sluggish (~0.5-1 s perceived latency). The LSP server should incrementalise rule evaluation by file-set — the
for_each_diroverpkgs/by-name/*/*is the canonical test fixture for cache-invalidation correctness. - The pitch lands hardest paired with the by-name finding: a single declarative one-liner asserts the file-shape invariant across 20,678 package directories with one require — replacing a hand-rolled walk in
nixpkgs-vet(the file side; the attribute side stays onnixpkgs-vet).