name: code-quality description: Run, fix, and extend the repo's per-language lint + static-analysis + style toolchain (ESLint/Prettier, Ruff/Bandit/mypy, RuboCop, ShellCheck/shfmt, yamllint/actionlint, Stylelint, markdownlint). Use when a contributor asks to "lint", "format", "fix the lint errors", set up a linter for a new language, understand why a lint rule is disabled, or add a check to the pre-commit hook. Also use before opening a PR to confirm the local lint is green.
Code quality toolchain
Every language has a linter + static-analyzer + style tool, configured to pass at a strong-but-pragmatic strength. The heavyweight toolchain is platform-internal; in this consumer the checks run locally (npm run lint or each tool directly) and as a staged-file pre-commit guard (scripts/lint-staged.sh), the only consumer lint backstop. The canonical reference — toolchain table, every deliberate rule relaxation, and the RuboCop-out-of-Gemfile rationale — lives in AGENTS.md → "Code quality". Read it before changing any lint config.
Run the checks
npm ci # JS/CSS/MD tools live in node_modules
npm run lint # eslint + stylelint + markdownlint
npm run format # prettier --write (JS only)
# Python (tools via pip; CI pins ruff/bandit/mypy)
ruff check && ruff format --check && mypy && bandit -r scripts tests -c pyproject.toml
# Ruby — standalone, Ruby >= 3.3 (NOT bundle exec; see below)
gem install rubocop:1.86.2 rubocop-performance:1.26.1 && rubocop
# Shell / YAML
shellcheck $(git ls-files '*.sh') .githooks/pre-commit
shfmt -i 2 -ci -bn -d $(git ls-files '*.sh') .githooks/pre-commit
yamllint .github/
actionlint -ignore '"github\.(event\.pull_request\.head\.ref|head_ref)" is potentially untrusted'
Gotchas (learned the hard way)
- RuboCop is NOT in the site
Gemfile. Its transitiveparalleldep needs Ruby >= 3.3, butvalidate-content/unit/generate/deploy-preview/the e2e web-server install theGemfileviaruby/setup-rubyon Ruby 3.2 — aGemfilegroup madebundle installfail on 3.2 before any step ran. Install RuboCop standalone on Ruby 3.3. Keep dev-only linters out of the runtimeGemfile. - The full e2e suite enforces bespoke lint-tests (
e2e/silent-catch-lint.test.js,e2e/parity-tag-lint.test.js,admin-css-banned-patterns, etc.) that the per-file linters don't know about. A Prettier line-wrap can move a// @parity-lint-allow:annotation off its target line, or expose a latent silent.catch(() => {}). After a broad reformat, run the full e2e suite (or at least those.test.jsfiles) —select-specs.jsonly runs them when base.js/config/package.json change. - ESLint
detect-*-regexpare warnings, not errors (linear regexes over trusted input). Don't try to drive warnings to zero; the gate is errors-only. - No consumer lint CI. The heavyweight lint toolchain is platform-internal; in this consumer the pre-commit hook (
scripts/lint-staged.sh) is the only lint backstop. Run the tools locally to debug a failure.
Adding a new language / file type
- Pick the best-in-class linter; add it to the right manifest (npm
package.jsonfor JS-runtime tools; pip for Python-family; binary download for compiled tools). - Create its config file (prefer a dedicated dotfile; only Python config lives in
pyproject.toml). - Add a per-language branch to
scripts/lint-staged.sh(tool-availability-gated so missing tools skip, never block). - Document the toolchain row + any rule relaxations in AGENTS.md → "Code quality".
- Relaxations get a comment explaining why; never disable a rule to hide a real bug.