Adopting polylint in a polyglot repo
This guide is for a repository that already has ruff, eslint or oxlint, prettier, a hook framework and per-language CI jobs, and wants one gate instead.
-
Import the existing configs
Section titled “Import the existing configs”Terminal window poly migrate # dry run: what it would import, and what it would deletepoly migrate --write # apply it (--allow-dirty on a dirty tree)poly migrate --recurse # every package of a monorepopoly migratereads ruff, taplo, markdownlint and typos configs (includingpyproject.toml’s[tool.ruff],[tool.typos],[tool.codespell]), and turns a.pre-commit-config.yamlorlefthook.ymlinto[hooks],[hooks.builtin]and[tools]entries — mapping jobs such as golangci-lint, hadolint or actionlint onto their recipes. It deletes a source only when nothing in it was lost;--verifyre-runs polylint afterwards. There is no eslint or prettier importer; an existing.oxlintrc.jsonor.oxfmtrc.jsonis read directly. -
See what each language gets
Section titled “See what each language gets”Run
poly lint --verbose .and read the summary. Every file is either checked or listed as skipped with a reason, such asno lint rules for Zig. Compare that with what your current tools cover; the language table shows how each language runs. Gaps that commonly matter:Language What to enable Go Lint needs the golangci-lintrecipe (and a Go toolchain); formatting uses the hostgofmt.Rust cargo clippyand friends run in the whole-project phase from[hooks]; formatting uses the hostrustfmt.Java Lint needs the checkstylerecipe and a host JRE; formatting needsgoogle-java-format(host) orpalantir-java-format(recipe).Ruby rubyfmt formats; lint is the quality tier and ast-grep pack only. Kotlin, Swift, Zig, Dart, Gleam, R Formatting needs the host tool, opt-in; otherwise tree-sitter. Protobuf, Starlark, Helm, GitHub Actions Recipes: buf,buildifier,helm,actionlint. -
Tune in one place
Section titled “Tune in one place”Put shared settings in the root
poly.toml. A nestedpoly.tomldeep-merges over its ancestors for the files beneath it, so a package can tune rules without repeating the baseline;root = truestops the cascade.extendsshares a baseline across repositories. See Nested config in a monorepo. -
Replace the hook framework
Section titled “Replace the hook framework”Terminal window poly hooks installA foreign hook already in place is kept as
<hook>.legacyand run before polylint; a lefthook hook is kept but not run (--replace-lefthookdiscards it). Hooks check a snapshot of the staged index, so unstaged edits neither hide nor cause failures. See Git hooks. -
Roll out in CI
Section titled “Roll out in CI”.github/workflows/ci.yaml - uses: actions/checkout@v4- uses: Goldziher/polylint@v0with:version: from-config # the release required_version in poly.toml allowscomponents: auto # pre-install and cache what this repository needs- run: poly fmt .- run: poly lint --max-skips 25 .Start with
--max-skips <N>set to today’s count of charged skips, then lower it to zero and switch to--deny-skips. Either way, a job that silently stops covering part of the tree fails with exit2. New findings from the quality tier and the ast-grep pack are warnings and do not fail the job.
Runners without network access
Section titled “Runners without network access”- Use the bundled Linux x86_64 musl build, which downloads no components; or
- on a core build, run
poly components sync .on a connected machine and seed the runner withpoly components import <archive>, or pointPOLY_COMPONENT_MIRRORat an internal mirror. - Run with
--offline(orPOLY_OFFLINE=1) so nothing is attempted. Recipe tools need their binaries installed or cached the same way.