ruff-sync 0.0.1.dev3__tar.gz → 0.0.2__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.
Files changed (49) hide show
  1. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.github/dependabot.yml +6 -3
  2. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.github/workflows/ci.yaml +1 -0
  3. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/PKG-INFO +108 -29
  4. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/README.md +107 -28
  5. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/pyproject.toml +3 -2
  6. ruff_sync-0.0.2/ruff_sync.py +530 -0
  7. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/scripts/dogfood.sh +1 -1
  8. ruff_sync-0.0.2/scripts/dogfood_check.sh +48 -0
  9. ruff_sync-0.0.2/tasks.py +193 -0
  10. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +4 -2
  11. ruff_sync-0.0.2/tests/lifecycle_tomls/readme_excludes_final.toml +39 -0
  12. ruff_sync-0.0.2/tests/lifecycle_tomls/readme_excludes_initial.toml +39 -0
  13. ruff_sync-0.0.2/tests/lifecycle_tomls/readme_excludes_upstream.toml +37 -0
  14. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/standard_final.toml +4 -2
  15. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/test_basic.py +130 -16
  16. ruff_sync-0.0.2/tests/test_check.py +223 -0
  17. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/test_corner_cases.py +1 -3
  18. ruff_sync-0.0.2/tests/test_e2e.py +227 -0
  19. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/test_project.py +12 -1
  20. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/test_whitespace.py +44 -0
  21. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/uv.lock +3 -1
  22. ruff_sync-0.0.1.dev3/ruff_sync.py +0 -301
  23. ruff_sync-0.0.1.dev3/tasks.py +0 -90
  24. ruff_sync-0.0.1.dev3/tests/test_e2e.py +0 -109
  25. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.agents/TESTING.md +0 -0
  26. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.agents/workflows/add-test-case.md +0 -0
  27. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.github/workflows/complexity.yaml +0 -0
  28. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.gitignore +0 -0
  29. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/.pre-commit-config.yaml +0 -0
  30. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/AGENTS.md +0 -0
  31. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/LICENSE.md +0 -0
  32. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/codecov.yml +0 -0
  33. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/ruff_sync_banner.png +0 -0
  34. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/__init__.py +0 -0
  35. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  36. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  37. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  38. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  39. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  40. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  41. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  42. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  43. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  44. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  45. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/ruff.toml +0 -0
  46. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/test_toml_operations.py +0 -0
  47. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  48. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  49. {ruff_sync-0.0.1.dev3 → ruff_sync-0.0.2}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -1,9 +1,12 @@
1
1
  # Please see the documentation for all configuration options:
2
2
  # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3
+ # https://docs.astral.sh/uv/guides/integration/dependency-bots/#dependabot
3
4
 
4
5
  version: 2
5
6
  updates:
6
- - package-ecosystem: "pip" # Documentation: For package managers such as pipenv and poetry, you need to use the pip YAML value.
7
- directory: "/" # Location of package manifests
7
+ - package-ecosystem: "uv"
8
+ directory: "/"
8
9
  schedule:
9
- interval: daily
10
+ interval: "weekly"
11
+ cooldown:
12
+ default-days: 7
@@ -33,6 +33,7 @@ jobs:
33
33
 
34
34
  tests:
35
35
  strategy:
36
+ fail-fast: ${{ github.event.pull_request.draft == true }}
36
37
  matrix:
37
38
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
38
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ruff-sync
3
- Version: 0.0.1.dev3
3
+ Version: 0.0.2
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
@@ -40,7 +40,9 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  # ruff-sync
42
42
 
43
- **Keep your Ruff config consistent across every repo — automatically.**
43
+ **Keep your Ruff config consistent across multiple projects.**
44
+
45
+ `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/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.
44
46
 
45
47
  ---
46
48
 
@@ -49,15 +51,15 @@ Description-Content-Type: text/markdown
49
51
  - [The Problem](#the-problem)
50
52
  - [How It Works](#how-it-works)
51
53
  - [Quick Start](#quick-start)
52
- - [Install](#install)
53
- - [Usage](#usage)
54
54
  - [Key Features](#key-features)
55
55
  - [Configuration](#configuration)
56
+ - [CI Integration](#ci-integration)
57
+ - [Example Workflow](#example-workflow)
58
+ - [Detailed Check Logic](#detailed-check-logic)
56
59
  - [Contributing](#contributing)
60
+ - [Dogfooding](#dogfooding)
57
61
  - [License](#license)
58
62
 
59
- `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/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.
60
-
61
63
  ## The Problem
62
64
 
63
65
  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.
@@ -74,7 +76,7 @@ Ruff's `extend` is perfect inside a monorepo, but if your projects live in **sep
74
76
 
75
77
  **That's what `ruff-sync` does.**
76
78
 
77
- ## How It Works
79
+ ### How It Works
78
80
 
79
81
  ```
80
82
  ┌─────────────────────────────┐
@@ -142,34 +144,32 @@ uv tool install git+https://github.com/Kilo59/ruff-sync
142
144
  # Sync from a GitHub URL (blob URLs are auto-converted to raw)
143
145
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
144
146
 
147
+ # Once configured in pyproject.toml (see Configuration), simply run:
148
+ ruff-sync
149
+
145
150
  # Sync into a specific project directory
146
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml --source ./my-project
151
+ ruff-sync --source ./my-project
147
152
 
148
153
  # Exclude specific sections from being overwritten using dotted paths
149
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml --exclude lint.per-file-ignores lint.ignore
150
- ```
154
+ ruff-sync --exclude lint.per-file-ignores lint.ignore
151
155
 
152
- ### CLI Reference
156
+ # Check if your local config is in sync (useful in CI)
157
+ ruff-sync check https://github.com/my-org/standards/blob/main/pyproject.toml
153
158
 
159
+ # Semantic check — ignore cosmetic differences like comments and whitespace
160
+ ruff-sync check --semantic
154
161
  ```
155
- usage: ruff-sync [-h] [--source SOURCE] [--exclude EXCLUDE [EXCLUDE ...]] upstream
156
162
 
157
- positional arguments:
158
- upstream The URL to download the pyproject.toml file from.
159
-
160
- optional arguments:
161
- -h, --help show this help message and exit
162
- --source SOURCE The directory to sync the pyproject.toml file to. Default: .
163
- --exclude EXCLUDE [EXCLUDE ...]
164
- Exclude certain ruff configs. Default: lint.per-file-ignores
165
- ```
163
+ Run `ruff-sync --help` for full details on all available options.
166
164
 
167
165
  ## Key Features
168
166
 
169
167
  - **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.
170
168
  - **GitHub URL support** — Paste a GitHub blob URL and it will automatically convert it to the raw content URL.
171
- - **Selective exclusions** — Keep project-specific overrides (like `target-version`) from being clobbered by the upstream config.
169
+ - **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
172
170
  - **Works with any host** — GitHub, GitLab, Bitbucket, or any raw URL that serves a `pyproject.toml`.
171
+ - **CI-ready `check` command** — Verify that your local config is in sync without modifying anything. Exits 1 if out of sync, making it perfect for pre-merge gates. ([See detailed logic](#detailed-check-logic))
172
+ - **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
173
173
 
174
174
  ## Configuration
175
175
 
@@ -177,16 +177,51 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
177
177
 
178
178
  ```toml
179
179
  [tool.ruff-sync]
180
+ # The source of truth for your Ruff configuration
181
+ upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"
182
+
180
183
  # Use simple names for top-level keys, and dotted paths for nested keys
181
184
  exclude = [
182
- "target-version", # A top-level key under [tool.ruff]
183
- "lint.per-file-ignores", # A nested key under [tool.ruff.lint]
184
- "lint.ignore"
185
+ "target-version", # Top-level [tool.ruff] key — projects target different Python versions
186
+ "lint.per-file-ignores", # Project-specific file overrides
187
+ "lint.ignore", # Project-specific rule suppressions
188
+ "lint.isort.known-first-party", # Every project has different first-party packages
189
+ "lint.flake8-tidy-imports.banned-api", # Entire plugin section — project-specific banned APIs
190
+ "lint.pydocstyle.convention", # Teams may disagree on google vs numpy vs pep257
185
191
  ]
186
192
  ```
187
193
 
188
- This sets the default exclusions so you don't need to pass `--exclude` on the command line every time.
189
- *Note: Any explicitly provided CLI arguments will override the list in `pyproject.toml`.*
194
+ This sets the default upstream and exclusions so you don't need to pass them on the command line every time.
195
+ *Note: Any explicitly provided CLI arguments will override the values in `pyproject.toml`.*
196
+
197
+ ## CI Integration
198
+
199
+ The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
200
+
201
+ ```yaml
202
+ # .github/workflows/ci.yaml
203
+ - name: Check ruff config is in sync
204
+ run: |
205
+ ruff-sync check --semantic
206
+ ```
207
+
208
+ With `--semantic`, minor reformatting of your local file won't cause a false positive — only actual rule or value differences will fail the check.
209
+
210
+ To see exactly what's drifted, omit `--no-diff` (the default) and the output will include a unified diff:
211
+
212
+ ```console
213
+ $ ruff-sync check --semantic
214
+ 🔍 Checking Ruff sync status...
215
+ ❌ Ruff configuration at pyproject.toml is out of sync!
216
+ --- local (semantic)
217
+ +++ upstream (semantic)
218
+ @@ -5,6 +5,7 @@
219
+ "select": [
220
+ + "PERF",
221
+ "RUF",
222
+ ...
223
+ ]
224
+ ```
190
225
 
191
226
  ## Example Workflow
192
227
 
@@ -203,6 +238,44 @@ git diff pyproject.toml # review the changes
203
238
  git commit -am "sync ruff config from upstream"
204
239
  ```
205
240
 
241
+ ## Detailed Check Logic
242
+
243
+ When you run `ruff-sync check`, it follows this process to determine if your project has drifted from the upstream source:
244
+
245
+ ```mermaid
246
+ flowchart TD
247
+ Start([Start]) --> Local[Read Local pyproject.toml]
248
+ Local --> Upstream[Download Upstream pyproject.toml]
249
+ Upstream --> Extract[Extract tool.ruff section]
250
+ Extract --> Exclude[Apply Exclusions]
251
+ Exclude --> Merge[Perform in-memory Merge]
252
+
253
+ subgraph Comparison [Comparison Logic]
254
+ direction TB
255
+ SemanticNode{--semantic?}
256
+ SemanticNode -- Yes --> Unwrap[Unwrap TOML objects to Python Dicts]
257
+ Unwrap --> CompareVal[Compare Key/Value Pairs]
258
+ SemanticNode -- No --> CompareFull[Compare Full File Strings]
259
+ end
260
+
261
+ Merge --> Comparison
262
+
263
+ CompareVal --> ResultNode{Match?}
264
+ CompareFull --> ResultNode
265
+
266
+ ResultNode -- Yes --> Success([Exit 0: In Sync])
267
+ ResultNode -- No --> Diff[Generate Diff]
268
+ Diff --> Fail([Exit 1: Out of Sync])
269
+
270
+ %% Styling
271
+ style Start fill:#4a90e2,color:#fff,stroke:#357abd
272
+ style Success fill:#48c774,color:#fff,stroke:#36975a
273
+ style Fail fill:#f14668,color:#fff,stroke:#b2334b
274
+ style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
275
+ style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
276
+ style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
277
+ ```
278
+
206
279
  ## Contributing
207
280
 
208
281
  This project uses:
@@ -225,9 +298,15 @@ uv run pytest -vv # test
225
298
 
226
299
  ## Dogfooding
227
300
 
228
- 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.
301
+ To see `ruff-sync` in action, you can "dogfood" it on this project's own config.
302
+
303
+ **Check if this project is in sync with its upstream:**
304
+
305
+ ```console
306
+ ./scripts/dogfood_check.sh
307
+ ```
229
308
 
230
- We've provided a script to make this easy:
309
+ **Or sync from a large upstream like Pydantic's config:**
231
310
 
232
311
  ```console
233
312
  ./scripts/dogfood.sh
@@ -10,7 +10,9 @@
10
10
 
11
11
  # ruff-sync
12
12
 
13
- **Keep your Ruff config consistent across every repo — automatically.**
13
+ **Keep your Ruff config consistent across multiple projects.**
14
+
15
+ `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/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.
14
16
 
15
17
  ---
16
18
 
@@ -19,15 +21,15 @@
19
21
  - [The Problem](#the-problem)
20
22
  - [How It Works](#how-it-works)
21
23
  - [Quick Start](#quick-start)
22
- - [Install](#install)
23
- - [Usage](#usage)
24
24
  - [Key Features](#key-features)
25
25
  - [Configuration](#configuration)
26
+ - [CI Integration](#ci-integration)
27
+ - [Example Workflow](#example-workflow)
28
+ - [Detailed Check Logic](#detailed-check-logic)
26
29
  - [Contributing](#contributing)
30
+ - [Dogfooding](#dogfooding)
27
31
  - [License](#license)
28
32
 
29
- `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/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.
30
-
31
33
  ## The Problem
32
34
 
33
35
  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.
@@ -44,7 +46,7 @@ Ruff's `extend` is perfect inside a monorepo, but if your projects live in **sep
44
46
 
45
47
  **That's what `ruff-sync` does.**
46
48
 
47
- ## How It Works
49
+ ### How It Works
48
50
 
49
51
  ```
50
52
  ┌─────────────────────────────┐
@@ -112,34 +114,32 @@ uv tool install git+https://github.com/Kilo59/ruff-sync
112
114
  # Sync from a GitHub URL (blob URLs are auto-converted to raw)
113
115
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
114
116
 
117
+ # Once configured in pyproject.toml (see Configuration), simply run:
118
+ ruff-sync
119
+
115
120
  # Sync into a specific project directory
116
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml --source ./my-project
121
+ ruff-sync --source ./my-project
117
122
 
118
123
  # Exclude specific sections from being overwritten using dotted paths
119
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml --exclude lint.per-file-ignores lint.ignore
120
- ```
124
+ ruff-sync --exclude lint.per-file-ignores lint.ignore
121
125
 
122
- ### CLI Reference
126
+ # Check if your local config is in sync (useful in CI)
127
+ ruff-sync check https://github.com/my-org/standards/blob/main/pyproject.toml
123
128
 
129
+ # Semantic check — ignore cosmetic differences like comments and whitespace
130
+ ruff-sync check --semantic
124
131
  ```
125
- usage: ruff-sync [-h] [--source SOURCE] [--exclude EXCLUDE [EXCLUDE ...]] upstream
126
132
 
127
- positional arguments:
128
- upstream The URL to download the pyproject.toml file from.
129
-
130
- optional arguments:
131
- -h, --help show this help message and exit
132
- --source SOURCE The directory to sync the pyproject.toml file to. Default: .
133
- --exclude EXCLUDE [EXCLUDE ...]
134
- Exclude certain ruff configs. Default: lint.per-file-ignores
135
- ```
133
+ Run `ruff-sync --help` for full details on all available options.
136
134
 
137
135
  ## Key Features
138
136
 
139
137
  - **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.
140
138
  - **GitHub URL support** — Paste a GitHub blob URL and it will automatically convert it to the raw content URL.
141
- - **Selective exclusions** — Keep project-specific overrides (like `target-version`) from being clobbered by the upstream config.
139
+ - **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
142
140
  - **Works with any host** — GitHub, GitLab, Bitbucket, or any raw URL that serves a `pyproject.toml`.
141
+ - **CI-ready `check` command** — Verify that your local config is in sync without modifying anything. Exits 1 if out of sync, making it perfect for pre-merge gates. ([See detailed logic](#detailed-check-logic))
142
+ - **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
143
143
 
144
144
  ## Configuration
145
145
 
@@ -147,16 +147,51 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
147
147
 
148
148
  ```toml
149
149
  [tool.ruff-sync]
150
+ # The source of truth for your Ruff configuration
151
+ upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"
152
+
150
153
  # Use simple names for top-level keys, and dotted paths for nested keys
151
154
  exclude = [
152
- "target-version", # A top-level key under [tool.ruff]
153
- "lint.per-file-ignores", # A nested key under [tool.ruff.lint]
154
- "lint.ignore"
155
+ "target-version", # Top-level [tool.ruff] key — projects target different Python versions
156
+ "lint.per-file-ignores", # Project-specific file overrides
157
+ "lint.ignore", # Project-specific rule suppressions
158
+ "lint.isort.known-first-party", # Every project has different first-party packages
159
+ "lint.flake8-tidy-imports.banned-api", # Entire plugin section — project-specific banned APIs
160
+ "lint.pydocstyle.convention", # Teams may disagree on google vs numpy vs pep257
155
161
  ]
156
162
  ```
157
163
 
158
- This sets the default exclusions so you don't need to pass `--exclude` on the command line every time.
159
- *Note: Any explicitly provided CLI arguments will override the list in `pyproject.toml`.*
164
+ This sets the default upstream and exclusions so you don't need to pass them on the command line every time.
165
+ *Note: Any explicitly provided CLI arguments will override the values in `pyproject.toml`.*
166
+
167
+ ## CI Integration
168
+
169
+ The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
170
+
171
+ ```yaml
172
+ # .github/workflows/ci.yaml
173
+ - name: Check ruff config is in sync
174
+ run: |
175
+ ruff-sync check --semantic
176
+ ```
177
+
178
+ With `--semantic`, minor reformatting of your local file won't cause a false positive — only actual rule or value differences will fail the check.
179
+
180
+ To see exactly what's drifted, omit `--no-diff` (the default) and the output will include a unified diff:
181
+
182
+ ```console
183
+ $ ruff-sync check --semantic
184
+ 🔍 Checking Ruff sync status...
185
+ ❌ Ruff configuration at pyproject.toml is out of sync!
186
+ --- local (semantic)
187
+ +++ upstream (semantic)
188
+ @@ -5,6 +5,7 @@
189
+ "select": [
190
+ + "PERF",
191
+ "RUF",
192
+ ...
193
+ ]
194
+ ```
160
195
 
161
196
  ## Example Workflow
162
197
 
@@ -173,6 +208,44 @@ git diff pyproject.toml # review the changes
173
208
  git commit -am "sync ruff config from upstream"
174
209
  ```
175
210
 
211
+ ## Detailed Check Logic
212
+
213
+ When you run `ruff-sync check`, it follows this process to determine if your project has drifted from the upstream source:
214
+
215
+ ```mermaid
216
+ flowchart TD
217
+ Start([Start]) --> Local[Read Local pyproject.toml]
218
+ Local --> Upstream[Download Upstream pyproject.toml]
219
+ Upstream --> Extract[Extract tool.ruff section]
220
+ Extract --> Exclude[Apply Exclusions]
221
+ Exclude --> Merge[Perform in-memory Merge]
222
+
223
+ subgraph Comparison [Comparison Logic]
224
+ direction TB
225
+ SemanticNode{--semantic?}
226
+ SemanticNode -- Yes --> Unwrap[Unwrap TOML objects to Python Dicts]
227
+ Unwrap --> CompareVal[Compare Key/Value Pairs]
228
+ SemanticNode -- No --> CompareFull[Compare Full File Strings]
229
+ end
230
+
231
+ Merge --> Comparison
232
+
233
+ CompareVal --> ResultNode{Match?}
234
+ CompareFull --> ResultNode
235
+
236
+ ResultNode -- Yes --> Success([Exit 0: In Sync])
237
+ ResultNode -- No --> Diff[Generate Diff]
238
+ Diff --> Fail([Exit 1: Out of Sync])
239
+
240
+ %% Styling
241
+ style Start fill:#4a90e2,color:#fff,stroke:#357abd
242
+ style Success fill:#48c774,color:#fff,stroke:#36975a
243
+ style Fail fill:#f14668,color:#fff,stroke:#b2334b
244
+ style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
245
+ style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
246
+ style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
247
+ ```
248
+
176
249
  ## Contributing
177
250
 
178
251
  This project uses:
@@ -195,9 +268,15 @@ uv run pytest -vv # test
195
268
 
196
269
  ## Dogfooding
197
270
 
198
- 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.
271
+ To see `ruff-sync` in action, you can "dogfood" it on this project's own config.
272
+
273
+ **Check if this project is in sync with its upstream:**
274
+
275
+ ```console
276
+ ./scripts/dogfood_check.sh
277
+ ```
199
278
 
200
- We've provided a script to make this easy:
279
+ **Or sync from a large upstream like Pydantic's config:**
201
280
 
202
281
  ```console
203
282
  ./scripts/dogfood.sh
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ruff-sync"
3
- version = "0.0.1.dev3"
3
+ version = "0.0.2"
4
4
  description = "Synchronize Ruff linter configuration across projects"
5
5
  keywords = ["ruff", "linter", "config", "synchronize", "python", "linting", "automation", "tomlkit"]
6
6
  authors = [
@@ -43,6 +43,7 @@ dev = [
43
43
  "coverage>=7.4.4",
44
44
  "invoke>=2.2.0",
45
45
  "mypy>=1.10.0",
46
+ "packaging>=26.0",
46
47
  "pre-commit>=3.7.0",
47
48
  "pyfakefs>=5.4.1",
48
49
  "pytest>=8.0.0",
@@ -80,7 +81,7 @@ exclude = ["target-version", "line-length", "lint.per-file-ignores", "lint.ignor
80
81
 
81
82
  [tool.ruff]
82
83
  target-version = "py310"
83
- line-length = 90
84
+ line-length = 100
84
85
 
85
86
  [tool.ruff.lint]
86
87
  select = [