ruff-sync 0.1.0.dev2__tar.gz → 0.1.2.dev1__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 (90) hide show
  1. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.pre-commit-config.yaml +5 -1
  2. ruff_sync-0.1.2.dev1/.pre-commit-hooks.yaml +15 -0
  3. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/AGENTS.md +1 -1
  4. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/PKG-INFO +77 -18
  5. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/README.md +76 -17
  6. ruff_sync-0.1.2.dev1/configs/data-science-engineering/ruff.toml +25 -0
  7. ruff_sync-0.1.2.dev1/docs/assets/favicon.png +0 -0
  8. ruff_sync-0.1.2.dev1/docs/assets/logo.png +0 -0
  9. ruff_sync-0.1.2.dev1/docs/best-practices.md +79 -0
  10. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/ci-integration.md +11 -19
  11. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/configuration.md +10 -3
  12. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/index.md +28 -8
  13. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/installation.md +3 -3
  14. ruff_sync-0.1.2.dev1/docs/pre-commit.md +92 -0
  15. ruff_sync-0.1.2.dev1/docs/troubleshooting.md +85 -0
  16. ruff_sync-0.1.2.dev1/docs/usage.md +207 -0
  17. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/mkdocs.yml +8 -3
  18. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/pyproject.toml +5 -2
  19. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/cli.py +38 -8
  20. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/core.py +156 -26
  21. ruff_sync-0.1.2.dev1/src/ruff_sync/pre_commit.py +149 -0
  22. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_basic.py +169 -1
  23. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_check.py +47 -0
  24. ruff_sync-0.1.2.dev1/tests/test_pre_commit.py +117 -0
  25. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/uv.lock +141 -125
  26. ruff_sync-0.1.0.dev2/docs/troubleshooting.md +0 -46
  27. ruff_sync-0.1.0.dev2/docs/usage.md +0 -100
  28. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/TESTING.md +0 -0
  29. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
  30. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
  31. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
  32. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
  33. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
  34. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
  35. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/release-notes-generation/SKILL.md +0 -0
  36. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/workflows/add-test-case.md +0 -0
  37. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.git-blame-ignore-revs +0 -0
  38. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/dependabot.yml +0 -0
  39. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/ci.yaml +0 -0
  40. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/complexity.yaml +0 -0
  41. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/docs.yaml +0 -0
  42. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.gitignore +0 -0
  43. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/LICENSE.md +0 -0
  44. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/codecov.yml +0 -0
  45. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/configs/fastapi/ruff.toml +0 -0
  46. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/configs/kitchen-sink/ruff.toml +0 -0
  47. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/assets/ruff_sync_banner.png +0 -0
  48. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/gen_ref_pages.py +0 -0
  49. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/check_dogfood.sh +0 -0
  50. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/gitclone_dogfood.sh +0 -0
  51. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/pull_dogfood.sh +0 -0
  52. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/skills-lock.json +0 -0
  53. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__init__.py +0 -0
  54. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__main__.py +0 -0
  55. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tasks.py +0 -0
  56. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/__init__.py +0 -0
  57. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/conftest.py +0 -0
  58. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
  59. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
  60. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
  61. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
  62. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  63. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  64. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  65. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  66. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  67. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  68. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
  69. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  70. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  71. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
  72. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
  73. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
  74. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
  75. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  76. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  77. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/ruff.toml +0 -0
  78. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_config_validation.py +0 -0
  79. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_corner_cases.py +0 -0
  80. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_deprecation.py +0 -0
  81. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_e2e.py +0 -0
  82. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_git_fetch.py +0 -0
  83. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_project.py +0 -0
  84. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_scaffold.py +0 -0
  85. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_toml_operations.py +0 -0
  86. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_url_handling.py +0 -0
  87. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_whitespace.py +0 -0
  88. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  89. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  90. {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -7,12 +7,16 @@ repos:
7
7
  - id: check-yaml
8
8
  args: ["--unsafe"]
9
9
  exclude: .agents/skills/mkdocs-generation/templates/mkdocs.yml
10
+ - id: check-toml
11
+ - id: check-case-conflict
10
12
  - id: end-of-file-fixer
11
13
  - id: trailing-whitespace
14
+ - id: mixed-line-ending
15
+ args: ["--fix=lf"]
12
16
  - id: no-commit-to-branch
13
17
  args: [--branch, develop, --branch, main]
14
18
  - repo: https://github.com/astral-sh/ruff-pre-commit
15
- rev: "v0.15.5"
19
+ rev: "v0.15.6"
16
20
  hooks:
17
21
  - id: ruff-check
18
22
  args: ["--fix"]
@@ -0,0 +1,15 @@
1
+ - id: ruff-sync-pull
2
+ name: ruff-sync-pull
3
+ description: Pull and apply upstream ruff configuration.
4
+ entry: ruff-sync pull
5
+ language: python
6
+ files: ^(\.ruff\.toml|ruff\.toml|pyproject\.toml)$
7
+ pass_filenames: false
8
+
9
+ - id: ruff-sync-check
10
+ name: ruff-sync-check
11
+ description: Check if ruff configuration is in sync with upstream.
12
+ entry: ruff-sync check --semantic
13
+ language: python
14
+ files: ^(\.ruff\.toml|ruff\.toml|pyproject\.toml)$
15
+ pass_filenames: false
@@ -87,7 +87,7 @@ uv run ruff check . --fix
87
87
  - Tests have additional overrides in `tests/ruff.toml` (extends the root config).
88
88
  - All Python files must include `from __future__ import annotations` (enforced by isort rule `I002`).
89
89
  - Use `uv run ruff check . --fix` to auto-fix issues. Use `--unsafe-fixes` only if explicitly asked.
90
- - **Do NOT disable or ignore rules** unless the user explicitly asks you to.
90
+ - **Do NOT disable or ignore rules** unless the user explicitly asks you to. You must **fix the underlying code** to pass the linter, rather than appending `# noqa` directives or adding rules to `ignore` in `pyproject.toml`.
91
91
 
92
92
  #### Understanding a Rule
93
93
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ruff-sync
3
- Version: 0.1.0.dev2
3
+ Version: 0.1.2.dev1
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://kilo59.github.io/ruff-sync/
@@ -54,6 +54,7 @@ Description-Content-Type: text/markdown
54
54
  - [Quick Start](#quick-start)
55
55
  - [Key Features](#key-features)
56
56
  - [Configuration](#configuration)
57
+ - [Pre-commit Integration](#pre-commit-integration)
57
58
  - [CI Integration](#ci-integration)
58
59
  - [Example Workflow](#example-workflow)
59
60
  - [Detailed Check Logic](#detailed-check-logic)
@@ -132,33 +133,60 @@ pip install ruff-sync
132
133
 
133
134
  ### Usage
134
135
 
136
+ **The Basic Sync**
137
+
135
138
  ```console
136
- # Sync from a GitHub/GitLab repository (root or specific directory)
139
+ # Pull rules from a central repository into your current project
137
140
  ruff-sync https://github.com/my-org/standards
138
- ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
141
+ ```
142
+
143
+ **Persistent Configuration**
144
+
145
+ ```console
146
+ # If configured in pyproject.toml (see Configuration), simply run:
147
+ ruff-sync
148
+ ```
149
+
150
+ **Initializing a New Project**
139
151
 
140
- # Or a direct blob/file URL (auto-converts to raw)
152
+ ```console
153
+ # Scaffold a new pyproject.toml if your directory is empty
154
+ ruff-sync https://github.com/my-org/standards --init
155
+ ```
156
+
157
+ **Syncing Subdirectories or Specific Files**
158
+
159
+ ```console
160
+ ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
141
161
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
162
+ ```
142
163
 
143
- # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
144
- # You can use the --branch flag to specify a branch (default: main)
164
+ **Using Git (SSH/HTTP)**
165
+
166
+ ```console
167
+ # Clones efficiently (depth 1, blob:none) to extract the config
145
168
  ruff-sync git@github.com:my-org/standards.git
146
- ruff-sync ssh://git@gitlab.com/my-org/standards.git
169
+ ```
147
170
 
148
- # Or if configured in pyproject.toml (see Configuration), simply run:
149
- ruff-sync
171
+ **Excluding Specific Rules**
150
172
 
173
+ ```console
151
174
  # Exclude specific sections from being overwritten using dotted paths
152
- ruff-sync --exclude lint.per-file-ignores lint.ignore
175
+ ruff-sync --exclude lint.ignore
176
+ ```
177
+
178
+ **Checking for Drift (CI)**
153
179
 
154
- # Check if your local config is in sync (useful in CI)
180
+ ```console
181
+ # Verify local config matches upstream. Exits 1 if config is out of sync.
182
+ # If opted in via --pre-commit, exits 2 if only the pre-commit hook is out of sync.
155
183
  ruff-sync check https://github.com/my-org/standards
156
184
 
157
- # Semantic check — ignore cosmetic differences like comments and whitespace
185
+ # Semantic check — ignores cosmetic differences like comments and whitespace
158
186
  ruff-sync check --semantic
159
187
  ```
160
188
 
161
- Run `ruff-sync --help` for full details on all available options.
189
+ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for more detailed examples and advanced workflows.
162
190
 
163
191
  ## Key Features
164
192
 
@@ -171,6 +199,7 @@ Run `ruff-sync --help` for full details on all available options.
171
199
  - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
172
200
  - 🤖 **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))
173
201
  - 🧠 **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
202
+ - 🔗 **Pre-commit hook sync** — Use `--pre-commit` to automatically keep your `ruff-pre-commit` hook version in `.pre-commit-config.yaml` matching your project's Ruff version.
174
203
 
175
204
  ## Configuration
176
205
 
@@ -181,6 +210,9 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
181
210
  # The source of truth for your Ruff configuration
182
211
  upstream = "https://github.com/my-org/standards"
183
212
 
213
+ # Automatically sync the pre-commit Ruff hook version
214
+ pre-commit-version-sync = true
215
+
184
216
  # Use simple names for top-level keys, and dotted paths for nested keys
185
217
  exclude = [
186
218
  "target-version", # Top-level [tool.ruff] key — projects target different Python versions
@@ -215,14 +247,30 @@ exclude = [
215
247
  # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
216
248
  branch = "develop"
217
249
 
218
- # A directory prefix to use when looking for a configuration file in a repository. (Default: "")
250
+ # The directory path within the repository where the config is located. (Default: "")
219
251
  # Useful if the upstream config is not at the repository root.
220
252
  path = "config/ruff"
221
253
 
222
254
  # The local target directory or file to sync into. (Default: ".")
223
255
  to = "."
256
+
257
+ # Keep the pre-commit Ruff hook version in sync with the project's Ruff version. (Default: false)
258
+ pre-commit-version-sync = true
224
259
  ```
225
260
 
261
+ ## Pre-commit Integration
262
+
263
+ Ensure your configuration is always in sync before every commit. Add this to your `.pre-commit-config.yaml`:
264
+
265
+ ```yaml
266
+ - repo: https://github.com/Kilo59/ruff-sync
267
+ rev: v0.1.0 # Use the latest version
268
+ hooks:
269
+ - id: ruff-sync-check
270
+ ```
271
+
272
+ See the [Pre-commit Guide](https://kilo59.github.io/ruff-sync/pre-commit/) for more details.
273
+
226
274
  ## CI Integration
227
275
 
228
276
  The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
@@ -252,6 +300,10 @@ $ ruff-sync check --semantic
252
300
  ]
253
301
  ```
254
302
 
303
+ > [!TIP]
304
+ > See the [Best Practices](https://kilo59.github.io/ruff-sync/best-practices/) guide for recommendations on whether to make your CI checks blocking or informational.
305
+
306
+
255
307
  ## Example Workflow
256
308
 
257
309
  A typical setup for an organization:
@@ -319,7 +371,7 @@ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml
319
371
 
320
372
  ```console
321
373
  # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
322
- ruff-sync pull https://github.com/my-org/standards --init
374
+ ruff-sync https://github.com/my-org/standards --init
323
375
  ```
324
376
 
325
377
  `ruff-sync` seamlessly supports both `pyproject.toml` and standalone `ruff.toml` (or `.ruff.toml`) files. If your local target is a directory, it will look for configuration files in the following order: `ruff.toml` -> `.ruff.toml` -> `pyproject.toml`. If your upstream source or your local target is a `ruff.toml`, it will automatically adapt and sync the root configuration rather than looking for a `[tool.ruff]` section.
@@ -349,18 +401,25 @@ flowchart TD
349
401
 
350
402
  Merge --> Comparison
351
403
 
352
- CompareVal --> ResultNode{Match?}
404
+ CompareVal --> ResultNode{Ruff Sync Match?}
353
405
  CompareFull --> ResultNode
354
406
 
355
- ResultNode -- Yes --> Success([Exit 0: In Sync])
407
+ ResultNode -- Yes --> PCNode{--pre-commit?}
408
+ PCNode -- Yes --> CheckPC[Check pre-commit hook version]
409
+ CheckPC -- Match --> Success([Exit 0: In Sync])
410
+ CheckPC -- Mismatch --> PCOut([Exit 2: Pre-commit Out of Sync])
411
+ PCNode -- No --> Success
412
+
356
413
  ResultNode -- No --> Diff[Generate Diff]
357
- Diff --> Fail([Exit 1: Out of Sync])
414
+ Diff --> Fail([Exit 1: Ruff Config Out of Sync])
358
415
 
359
416
  %% Styling
360
417
  style Start fill:#4a90e2,color:#fff,stroke:#357abd
361
418
  style Success fill:#48c774,color:#fff,stroke:#36975a
362
419
  style Fail fill:#f14668,color:#fff,stroke:#b2334b
420
+ style PCOut fill:#ff9800,color:#fff,stroke:#e65100
363
421
  style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
422
+ style PCNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
364
423
  style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
365
424
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
366
425
  ```
@@ -23,6 +23,7 @@
23
23
  - [Quick Start](#quick-start)
24
24
  - [Key Features](#key-features)
25
25
  - [Configuration](#configuration)
26
+ - [Pre-commit Integration](#pre-commit-integration)
26
27
  - [CI Integration](#ci-integration)
27
28
  - [Example Workflow](#example-workflow)
28
29
  - [Detailed Check Logic](#detailed-check-logic)
@@ -101,33 +102,60 @@ pip install ruff-sync
101
102
 
102
103
  ### Usage
103
104
 
105
+ **The Basic Sync**
106
+
104
107
  ```console
105
- # Sync from a GitHub/GitLab repository (root or specific directory)
108
+ # Pull rules from a central repository into your current project
106
109
  ruff-sync https://github.com/my-org/standards
107
- ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
110
+ ```
111
+
112
+ **Persistent Configuration**
113
+
114
+ ```console
115
+ # If configured in pyproject.toml (see Configuration), simply run:
116
+ ruff-sync
117
+ ```
118
+
119
+ **Initializing a New Project**
108
120
 
109
- # Or a direct blob/file URL (auto-converts to raw)
121
+ ```console
122
+ # Scaffold a new pyproject.toml if your directory is empty
123
+ ruff-sync https://github.com/my-org/standards --init
124
+ ```
125
+
126
+ **Syncing Subdirectories or Specific Files**
127
+
128
+ ```console
129
+ ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
110
130
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
131
+ ```
111
132
 
112
- # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
113
- # You can use the --branch flag to specify a branch (default: main)
133
+ **Using Git (SSH/HTTP)**
134
+
135
+ ```console
136
+ # Clones efficiently (depth 1, blob:none) to extract the config
114
137
  ruff-sync git@github.com:my-org/standards.git
115
- ruff-sync ssh://git@gitlab.com/my-org/standards.git
138
+ ```
116
139
 
117
- # Or if configured in pyproject.toml (see Configuration), simply run:
118
- ruff-sync
140
+ **Excluding Specific Rules**
119
141
 
142
+ ```console
120
143
  # Exclude specific sections from being overwritten using dotted paths
121
- ruff-sync --exclude lint.per-file-ignores lint.ignore
144
+ ruff-sync --exclude lint.ignore
145
+ ```
146
+
147
+ **Checking for Drift (CI)**
122
148
 
123
- # Check if your local config is in sync (useful in CI)
149
+ ```console
150
+ # Verify local config matches upstream. Exits 1 if config is out of sync.
151
+ # If opted in via --pre-commit, exits 2 if only the pre-commit hook is out of sync.
124
152
  ruff-sync check https://github.com/my-org/standards
125
153
 
126
- # Semantic check — ignore cosmetic differences like comments and whitespace
154
+ # Semantic check — ignores cosmetic differences like comments and whitespace
127
155
  ruff-sync check --semantic
128
156
  ```
129
157
 
130
- Run `ruff-sync --help` for full details on all available options.
158
+ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for more detailed examples and advanced workflows.
131
159
 
132
160
  ## Key Features
133
161
 
@@ -140,6 +168,7 @@ Run `ruff-sync --help` for full details on all available options.
140
168
  - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
141
169
  - 🤖 **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
170
  - 🧠 **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
171
+ - 🔗 **Pre-commit hook sync** — Use `--pre-commit` to automatically keep your `ruff-pre-commit` hook version in `.pre-commit-config.yaml` matching your project's Ruff version.
143
172
 
144
173
  ## Configuration
145
174
 
@@ -150,6 +179,9 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
150
179
  # The source of truth for your Ruff configuration
151
180
  upstream = "https://github.com/my-org/standards"
152
181
 
182
+ # Automatically sync the pre-commit Ruff hook version
183
+ pre-commit-version-sync = true
184
+
153
185
  # Use simple names for top-level keys, and dotted paths for nested keys
154
186
  exclude = [
155
187
  "target-version", # Top-level [tool.ruff] key — projects target different Python versions
@@ -184,14 +216,30 @@ exclude = [
184
216
  # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
185
217
  branch = "develop"
186
218
 
187
- # A directory prefix to use when looking for a configuration file in a repository. (Default: "")
219
+ # The directory path within the repository where the config is located. (Default: "")
188
220
  # Useful if the upstream config is not at the repository root.
189
221
  path = "config/ruff"
190
222
 
191
223
  # The local target directory or file to sync into. (Default: ".")
192
224
  to = "."
225
+
226
+ # Keep the pre-commit Ruff hook version in sync with the project's Ruff version. (Default: false)
227
+ pre-commit-version-sync = true
193
228
  ```
194
229
 
230
+ ## Pre-commit Integration
231
+
232
+ Ensure your configuration is always in sync before every commit. Add this to your `.pre-commit-config.yaml`:
233
+
234
+ ```yaml
235
+ - repo: https://github.com/Kilo59/ruff-sync
236
+ rev: v0.1.0 # Use the latest version
237
+ hooks:
238
+ - id: ruff-sync-check
239
+ ```
240
+
241
+ See the [Pre-commit Guide](https://kilo59.github.io/ruff-sync/pre-commit/) for more details.
242
+
195
243
  ## CI Integration
196
244
 
197
245
  The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
@@ -221,6 +269,10 @@ $ ruff-sync check --semantic
221
269
  ]
222
270
  ```
223
271
 
272
+ > [!TIP]
273
+ > See the [Best Practices](https://kilo59.github.io/ruff-sync/best-practices/) guide for recommendations on whether to make your CI checks blocking or informational.
274
+
275
+
224
276
  ## Example Workflow
225
277
 
226
278
  A typical setup for an organization:
@@ -288,7 +340,7 @@ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml
288
340
 
289
341
  ```console
290
342
  # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
291
- ruff-sync pull https://github.com/my-org/standards --init
343
+ ruff-sync https://github.com/my-org/standards --init
292
344
  ```
293
345
 
294
346
  `ruff-sync` seamlessly supports both `pyproject.toml` and standalone `ruff.toml` (or `.ruff.toml`) files. If your local target is a directory, it will look for configuration files in the following order: `ruff.toml` -> `.ruff.toml` -> `pyproject.toml`. If your upstream source or your local target is a `ruff.toml`, it will automatically adapt and sync the root configuration rather than looking for a `[tool.ruff]` section.
@@ -318,18 +370,25 @@ flowchart TD
318
370
 
319
371
  Merge --> Comparison
320
372
 
321
- CompareVal --> ResultNode{Match?}
373
+ CompareVal --> ResultNode{Ruff Sync Match?}
322
374
  CompareFull --> ResultNode
323
375
 
324
- ResultNode -- Yes --> Success([Exit 0: In Sync])
376
+ ResultNode -- Yes --> PCNode{--pre-commit?}
377
+ PCNode -- Yes --> CheckPC[Check pre-commit hook version]
378
+ CheckPC -- Match --> Success([Exit 0: In Sync])
379
+ CheckPC -- Mismatch --> PCOut([Exit 2: Pre-commit Out of Sync])
380
+ PCNode -- No --> Success
381
+
325
382
  ResultNode -- No --> Diff[Generate Diff]
326
- Diff --> Fail([Exit 1: Out of Sync])
383
+ Diff --> Fail([Exit 1: Ruff Config Out of Sync])
327
384
 
328
385
  %% Styling
329
386
  style Start fill:#4a90e2,color:#fff,stroke:#357abd
330
387
  style Success fill:#48c774,color:#fff,stroke:#36975a
331
388
  style Fail fill:#f14668,color:#fff,stroke:#b2334b
389
+ style PCOut fill:#ff9800,color:#fff,stroke:#e65100
332
390
  style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
391
+ style PCNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
333
392
  style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
334
393
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
335
394
  ```
@@ -0,0 +1,25 @@
1
+ # configs/data-science-engineering/ruff.toml
2
+
3
+ # Enable Jupyter notebook linting
4
+ extend-include = ["*.ipynb"]
5
+ extend-exclude = [".ipynb_checkpoints"]
6
+
7
+ [lint]
8
+ # Enable rules tailored for data science and engineering.
9
+ extend-select = [
10
+ # https://docs.astral.sh/ruff/rules/#isort-i
11
+ "I", # isort: Import sorting
12
+ # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy
13
+ "NPY", # NumPy-specific rules: NumPy conventions
14
+ # https://docs.astral.sh/ruff/rules/#pandas-vet-pd
15
+ "PD", # pandas-vet: Pandas code checks
16
+ ]
17
+ # Ignore rules that conflict with `ruff format` (formatter-managed concerns).
18
+ # Keep this in sync with other curated configs (e.g. fastapi/ruff.toml).
19
+ ignore = [
20
+ "E111", # indentation is handled by the formatter
21
+ "E114", # indentation with comments is handled by the formatter
22
+ "E117", # over-indented code is handled by the formatter
23
+ "E501", # line length is handled by the formatter
24
+ "W191", # tabs vs spaces is handled by the formatter
25
+ ]
@@ -0,0 +1,79 @@
1
+ # Best Practices
2
+
3
+ When managing linter configurations across multiple projects with `ruff-sync`, following these best practices helps ensure a smooth, predictable, and manageable workflow for your entire organization.
4
+
5
+ ---
6
+
7
+ ## 📌 Pin Upstream Versions
8
+
9
+ Instead of syncing directly from the `main` branch, it can be beneficial to **pin to specific tags, releases, or stable branches**.
10
+
11
+ Linting rules evolve. If you sync from `main`, a new rule added to the central repository could instantly break CI across dozens of downstream projects the next time they run `ruff-sync` in an automated pipeline.
12
+
13
+ Instead, configure your upstream like this:
14
+
15
+ ```toml
16
+ [tool.ruff-sync]
17
+ # Pin to a specific tag or commit for stability
18
+ upstream = "https://github.com/my-org/standards/blob/v1.2.0/pyproject.toml"
19
+ ```
20
+
21
+ When you are ready to roll out new linting rules, you can update the version across repositories in a controlled manner.
22
+
23
+ ---
24
+
25
+ ## 🎯 Use Strategic Exclusions
26
+
27
+ Not every setting should be centralized. Some configurations are inherently project-specific.
28
+
29
+ Use the `exclude` option to prevent `ruff-sync` from overwriting configurations that should remain local.
30
+
31
+ For example, do not force all repositories to use the same Python target version, and allow projects to selectively ignore specific upstream rules that don't make sense locally:
32
+
33
+ ```toml
34
+ [tool.ruff-sync]
35
+ upstream = "https://github.com/my-org/standards"
36
+ exclude = [
37
+ # Always preserve local file-level exceptions (excluded by default)
38
+ "lint.per-file-ignores",
39
+ # Allow projects to define their own Python version
40
+ "target-version",
41
+ # Preserve local ignored rules that don't apply to this specific project
42
+ "lint.ignore"
43
+ ]
44
+ ```
45
+
46
+ By excluding `lint.ignore`, a project can adopt the organization's standard `lint.select` list, but gracefully opt out of specific rules that cause issues in their specific domain without breaking the sync process.
47
+
48
+ ---
49
+
50
+ ## 🚦 Semantic Checks in CI
51
+
52
+ To ensure your repository hasn't drifted from your organization's unified standards, you should run `ruff-sync check --semantic` in your Continuous Integration pipeline.
53
+
54
+ **If you pin to a stable tag (as recommended above):**
55
+ Make this a **blocking check**. Since the upstream configuration won't change unexpectedly, any drift means a developer inadvertently modified the local rules. Failing the build ensures the repository stays perfectly compliant.
56
+
57
+ **If you sync directly from a moving branch (like `main`):**
58
+ We recommend making this an **informational, non-failing check**. If a new rule is added upstream while a developer is working on a feature, blocking their PR can cause frustration. Instead, pair the informational check with a **weekly automated workflow** that pulls down the latest configuration and opens a pull request. This groups linter updates into a single controllable PR that can be reviewed and tested in isolation.
59
+
60
+ See the [CI Integration Guide](ci-integration.md#automated-sync-prs) for an example GitHub Actions workflow that automatically syncs the configuration and opens a PR.
61
+
62
+ ---
63
+
64
+ ## 🏢 Hierarchical Configuration
65
+
66
+ For large organizations with distinct teams or sub-projects (like a frontend-heavy full stack repo vs. a pure backend service), you can define multiple upstreams to create a hierarchy.
67
+
68
+ Because `ruff-sync` merges multiple upstreams sequentially (from top to bottom), the "last one wins." This allows you to have a strict company-wide base config, and a slightly looser (or tighter) team-specific config.
69
+
70
+ ```toml
71
+ [tool.ruff-sync]
72
+ upstream = [
73
+ # 1. Base company rules
74
+ "https://github.com/my-org/standards/blob/main/base.toml",
75
+
76
+ # 2. Team-specific tweaks (these override the base rules)
77
+ "https://github.com/my-org/standards/blob/main/team-alpha.toml",
78
+ ]
79
+ ```
@@ -47,7 +47,7 @@ jobs:
47
47
  - uses: actions/checkout@v4
48
48
  - uses: astral-sh/setup-uv@v5
49
49
  - name: Pull upstream
50
- run: uvx ruff-sync pull
50
+ run: uvx ruff-sync
51
51
  - name: Create Pull Request
52
52
  uses: peter-evans/create-pull-request@v6
53
53
  with:
@@ -72,35 +72,27 @@ ruff-sync-check:
72
72
 
73
73
  ---
74
74
 
75
- ## 🛠️ Pre-commit Integration
76
-
77
75
  You can use `ruff-sync` with `pre-commit` to ensure your configuration is always in sync before pushing.
78
76
 
79
- Add this to your `.pre-commit-config.yaml`:
80
-
81
- ```yaml
82
- repos:
83
- - repo: local
84
- hooks:
85
- - id: ruff-sync-check
86
- name: ruff-sync-check
87
- entry: uvx ruff-sync check --semantic
88
- language: system
89
- files: ^pyproject\.toml$
90
- pass_filenames: false
91
- ```
92
-
93
- !!! note
94
- Running `ruff-sync check` in pre-commit is fast because it only performs a network request if the local `pyproject.toml` is older than the upstream or if no cache exists.
77
+ See the [Pre-commit Guide](pre-commit.md) for details on using the official hooks.
95
78
 
96
79
  ---
97
80
 
98
81
  ## 💡 Best Practices
99
82
 
83
+ > [!TIP]
84
+ > Read the complete [Best Practices](best-practices.md) guide for a broader look at organizing `ruff-sync` deployments, including when semantic checks should be blocking vs. informational.
85
+
100
86
  ### Use `--semantic`
101
87
 
102
88
  In CI, you usually only care about the functional configuration. Using `--semantic` ensures that minor formatting changes don't break your builds, while still guaranteeing that the actual rules are identical.
103
89
 
90
+ ### Handle Exclusions Properly
91
+
92
+ If your project intentionally diverges from the upstream (e.g., using different `per-file-ignores` or ignoring a specific rule), ensure those overrides are listed in the `[tool.ruff-sync]` `exclude` list in your `pyproject.toml`.
93
+
94
+ The `check` command respects your local `exclude` list. If you exclude a setting, `ruff-sync check` will completely ignore it when comparing against the upstream, ensuring that intended deviations never cause CI to fail!
95
+
104
96
  ### Use a Dedicated Workflow
105
97
 
106
98
  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.
@@ -13,11 +13,15 @@
13
13
  | `path` | `str` | `""` | The directory path within the repository where the config is located. |
14
14
  | `semantic` | `bool` | `false` | Whether `check` should default to semantic matching. |
15
15
  | `diff` | `bool` | `true` | Whether `check` should show a diff by default. |
16
+ | `pre-commit-version-sync` | `bool` | `false` | Sync the pre-commit Ruff hook version with the project's Ruff version. |
16
17
 
17
18
  ## Exclude Patterns
18
19
 
19
20
  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
 
22
+ > [!TIP]
23
+ > See [Strategic Exclusions](best-practices.md#use-strategic-exclusions) in our Best Practices guide for recommendations on what settings to keep local.
24
+
21
25
  Exclusions use dotted paths to target specific sections or keys.
22
26
 
23
27
  ### Examples
@@ -60,16 +64,19 @@ exclude = ["target-version"]
60
64
 
61
65
  #### Sequential merging of multiple sources
62
66
 
63
- You can specify multiple upstream sources as a list. They will be merged in order, with later sources overriding earlier ones.
67
+ You can specify multiple upstream sources as a list. They will be merged in order—from top to bottom—with later sources overriding or extending earlier ones.
64
68
 
65
69
  ```toml
66
70
  [tool.ruff-sync]
67
71
  upstream = [
68
- "https://github.com/my-org/shared-config",
69
- "https://github.com/my-org/team-overrides",
72
+ "https://github.com/my-org/shared-config", # 1. Base rules
73
+ "https://github.com/my-org/team-overrides", # 2. Team-specific tweaks (wins)
70
74
  ]
71
75
  ```
72
76
 
77
+ !!! tip "Last One Wins"
78
+ The merge logic follows a "last one wins" approach for simple keys (like `line-length`), while performing a deep merge for configuration tables like `lint.per-file-ignores`.
79
+
73
80
  ## Deprecation Notes
74
81
 
75
82
  - The key `source` in `[tool.ruff-sync]` is deprecated and will be removed in a future version. Use `to` instead.