ruff-sync 0.0.5.dev2__tar.gz → 0.1.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.github/workflows/ci.yaml +21 -4
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.github/workflows/complexity.yaml +4 -5
- ruff_sync-0.1.0.dev0/.github/workflows/docs.yaml +32 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/AGENTS.md +15 -12
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/PKG-INFO +40 -10
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/README.md +39 -9
- ruff_sync-0.1.0.dev0/docs/ci-integration.md +59 -0
- ruff_sync-0.1.0.dev0/docs/configuration.md +64 -0
- ruff_sync-0.1.0.dev0/docs/gen_ref_pages.py +34 -0
- ruff_sync-0.1.0.dev0/docs/index.md +40 -0
- ruff_sync-0.1.0.dev0/docs/installation.md +47 -0
- ruff_sync-0.1.0.dev0/docs/usage.md +75 -0
- ruff_sync-0.1.0.dev0/mkdocs.yml +100 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/pyproject.toml +54 -6
- ruff_sync-0.1.0.dev0/src/ruff_sync/__init__.py +44 -0
- ruff_sync-0.1.0.dev0/src/ruff_sync/__main__.py +10 -0
- ruff_sync-0.0.5.dev2/ruff_sync.py → ruff_sync-0.1.0.dev0/src/ruff_sync/cli.py +192 -93
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tasks.py +16 -6
- ruff_sync-0.1.0.dev0/tests/__init__.py +1 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_basic.py +34 -33
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_config_validation.py +2 -1
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_deprecation.py +4 -2
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_e2e.py +3 -2
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_git_fetch.py +7 -4
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_project.py +2 -1
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_url_handling.py +38 -3
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/uv.lock +508 -1
- ruff_sync-0.0.5.dev2/tests/__init__.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.agents/TESTING.md +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.agents/workflows/add-test-case.md +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.git-blame-ignore-revs +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.github/dependabot.yml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.gitignore +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/.pre-commit-config.yaml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/LICENSE.md +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/codecov.yml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/configs/fastapi/ruff.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/configs/kitchen-sink/ruff.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/ruff_sync_banner.png +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/scripts/check_dogfood.sh +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/scripts/gitclone_dogfood.sh +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/scripts/pull_dogfood.sh +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/conftest.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/standard_final.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/standard_initial.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/ruff.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_check.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_corner_cases.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_scaffold.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_toml_operations.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/test_whitespace.py +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/wo_ruff_cfg/pyproject.toml +0 -0
- {ruff_sync-0.0.5.dev2 → ruff_sync-0.1.0.dev0}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
|
@@ -66,8 +66,10 @@ jobs:
|
|
|
66
66
|
|
|
67
67
|
pre-publish:
|
|
68
68
|
name: Test package installation
|
|
69
|
-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
70
69
|
needs: [static-analysis, tests]
|
|
70
|
+
if: >-
|
|
71
|
+
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
|
72
|
+
(github.event_name == 'pull_request' && github.event.pull_request.draft == false)
|
|
71
73
|
runs-on: ubuntu-latest
|
|
72
74
|
steps:
|
|
73
75
|
- name: Checkout
|
|
@@ -80,14 +82,29 @@ jobs:
|
|
|
80
82
|
run: uv python install 3.10
|
|
81
83
|
|
|
82
84
|
- name: Build package
|
|
83
|
-
run:
|
|
85
|
+
run: |
|
|
86
|
+
echo "Building ruff-sync package..."
|
|
87
|
+
uv build
|
|
84
88
|
|
|
85
|
-
- name: Install
|
|
89
|
+
- name: Install packaged program
|
|
86
90
|
run: |
|
|
91
|
+
echo "Installing built wheel..."
|
|
87
92
|
uv tool install $(ls dist/*.whl)
|
|
88
|
-
ruff-sync --version
|
|
93
|
+
echo "Version: $(ruff-sync --version)"
|
|
94
|
+
|
|
95
|
+
- name: Test package installation (Pull)
|
|
96
|
+
run: |
|
|
97
|
+
echo "Testing ruff-sync pull against Kilo59/ruff-sync..."
|
|
89
98
|
ruff-sync https://github.com/Kilo59/ruff-sync
|
|
99
|
+
|
|
100
|
+
- name: Test package installation (Check)
|
|
101
|
+
run: |
|
|
102
|
+
echo "Testing ruff-sync check against Kilo59/ruff-sync..."
|
|
90
103
|
ruff-sync check https://github.com/Kilo59/ruff-sync
|
|
104
|
+
|
|
105
|
+
- name: Verify semantic check failure
|
|
106
|
+
run: |
|
|
107
|
+
echo "Verifying that --semantic check fails for kitchen-sink config (expected failure)..."
|
|
91
108
|
! ruff-sync check --semantic https://github.com/Kilo59/ruff-sync --path configs/kitchen-sink
|
|
92
109
|
|
|
93
110
|
publish:
|
|
@@ -28,11 +28,10 @@ jobs:
|
|
|
28
28
|
DIFF=$(wily diff ruff_sync.py tasks.py tests/ --no-detail -r origin/${{ github.event.pull_request.base.ref }})
|
|
29
29
|
echo "$DIFF"
|
|
30
30
|
|
|
31
|
-
# Build
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
echo "diff=$DIFF" >> "$GITHUB_OUTPUT"
|
|
31
|
+
# Build multiline output
|
|
32
|
+
echo "diff<<DIFF_EOF" >> "$GITHUB_OUTPUT"
|
|
33
|
+
echo "$DIFF" >> "$GITHUB_OUTPUT"
|
|
34
|
+
echo "DIFF_EOF" >> "$GITHUB_OUTPUT"
|
|
36
35
|
- name: Add Wily PR Comment
|
|
37
36
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
38
37
|
if: github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.number && steps.wily.outputs.diff != ''
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
deploy:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
run: uv python install
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --all-extras --dev --group docs
|
|
30
|
+
|
|
31
|
+
- name: Build and deploy documentation
|
|
32
|
+
run: uv run mkdocs gh-deploy --force
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**ruff-sync** is a CLI tool that synchronizes [Ruff](https://docs.astral.sh/ruff/) linter configuration across multiple Python projects. It downloads an upstream `pyproject.toml`, extracts the `[tool.ruff]` section, and merges it into a local project's `pyproject.toml` while preserving formatting, comments, and whitespace.
|
|
6
6
|
|
|
7
7
|
- **GitHub Repository**: [`Kilo59/ruff-sync`](https://github.com/Kilo59/ruff-sync)
|
|
8
|
-
- The
|
|
8
|
+
- The application uses a `src` layout in `src/ruff_sync/`.
|
|
9
9
|
- Dev tasks are defined in `tasks.py` using [Invoke](https://www.pyinvoke.org/).
|
|
10
10
|
|
|
11
11
|
## GitHub Context
|
|
@@ -54,11 +54,14 @@ gh label list # See available labels
|
|
|
54
54
|
|
|
55
55
|
## Project Structure
|
|
56
56
|
|
|
57
|
-
```
|
|
57
|
+
```text
|
|
58
58
|
.agents/ # Agent-specific instructions (Deep Standards)
|
|
59
59
|
TESTING.md # Mandatory testing patterns and rules
|
|
60
60
|
workflows/ # Step-by-step guides for common tasks
|
|
61
|
-
ruff_sync
|
|
61
|
+
src/ruff_sync/ # The application source
|
|
62
|
+
__init__.py # Public API
|
|
63
|
+
cli.py # CLI, merging logic, HTTP
|
|
64
|
+
__main__.py # -m support
|
|
62
65
|
tasks.py # Invoke tasks: lint, fmt, type-check, deps, new-case
|
|
63
66
|
pyproject.toml # Project config, dependencies, ruff/mypy settings
|
|
64
67
|
tests/
|
|
@@ -114,7 +117,7 @@ uv run mypy .
|
|
|
114
117
|
```
|
|
115
118
|
|
|
116
119
|
- mypy is configured in strict mode with `python_version = "3.10"`.
|
|
117
|
-
- It checks `
|
|
120
|
+
- It checks `src/`, `tests/`, and `tasks.py`.
|
|
118
121
|
- Tests have relaxed rules: `type-arg` and `no-untyped-def` are disabled for `tests.*`.
|
|
119
122
|
- `tomlkit` returns complex union types — use `cast(Any, ...)` in tests when indexing parsed TOML documents to satisfy mypy without verbose type narrowing.
|
|
120
123
|
|
|
@@ -130,7 +133,7 @@ Or with coverage:
|
|
|
130
133
|
uv run coverage run -m pytest -vv
|
|
131
134
|
```
|
|
132
135
|
|
|
133
|
-
- Coverage is tracked for `ruff_sync
|
|
136
|
+
- Coverage is tracked for `src/ruff_sync/` only.
|
|
134
137
|
- Tests use `respx` to mock HTTP calls and `pyfakefs` for filesystem mocking.
|
|
135
138
|
- `pytest-asyncio` is in **strict** mode — async tests need the `@pytest.mark.asyncio` decorator.
|
|
136
139
|
|
|
@@ -165,13 +168,13 @@ uv run coverage run -m pytest -vv
|
|
|
165
168
|
|
|
166
169
|
Defined in `tasks.py`. **ALWAYS** run these through uv: `uv run invoke <task>`
|
|
167
170
|
|
|
168
|
-
| Task | Alias
|
|
169
|
-
| ------------ |
|
|
170
|
-
| `lint` |
|
|
171
|
-
| `fmt` |
|
|
172
|
-
| `type-check` | `types`
|
|
173
|
-
| `deps` | `sync`
|
|
174
|
-
| `new-case` | `new-lifecycle-tomls` | Scaffold lifecycle TOML fixtures
|
|
171
|
+
| Task | Alias | Description |
|
|
172
|
+
| ------------ | --------------------- | -------------------------------------- |
|
|
173
|
+
| `lint` | | Lint with ruff (auto-fixes by default) |
|
|
174
|
+
| `fmt` | | Format with ruff format |
|
|
175
|
+
| `type-check` | `types` | Type-check with mypy |
|
|
176
|
+
| `deps` | `sync` | Sync dependencies with uv |
|
|
177
|
+
| `new-case` | `new-lifecycle-tomls` | Scaffold lifecycle TOML fixtures |
|
|
175
178
|
|
|
176
179
|
## CI
|
|
177
180
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ruff-sync
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
4
|
Summary: Synchronize Ruff linter configuration across projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/Kilo59/ruff-sync
|
|
6
6
|
Project-URL: Documentation, https://github.com/Kilo59/ruff-sync#readme
|
|
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
## Table of Contents
|
|
51
51
|
|
|
52
52
|
- [The Problem](#the-problem)
|
|
53
53
|
- [How It Works](#how-it-works)
|
|
@@ -102,9 +102,9 @@ Ruff's `extend` is perfect inside a monorepo, but if your projects live in **sep
|
|
|
102
102
|
└─────────────────────────────┘
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
1. You point `ruff-sync` at the URL of your canonical
|
|
106
|
-
2. It downloads the file
|
|
107
|
-
3. It **merges** the upstream config into your local
|
|
105
|
+
1. You point `ruff-sync` at the URL of your canonical configuration (repository, directory, or direct file).
|
|
106
|
+
2. It downloads the file and extracts the configuration (from `[tool.ruff]` in `pyproject.toml` or the top-level in `ruff.toml`).
|
|
107
|
+
3. It **merges** the upstream config into your local project — 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`).
|
|
108
108
|
|
|
109
109
|
No package registry. No publishing step. Just a URL.
|
|
110
110
|
|
|
@@ -133,8 +133,9 @@ pip install ruff-sync
|
|
|
133
133
|
### Usage
|
|
134
134
|
|
|
135
135
|
```console
|
|
136
|
-
# Sync from a GitHub/GitLab repository (
|
|
136
|
+
# Sync from a GitHub/GitLab repository (root or specific directory)
|
|
137
137
|
ruff-sync https://github.com/my-org/standards
|
|
138
|
+
ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
|
|
138
139
|
|
|
139
140
|
# Or a direct blob/file URL (auto-converts to raw)
|
|
140
141
|
ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
|
|
@@ -162,7 +163,8 @@ Run `ruff-sync --help` for full details on all available options.
|
|
|
162
163
|
## Key Features
|
|
163
164
|
|
|
164
165
|
- 🏗️ **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
|
|
165
|
-
- 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
|
|
166
|
+
- 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs, tree (directory) URLs, or blob (file) URLs to raw content URLs.
|
|
167
|
+
- 🔍 **Smart configuration discovery** — Point at a directory and `ruff-sync` will automatically find your config. It checks `pyproject.toml`, `ruff.toml`, and `.ruff.toml` (in that order).
|
|
166
168
|
- 📥 **Git clone support** — If the URL starts with `git@` or uses the `ssh://`, `git://`, or `git+ssh://` schemes, `ruff-sync` will perform an efficient shallow clone (using `--filter=blob:none` and `--no-checkout`) to safely extract the configuration with minimal network traffic.
|
|
167
169
|
- 🛡️ **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
|
|
168
170
|
- 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
|
|
@@ -267,18 +269,46 @@ git commit -am "sync ruff config from upstream"
|
|
|
267
269
|
|
|
268
270
|
While `ruff-sync` is designed to sync from _any_ repository or URL of your choosing, this repository also provides a few curated configurations in the [`configs/`](./configs/) directory that you can use directly.
|
|
269
271
|
|
|
270
|
-
|
|
272
|
+
`ruff-sync` is flexible with URLs. You can point it at a repository root, a specific directory (tree), a direct file (blob), or even a raw content URL.
|
|
273
|
+
|
|
274
|
+
#### Kitchen Sink
|
|
275
|
+
|
|
276
|
+
An exhaustive configuration that explicitly enables and documents almost all available Ruff rules. Great for establishing a strict baseline.
|
|
271
277
|
|
|
272
278
|
```console
|
|
279
|
+
# Directory URL (recommended)
|
|
280
|
+
ruff-sync https://github.com/Kilo59/ruff-sync/tree/main/configs/kitchen-sink
|
|
281
|
+
|
|
282
|
+
# Direct file URL (blob)
|
|
273
283
|
ruff-sync https://github.com/Kilo59/ruff-sync/blob/main/configs/kitchen-sink/ruff.toml
|
|
284
|
+
|
|
285
|
+
# Raw content URL
|
|
286
|
+
ruff-sync https://raw.githubusercontent.com/Kilo59/ruff-sync/main/configs/kitchen-sink/ruff.toml
|
|
287
|
+
|
|
288
|
+
# Git SSH URL (clones the repo)
|
|
289
|
+
ruff-sync git@github.com:Kilo59/ruff-sync.git --path configs/kitchen-sink
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### FastAPI & Async
|
|
293
|
+
|
|
294
|
+
Tailored for modern web applications. Includes rules for `asyncio`, security (`flake8-bandit`), and Pydantic-friendly naming conventions.
|
|
295
|
+
|
|
296
|
+
```console
|
|
297
|
+
# Repository Root (if the config is at the root)
|
|
298
|
+
ruff-sync https://github.com/my-org/fastapi-standards
|
|
299
|
+
|
|
300
|
+
# Directory URL
|
|
301
|
+
ruff-sync https://github.com/Kilo59/ruff-sync/tree/main/configs/fastapi
|
|
274
302
|
```
|
|
275
303
|
|
|
276
|
-
|
|
304
|
+
#### Default Syncing
|
|
305
|
+
|
|
306
|
+
Set your preferred standard as the default in your `pyproject.toml`:
|
|
277
307
|
|
|
278
308
|
```toml
|
|
279
309
|
[tool.ruff-sync]
|
|
280
310
|
upstream = "https://github.com/Kilo59/ruff-sync"
|
|
281
|
-
path = "configs/
|
|
311
|
+
path = "configs/fastapi"
|
|
282
312
|
```
|
|
283
313
|
|
|
284
314
|
## Bootstrapping a New Project
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## Table of Contents
|
|
20
20
|
|
|
21
21
|
- [The Problem](#the-problem)
|
|
22
22
|
- [How It Works](#how-it-works)
|
|
@@ -71,9 +71,9 @@ Ruff's `extend` is perfect inside a monorepo, but if your projects live in **sep
|
|
|
71
71
|
└─────────────────────────────┘
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
1. You point `ruff-sync` at the URL of your canonical
|
|
75
|
-
2. It downloads the file
|
|
76
|
-
3. It **merges** the upstream config into your local
|
|
74
|
+
1. You point `ruff-sync` at the URL of your canonical configuration (repository, directory, or direct file).
|
|
75
|
+
2. It downloads the file and extracts the configuration (from `[tool.ruff]` in `pyproject.toml` or the top-level in `ruff.toml`).
|
|
76
|
+
3. It **merges** the upstream config into your local project — 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`).
|
|
77
77
|
|
|
78
78
|
No package registry. No publishing step. Just a URL.
|
|
79
79
|
|
|
@@ -102,8 +102,9 @@ pip install ruff-sync
|
|
|
102
102
|
### Usage
|
|
103
103
|
|
|
104
104
|
```console
|
|
105
|
-
# Sync from a GitHub/GitLab repository (
|
|
105
|
+
# Sync from a GitHub/GitLab repository (root or specific directory)
|
|
106
106
|
ruff-sync https://github.com/my-org/standards
|
|
107
|
+
ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
|
|
107
108
|
|
|
108
109
|
# Or a direct blob/file URL (auto-converts to raw)
|
|
109
110
|
ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
|
|
@@ -131,7 +132,8 @@ Run `ruff-sync --help` for full details on all available options.
|
|
|
131
132
|
## Key Features
|
|
132
133
|
|
|
133
134
|
- 🏗️ **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
|
|
134
|
-
- 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
|
|
135
|
+
- 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs, tree (directory) URLs, or blob (file) URLs to raw content URLs.
|
|
136
|
+
- 🔍 **Smart configuration discovery** — Point at a directory and `ruff-sync` will automatically find your config. It checks `pyproject.toml`, `ruff.toml`, and `.ruff.toml` (in that order).
|
|
135
137
|
- 📥 **Git clone support** — If the URL starts with `git@` or uses the `ssh://`, `git://`, or `git+ssh://` schemes, `ruff-sync` will perform an efficient shallow clone (using `--filter=blob:none` and `--no-checkout`) to safely extract the configuration with minimal network traffic.
|
|
136
138
|
- 🛡️ **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
|
|
137
139
|
- 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
|
|
@@ -236,18 +238,46 @@ git commit -am "sync ruff config from upstream"
|
|
|
236
238
|
|
|
237
239
|
While `ruff-sync` is designed to sync from _any_ repository or URL of your choosing, this repository also provides a few curated configurations in the [`configs/`](./configs/) directory that you can use directly.
|
|
238
240
|
|
|
239
|
-
|
|
241
|
+
`ruff-sync` is flexible with URLs. You can point it at a repository root, a specific directory (tree), a direct file (blob), or even a raw content URL.
|
|
242
|
+
|
|
243
|
+
#### Kitchen Sink
|
|
244
|
+
|
|
245
|
+
An exhaustive configuration that explicitly enables and documents almost all available Ruff rules. Great for establishing a strict baseline.
|
|
240
246
|
|
|
241
247
|
```console
|
|
248
|
+
# Directory URL (recommended)
|
|
249
|
+
ruff-sync https://github.com/Kilo59/ruff-sync/tree/main/configs/kitchen-sink
|
|
250
|
+
|
|
251
|
+
# Direct file URL (blob)
|
|
242
252
|
ruff-sync https://github.com/Kilo59/ruff-sync/blob/main/configs/kitchen-sink/ruff.toml
|
|
253
|
+
|
|
254
|
+
# Raw content URL
|
|
255
|
+
ruff-sync https://raw.githubusercontent.com/Kilo59/ruff-sync/main/configs/kitchen-sink/ruff.toml
|
|
256
|
+
|
|
257
|
+
# Git SSH URL (clones the repo)
|
|
258
|
+
ruff-sync git@github.com:Kilo59/ruff-sync.git --path configs/kitchen-sink
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### FastAPI & Async
|
|
262
|
+
|
|
263
|
+
Tailored for modern web applications. Includes rules for `asyncio`, security (`flake8-bandit`), and Pydantic-friendly naming conventions.
|
|
264
|
+
|
|
265
|
+
```console
|
|
266
|
+
# Repository Root (if the config is at the root)
|
|
267
|
+
ruff-sync https://github.com/my-org/fastapi-standards
|
|
268
|
+
|
|
269
|
+
# Directory URL
|
|
270
|
+
ruff-sync https://github.com/Kilo59/ruff-sync/tree/main/configs/fastapi
|
|
243
271
|
```
|
|
244
272
|
|
|
245
|
-
|
|
273
|
+
#### Default Syncing
|
|
274
|
+
|
|
275
|
+
Set your preferred standard as the default in your `pyproject.toml`:
|
|
246
276
|
|
|
247
277
|
```toml
|
|
248
278
|
[tool.ruff-sync]
|
|
249
279
|
upstream = "https://github.com/Kilo59/ruff-sync"
|
|
250
|
-
path = "configs/
|
|
280
|
+
path = "configs/fastapi"
|
|
251
281
|
```
|
|
252
282
|
|
|
253
283
|
## Bootstrapping a New Project
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# CI Integration
|
|
2
|
+
|
|
3
|
+
`ruff-sync` is designed to be run in CI pipelines to ensure that all repositories in an organization stay in sync with the central standards.
|
|
4
|
+
|
|
5
|
+
## Usage in CI
|
|
6
|
+
|
|
7
|
+
The best way to use `ruff-sync` in CI is with the `check` command. If the configuration has drifted, `ruff-sync check` will exit with a non-zero code, failing the build.
|
|
8
|
+
|
|
9
|
+
### GitHub Actions
|
|
10
|
+
|
|
11
|
+
We recommend using `uv` to run `ruff-sync` in GitHub Actions.
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
name: "Standards Check"
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
push:
|
|
18
|
+
branches: [main]
|
|
19
|
+
pull_request:
|
|
20
|
+
branches: [main]
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
ruff-sync:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v5
|
|
30
|
+
|
|
31
|
+
- name: Check Ruff Sync
|
|
32
|
+
run: uvx ruff-sync check --semantic
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### GitLab CI
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
ruff-sync-check:
|
|
39
|
+
image: python:3.12
|
|
40
|
+
script:
|
|
41
|
+
- pip install ruff-sync
|
|
42
|
+
- ruff-sync check --semantic
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Best Practices
|
|
46
|
+
|
|
47
|
+
### Use `--semantic`
|
|
48
|
+
|
|
49
|
+
By default, `ruff-sync check` does a strict comparison of the TOML files. This means that if you manually reformat `pyproject.toml` or add a comment, the check will fail even if the actual Ruff rules are the same.
|
|
50
|
+
|
|
51
|
+
In CI, you usually only care about the functional configuration. Using `--semantic` ensures that minor formatting changes don't break your builds.
|
|
52
|
+
|
|
53
|
+
### Use a Dedicated Workflow
|
|
54
|
+
|
|
55
|
+
Running `ruff-sync` as a separate job in your linting workflow makes it easy to identify when a failure is due to configuration drift rather than a code quality issue.
|
|
56
|
+
|
|
57
|
+
### Automated PRs
|
|
58
|
+
|
|
59
|
+
Instead of just checking, some organizations prefer to have a bot automatically open a PR when the upstream configuration changes. You can achieve this by running `ruff-sync pull` and using an action like `create-pull-request`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
`ruff-sync` can be configured in your `pyproject.toml` file under the `[tool.ruff-sync]` section.
|
|
4
|
+
|
|
5
|
+
## Reference
|
|
6
|
+
|
|
7
|
+
| Key | Type | Default | Description |
|
|
8
|
+
| :--- | :--- | :--- | :--- |
|
|
9
|
+
| `upstream` | `str` | *Required* | The URL of the upstream `pyproject.toml` or `ruff.toml`. |
|
|
10
|
+
| `to` | `str` | `"."` | The local directory or file where configuration should be merged. |
|
|
11
|
+
| `exclude` | `list[str]` | `["lint.per-file-ignores"]` | A list of configuration keys to preserve locally. |
|
|
12
|
+
| `branch` | `str` | `"main"` | The default branch to use when resolving repository URLs. |
|
|
13
|
+
| `path` | `str` | `""` | The directory path within the repository where the config is located. |
|
|
14
|
+
| `semantic` | `bool` | `false` | Whether `check` should default to semantic matching. |
|
|
15
|
+
| `diff` | `bool` | `true` | Whether `check` should show a diff by default. |
|
|
16
|
+
|
|
17
|
+
## Exclude Patterns
|
|
18
|
+
|
|
19
|
+
The `exclude` setting is powerful. It allows you to adopt most of an upstream configuration while keeping some parts specific to your repository.
|
|
20
|
+
|
|
21
|
+
Exclusions use dotted paths to target specific sections or keys.
|
|
22
|
+
|
|
23
|
+
### Examples
|
|
24
|
+
|
|
25
|
+
#### Preserve per-file ignores
|
|
26
|
+
|
|
27
|
+
This is the default. It ensures that any custom ignores you've set for specific files in your repo aren't overwritten by the upstream.
|
|
28
|
+
|
|
29
|
+
```toml
|
|
30
|
+
[tool.ruff-sync]
|
|
31
|
+
exclude = ["lint.per-file-ignores"]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Manage specific plugins locally
|
|
35
|
+
|
|
36
|
+
If you want to use the upstream rules but manage `isort` settings yourself:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
[tool.ruff-sync]
|
|
40
|
+
exclude = ["lint.isort"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### Keep a specific rule toggle
|
|
44
|
+
|
|
45
|
+
If you want to manage whether a specific rule is ignored or selected:
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
[tool.ruff-sync]
|
|
49
|
+
exclude = ["lint.ignore", "lint.select"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### Preserve target version
|
|
53
|
+
|
|
54
|
+
If your projects are on different Python versions but share linting rules:
|
|
55
|
+
|
|
56
|
+
```toml
|
|
57
|
+
[tool.ruff-sync]
|
|
58
|
+
exclude = ["target-version"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Deprecation Notes
|
|
62
|
+
|
|
63
|
+
- The key `source` in `[tool.ruff-sync]` is deprecated and will be removed in a future version. Use `to` instead.
|
|
64
|
+
- The `--source` CLI flag is also deprecated in favor of `--to`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Generate the code reference pages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pathlib
|
|
6
|
+
|
|
7
|
+
import mkdocs_gen_files
|
|
8
|
+
|
|
9
|
+
nav = mkdocs_gen_files.Nav() # type: ignore[attr-defined,no-untyped-call]
|
|
10
|
+
|
|
11
|
+
for path in sorted(pathlib.Path("src").rglob("*.py")):
|
|
12
|
+
module_path = path.relative_to("src").with_suffix("")
|
|
13
|
+
doc_path = path.relative_to("src").with_suffix(".md")
|
|
14
|
+
full_doc_path = pathlib.Path("reference", doc_path)
|
|
15
|
+
|
|
16
|
+
parts = list(module_path.parts)
|
|
17
|
+
|
|
18
|
+
if parts[-1] == "__init__":
|
|
19
|
+
parts.pop()
|
|
20
|
+
doc_path = doc_path.with_name("index.md")
|
|
21
|
+
full_doc_path = full_doc_path.with_name("index.md")
|
|
22
|
+
elif parts[-1] == "__main__":
|
|
23
|
+
continue
|
|
24
|
+
|
|
25
|
+
nav[tuple(parts)] = doc_path.as_posix()
|
|
26
|
+
|
|
27
|
+
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
|
|
28
|
+
identifier = ".".join(parts)
|
|
29
|
+
print(f"::: {identifier}", file=fd)
|
|
30
|
+
|
|
31
|
+
mkdocs_gen_files.set_edit_path(full_doc_path, path)
|
|
32
|
+
|
|
33
|
+
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
|
|
34
|
+
nav_file.writelines(nav.build_literate_nav())
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ruff-sync
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/ruff-sync/)
|
|
4
|
+
[](https://pypi.org/project/ruff-sync/)
|
|
5
|
+
[](https://github.com/Kilo59/ruff-sync/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
**ruff-sync** is a lightweight CLI tool to synchronize [Ruff](https://docs.astral.sh/ruff/) linter configuration across multiple Python projects.
|
|
8
|
+
|
|
9
|
+
## The Problem
|
|
10
|
+
|
|
11
|
+
Maintaining a consistent Ruff configuration across 10, 50, or 100 repositories is painful. When you decide to adopt a new rule or change a setting, you have to manually update every single `pyproject.toml`.
|
|
12
|
+
|
|
13
|
+
Internal "base" configurations or shared presets often fall out of sync, or require complex inheritance setups that are hard to debug.
|
|
14
|
+
|
|
15
|
+
## The Solution
|
|
16
|
+
|
|
17
|
+
`ruff-sync` lets you define a "source of truth" (a URL to a `pyproject.toml` or `ruff.toml`) and pull the `[tool.ruff]` section into your local projects with a single command.
|
|
18
|
+
|
|
19
|
+
- **Formatting Preserved**: Unlike standard TOML parsers, `ruff-sync` uses `tomlkit` to preserve your comments, indentation, and whitespace.
|
|
20
|
+
- **Smart Merging**: Intelligently merges nested tables (like `lint.per-file-ignores`) while allowing you to exclude specific keys you want to manage locally.
|
|
21
|
+
- **CI Friendly**: Use `ruff-sync check` in your CI pipeline to ensure projects haven't drifted from the upstream standard.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Configure your project
|
|
26
|
+
|
|
27
|
+
Add the upstream URL to your `pyproject.toml`:
|
|
28
|
+
|
|
29
|
+
```toml
|
|
30
|
+
[tool.ruff-sync]
|
|
31
|
+
upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Pull the configuration
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv run ruff-sync pull
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This will download the upstream file, extract the `[tool.ruff]` section, and merge it into your local file.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
`ruff-sync` is a Python CLI tool. We recommend using `uv` for the best experience, but it works with standard Python package managers as well.
|
|
4
|
+
|
|
5
|
+
## Recommended: Using `uv`
|
|
6
|
+
|
|
7
|
+
If you are using [uv](https://docs.astral.sh/uv/), you can run `ruff-sync` without installing it globally:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uvx ruff-sync pull
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or add it to your project's development dependencies:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv add --dev ruff-sync
|
|
17
|
+
uv run ruff-sync pull
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Other Methods
|
|
21
|
+
|
|
22
|
+
=== "pipx"
|
|
23
|
+
|
|
24
|
+
[pipx](https://github.com/pypa/pipx) is the recommended way to install Python CLIs globally in isolated environments.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pipx install ruff-sync
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
=== "pip"
|
|
31
|
+
|
|
32
|
+
You can install `ruff-sync` from PyPI using `pip`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install ruff-sync
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> [!WARNING]
|
|
39
|
+
> We recommend using a virtual environment or `pipx` to avoid dependency conflicts with other global packages.
|
|
40
|
+
|
|
41
|
+
## Verifying Installation
|
|
42
|
+
|
|
43
|
+
Check that `ruff-sync` is installed correctly by running:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
ruff-sync --version
|
|
47
|
+
```
|