Skip to main content

Synchronize Ruff linter configuration across projects

Project description

ruff-sync banner
PyPI version codecov pre-commit.ci status Ruff Wily

ruff-sync

Keep your Ruff config consistent across every repo — automatically.


Table of Contents

ruff-sync is a CLI tool that pulls a canonical Ruff configuration from an upstream pyproject.toml (hosted anywhere — GitHub, GitLab, a raw URL) and merges it into your local project, preserving your comments, formatting, and project-specific overrides.

The Problem

If you maintain more than one Python project, you've probably copy-pasted your [tool.ruff] config between repos more than once. When you decide to enable a new rule or bump your target Python version, you get to do it again — in every repo. Configs drift, standards diverge, and your "shared" style guide becomes a polite suggestion.

How Other Ecosystems Solve This

Ecosystem Mechanism Limitation for Ruff users
ESLint Shareable configs — publish an npm package, then extends: ["my-org-config"] Requires a package registry (npm). Python doesn't have an equivalent convention.
Prettier Shared configs — same npm-package pattern, referenced via "prettier": "@my-org/prettier-config" in package.json Same — tightly coupled to npm.
Ruff extend — extend from a local file path (great for monorepos) Only supports local paths. No native remote URL support (requested in astral-sh/ruff#12352).

Ruff's extend is perfect inside a monorepo, but if your projects live in separate repositories, there's no built-in way to inherit config from a central source.

That's what ruff-sync does.

How It Works

┌─────────────────────────────┐
│  Upstream repo              │
│  (your "source of truth")   │
│                             │
│  pyproject.toml             │
│    [tool.ruff]              │
│    target-version = "py310" │
│    lint.select = [...]      │
└──────────┬──────────────────┘
           │  ruff-sync downloads
           │  & extracts [tool.ruff]
           ▼
┌─────────────────────────────┐
│  Your local project         │
│                             │
│  pyproject.toml             │
│    [tool.ruff]  ◄── merged  │
│    # your comments kept ✓   │
│    # formatting kept ✓      │
│    # per-file-ignores kept ✓│
└─────────────────────────────┘
  1. You point ruff-sync at the URL of your canonical pyproject.toml.
  2. It downloads the file, extracts the [tool.ruff] section.
  3. It merges the upstream config into your local pyproject.toml — updating values that changed, adding new rules, but preserving your local comments, whitespace, and any sections you've chosen to exclude (like per-file-ignores).

No package registry. No publishing step. Just a URL.

Quick Start

Install

With uv (recommended):

uv tool install ruff-sync

With pipx:

pipx install ruff-sync

With pip:

pip install ruff-sync

From Source (Bleeding Edge)

If you want the latest development version:

uv tool install git+https://github.com/Kilo59/ruff-sync

Usage

# Sync from a GitHub URL (blob URLs are auto-converted to raw)
ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml

# Once configured in pyproject.toml (see below), simply run:
ruff-sync

# Sync into a specific project directory
ruff-sync --source ./my-project

# Exclude specific sections from being overwritten using dotted paths
ruff-sync --exclude lint.per-file-ignores lint.ignore

CLI Reference

usage: ruff-sync [-h] [--source SOURCE] [--exclude EXCLUDE [EXCLUDE ...]] [-v] [upstream]

positional arguments:
  upstream              The URL to download the pyproject.toml file from.
                        Optional if defined in [tool.ruff-sync]

optional arguments:
  -h, --help            show this help message and exit
  --source SOURCE       The directory to sync the pyproject.toml file to. Default: .
  --exclude EXCLUDE [EXCLUDE ...]
                        Exclude certain ruff configs. Default: lint.per-file-ignores
  -v, --verbose         Increase verbosity. -v for INFO, -vv for DEBUG.

Key Features

  • Format-preserving merges — Uses tomlkit under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
  • GitHub URL support — Paste a GitHub blob URL and it will automatically convert it to the raw content URL.
  • Selective exclusions — Keep project-specific overrides (like target-version) from being clobbered by the upstream config.
  • Works with any host — GitHub, GitLab, Bitbucket, or any raw URL that serves a pyproject.toml.

Configuration

You can configure ruff-sync itself in your pyproject.toml:

[tool.ruff-sync]
# The source of truth for your ruff configuration
upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"

# Use simple names for top-level keys, and dotted paths for nested keys
exclude = [
    "target-version",          # A top-level key under [tool.ruff]
    "lint.per-file-ignores",   # A nested key under [tool.ruff.lint]
    "lint.ignore"
]

This sets the default exclusions so you don't need to pass --exclude on the command line every time. Note: Any explicitly provided CLI arguments will override the list in pyproject.toml.

Example Workflow

A typical setup for an organization:

  1. Create a "standards" repo with your canonical pyproject.toml containing your shared [tool.ruff] config.
  2. In each project, run ruff-sync pointing at that repo — either manually, in a Makefile, or as a CI step.
  3. When you update the standard, re-run ruff-sync in each project to pull the changes. Your local comments and per-file-ignores stay intact.
# In each project repo:
ruff-sync https://github.com/my-org/python-standards/blob/main/pyproject.toml
git diff pyproject.toml  # review the changes
git commit -am "sync ruff config from upstream"

Contributing

This project uses:

  • uv for dependency management
  • Ruff for linting and formatting
  • mypy for type checking (strict mode)
  • pytest for testing
# Setup
uv sync --group dev

# Run checks
uv run ruff check . --fix   # lint
uv run ruff format .        # format
uv run mypy .               # type check
uv run pytest -vv           # test

Dogfooding

To see ruff-sync in action on a complex, real-world configuration, you can "dogfood" it by syncing this project's own pyproject.toml with a large upstream config like Pydantic's.

We've provided a script to make this easy:

./scripts/dogfood.sh

This will download Pydantic's Ruff configuration and merge it into the local pyproject.toml. You can then use git diff to see how it merged the keys while preserving the existing structure and comments.

To revert the changes after testing:

git checkout pyproject.toml

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ruff_sync-0.0.1.dev4.tar.gz (347.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ruff_sync-0.0.1.dev4-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file ruff_sync-0.0.1.dev4.tar.gz.

File metadata

  • Download URL: ruff_sync-0.0.1.dev4.tar.gz
  • Upload date:
  • Size: 347.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ruff_sync-0.0.1.dev4.tar.gz
Algorithm Hash digest
SHA256 eaea4adeb7693b2d01d726e7c1bd80f9bde737a4d38015170e83b24dec38c447
MD5 a90975af5c1ef6af7f38fb38ecc8dc8d
BLAKE2b-256 d349114660de4b5ecc7a675903e49daf9f1c4d57bf08c946c99882d776dd49b4

See more details on using hashes here.

File details

Details for the file ruff_sync-0.0.1.dev4-py3-none-any.whl.

File metadata

  • Download URL: ruff_sync-0.0.1.dev4-py3-none-any.whl
  • Upload date:
  • Size: 10.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ruff_sync-0.0.1.dev4-py3-none-any.whl
Algorithm Hash digest
SHA256 59cec2f177aa8c50cafebad177de4db706aebd7d6d57d16e6e9e16c3c79a66c2
MD5 b99fc15a7b49a4b5c47e40f51b15eac8
BLAKE2b-256 146f4cf688e635ec7367136f81ebfb42a469325b3c1b00b5d9b71a51bd1364c9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page