Feature Roadmap: Ecosystem-Inspired Improvements
After surveying how 8+ ecosystems handle shared linter/formatter configuration, here is a prioritized set of features and improvements to consider for ruff-sync.
Ecosystems Surveyed
| Ecosystem |
Tool |
Mechanism |
Key Insight |
| JavaScript |
ESLint |
extends: ["my-org-config"] via npm; flat config imports arrays |
Composable config arrays with explicit precedence |
| JavaScript |
Prettier |
"prettier": "@my-org/prettier-config" in package.json |
npm package as single source of truth |
| JavaScript |
Stylelint |
extends: "stylelint-config-standard" via npm |
Same extends + overrides pattern as ESLint |
| Ruby |
RuboCop |
inherit_from: https://raw.githubusercontent.com/.../rubocop.yml |
Native remote URL inheritance — closest analog to ruff-sync |
| Go |
golangci-lint |
--config path/to/file; no native remote extends |
Teams resort to CI scripts that fetch configs — the exact gap ruff-sync fills |
| Rust |
rustfmt/clippy |
Parent-directory search; [workspace.lints] inheritance |
Only works within monorepos; cross-repo sharing needs external tooling |
| Python |
Ruff (extend) |
extend = "../parent/ruff.toml" — local paths only |
astral-sh/ruff#12352 requests remote URL support |
| Python |
pre-commit |
No include; each repo needs its own .pre-commit-config.yaml |
Same config centralization gap |
| Multi-lang |
Projen |
Generates project files from typed definitions |
"Config-as-code" generator model |
RuboCop's inherit_from with remote URLs is the closest native peer to what ruff-sync does. If Ruff ever adopts remote extend (ruff#12352), ruff-sync would still add value through selective exclusions, format-preserving merges, check/diff commands, and multi-tool support.
🟢 Tier 1: High Impact, Natural Extensions
1. ✅ Config inheritance / layering (like ESLint flat config)
Allow composing config from multiple upstream sources with explicit precedence.
[tool.ruff-sync]
upstream = [
"https://github.com/my-org/base-standards", # base layer
"https://github.com/my-org/python-web-standards", # domain layer (overrides base)
]
Why: Organizations often have a base config + domain-specific overlays (web, data science, CLI tools). ESLint's flat config is an ordered array; RuboCop's inherit_from accepts a list.
2. ✅ Drift detection with richer CI output
Enhance check command for CI consumption.
--output-format json|sarif|github for machine-readable output
- GitHub Actions annotations (
::error file=...) to surface drift directly on PRs
- Exit code differentiation (e.g., 1 = out of sync, 2 = upstream unreachable)
Inspiration: golangci-lint outputs SARIF, JSON, and GitHub annotations; ESLint supports --format json and custom formatters.
3. Lock/pin upstream versions
Pin to a specific commit SHA or tag for deterministic, reproducible syncing.
- Generate a
ruff-sync.lock (or section in pyproject.toml) recording the resolved commit SHA after pull
check verifies against the locked version
ruff-sync update to bump to latest and update the lock
Inspiration: npm package-lock.json, Go go.sum, pre-commit rev: pinning.
Why: Without pinning, a pull in CI could silently pick up breaking upstream changes.
🟡 Tier 2: Valuable Additions
4. Dry-run / preview mode
pull --dry-run that shows the full merged output or writes to stdout without modifying files. check --diff partially covers this, but a dedicated dry-run for pull would complete the picture.
Inspiration: terraform plan, npm pack --dry-run.
5. Merge strategy options per key
Instead of binary sync-or-skip, support different merge behaviors per key:
[tool.ruff-sync]
# Append upstream values to local list instead of replacing
merge-strategy.lint.extend-select = "append"
# Never touch this key
merge-strategy.target-version = "local-only"
Inspiration: Git merge strategies, ESLint flat config composition.
6. status command — richer sync reporting
$ ruff-sync status
ruff-sync v0.2.0
upstream: https://github.com/my-org/standards (main @ abc1234)
target: pyproject.toml
ruff ✅ in sync (last pulled: 2026-03-10)
mypy ❌ out of sync (3 keys differ)
7. Monorepo support
Sync multiple sub-projects within a single repository, each with different exclusions:
[[tool.ruff-sync.targets]]
to = "packages/api"
exclude = ["lint.per-file-ignores"]
[[tool.ruff-sync.targets]]
to = "packages/worker"
exclude = ["target-version"]
🔵 Tier 3: Forward-Looking / Exploratory
8. Bidirectional sync / push command
Push local config changes back upstream by opening a PR. Use case: a team member discovers a useful rule downstream and wants to propose it upstream.
9. Webhook / GitHub App for automatic PRs
A hosted service watching the upstream repo and automatically opening PRs on downstream repos when config changes. (Renovate / Dependabot / pre-commit.ci model.)
10. ✅ Config validation
Validate the merged config before applying:
- Warn if upstream references deprecated Ruff rules
- Warn if upstream
target-version conflicts with local requires-python
- Optionally run
ruff check against the merged config to validate syntax
README: Additional Ecosystems to Document
The README's "How Other Ecosystems Solve This" section currently mentions ESLint, Prettier, and Ruff's extend. Consider adding:
- RuboCop (Ruby) — native
inherit_from: <URL> is the closest peer
- Stylelint (CSS) —
extends via npm packages
- golangci-lint (Go) — no native remote config; teams use CI workarounds
- Rust —
[workspace.lints] for monorepos only
- pre-commit — no
include/extends; repos must duplicate config
Related Issues
Feature Roadmap: Ecosystem-Inspired Improvements
After surveying how 8+ ecosystems handle shared linter/formatter configuration, here is a prioritized set of features and improvements to consider for ruff-sync.
Ecosystems Surveyed
extends: ["my-org-config"]via npm; flat config imports arrays"prettier": "@my-org/prettier-config"inpackage.jsonextends: "stylelint-config-standard"via npminherit_from: https://raw.githubusercontent.com/.../rubocop.yml--config path/to/file; no native remote extends[workspace.lints]inheritanceextend)extend = "../parent/ruff.toml"— local paths onlyinclude; each repo needs its own.pre-commit-config.yaml🟢 Tier 1: High Impact, Natural Extensions
1. ✅ Config inheritance / layering (like ESLint flat config)
Allow composing config from multiple upstream sources with explicit precedence.
Why: Organizations often have a base config + domain-specific overlays (web, data science, CLI tools). ESLint's flat config is an ordered array; RuboCop's
inherit_fromaccepts a list.2. ✅ Drift detection with richer CI output
Enhance
checkcommand for CI consumption.--output-format json|sarif|githubfor machine-readable output::error file=...) to surface drift directly on PRsInspiration: golangci-lint outputs SARIF, JSON, and GitHub annotations; ESLint supports
--format jsonand custom formatters.3. Lock/pin upstream versions
Pin to a specific commit SHA or tag for deterministic, reproducible syncing.
ruff-sync.lock(or section inpyproject.toml) recording the resolved commit SHA afterpullcheckverifies against the locked versionruff-sync updateto bump to latest and update the lockInspiration: npm
package-lock.json, Gogo.sum, pre-commitrev:pinning.Why: Without pinning, a
pullin CI could silently pick up breaking upstream changes.🟡 Tier 2: Valuable Additions
4. Dry-run / preview mode
pull --dry-runthat shows the full merged output or writes to stdout without modifying files.check --diffpartially covers this, but a dedicated dry-run forpullwould complete the picture.Inspiration:
terraform plan,npm pack --dry-run.5. Merge strategy options per key
Instead of binary sync-or-skip, support different merge behaviors per key:
Inspiration: Git merge strategies, ESLint flat config composition.
6.
statuscommand — richer sync reporting7. Monorepo support
Sync multiple sub-projects within a single repository, each with different exclusions:
🔵 Tier 3: Forward-Looking / Exploratory
8. Bidirectional sync / push command
Push local config changes back upstream by opening a PR. Use case: a team member discovers a useful rule downstream and wants to propose it upstream.
9. Webhook / GitHub App for automatic PRs
A hosted service watching the upstream repo and automatically opening PRs on downstream repos when config changes. (Renovate / Dependabot / pre-commit.ci model.)
10. ✅ Config validation
Validate the merged config before applying:
target-versionconflicts with localrequires-pythonruff checkagainst the merged config to validate syntaxREADME: Additional Ecosystems to Document
The README's "How Other Ecosystems Solve This" section currently mentions ESLint, Prettier, and Ruff's
extend. Consider adding:inherit_from: <URL>is the closest peerextendsvia npm packages[workspace.lints]for monorepos onlyinclude/extends; repos must duplicate configRelated Issues