ruff-sync 0.1.1.dev1__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 (88) hide show
  1. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.pre-commit-config.yaml +3 -0
  2. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/AGENTS.md +1 -1
  3. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/PKG-INFO +33 -14
  4. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/README.md +32 -13
  5. ruff_sync-0.1.2.dev1/configs/data-science-engineering/ruff.toml +25 -0
  6. ruff_sync-0.1.2.dev1/docs/best-practices.md +79 -0
  7. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/ci-integration.md +4 -1
  8. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/configuration.md +4 -0
  9. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/index.md +10 -2
  10. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/installation.md +3 -3
  11. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/pre-commit.md +24 -1
  12. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/troubleshooting.md +5 -5
  13. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/usage.md +74 -12
  14. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/mkdocs.yml +1 -0
  15. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/pyproject.toml +4 -1
  16. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/cli.py +24 -4
  17. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/core.py +57 -21
  18. ruff_sync-0.1.2.dev1/src/ruff_sync/pre_commit.py +149 -0
  19. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_check.py +47 -0
  20. ruff_sync-0.1.2.dev1/tests/test_pre_commit.py +117 -0
  21. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/uv.lock +1 -1
  22. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/TESTING.md +0 -0
  23. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
  24. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
  25. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
  26. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
  27. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
  28. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
  29. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/release-notes-generation/SKILL.md +0 -0
  30. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/workflows/add-test-case.md +0 -0
  31. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.git-blame-ignore-revs +0 -0
  32. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/dependabot.yml +0 -0
  33. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/ci.yaml +0 -0
  34. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/complexity.yaml +0 -0
  35. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/docs.yaml +0 -0
  36. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.gitignore +0 -0
  37. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.pre-commit-hooks.yaml +0 -0
  38. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/LICENSE.md +0 -0
  39. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/codecov.yml +0 -0
  40. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/configs/fastapi/ruff.toml +0 -0
  41. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/configs/kitchen-sink/ruff.toml +0 -0
  42. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/favicon.png +0 -0
  43. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/logo.png +0 -0
  44. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/ruff_sync_banner.png +0 -0
  45. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/gen_ref_pages.py +0 -0
  46. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/check_dogfood.sh +0 -0
  47. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/gitclone_dogfood.sh +0 -0
  48. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/pull_dogfood.sh +0 -0
  49. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/skills-lock.json +0 -0
  50. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__init__.py +0 -0
  51. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__main__.py +0 -0
  52. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tasks.py +0 -0
  53. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/__init__.py +0 -0
  54. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/conftest.py +0 -0
  55. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
  56. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
  57. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
  58. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
  59. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  60. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  61. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  62. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  63. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  64. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  65. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
  66. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  67. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  68. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
  69. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
  70. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
  71. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
  72. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  73. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  74. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/ruff.toml +0 -0
  75. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_basic.py +0 -0
  76. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_config_validation.py +0 -0
  77. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_corner_cases.py +0 -0
  78. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_deprecation.py +0 -0
  79. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_e2e.py +0 -0
  80. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_git_fetch.py +0 -0
  81. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_project.py +0 -0
  82. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_scaffold.py +0 -0
  83. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_toml_operations.py +0 -0
  84. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_url_handling.py +0 -0
  85. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_whitespace.py +0 -0
  86. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  87. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  88. {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -8,8 +8,11 @@ repos:
8
8
  args: ["--unsafe"]
9
9
  exclude: .agents/skills/mkdocs-generation/templates/mkdocs.yml
10
10
  - id: check-toml
11
+ - id: check-case-conflict
11
12
  - id: end-of-file-fixer
12
13
  - id: trailing-whitespace
14
+ - id: mixed-line-ending
15
+ args: ["--fix=lf"]
13
16
  - id: no-commit-to-branch
14
17
  args: [--branch, develop, --branch, main]
15
18
  - repo: https://github.com/astral-sh/ruff-pre-commit
@@ -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.1.dev1
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/
@@ -137,48 +137,49 @@ pip install ruff-sync
137
137
 
138
138
  ```console
139
139
  # Pull rules from a central repository into your current project
140
- ruff-sync pull https://github.com/my-org/standards
140
+ ruff-sync https://github.com/my-org/standards
141
141
  ```
142
142
 
143
143
  **Persistent Configuration**
144
144
 
145
145
  ```console
146
146
  # If configured in pyproject.toml (see Configuration), simply run:
147
- ruff-sync pull
147
+ ruff-sync
148
148
  ```
149
149
 
150
150
  **Initializing a New Project**
151
151
 
152
152
  ```console
153
153
  # Scaffold a new pyproject.toml if your directory is empty
154
- ruff-sync pull https://github.com/my-org/standards --init
154
+ ruff-sync https://github.com/my-org/standards --init
155
155
  ```
156
156
 
157
157
  **Syncing Subdirectories or Specific Files**
158
158
 
159
159
  ```console
160
- ruff-sync pull https://github.com/my-org/standards/tree/main/configs/shared
161
- ruff-sync pull https://github.com/my-org/standards/blob/main/pyproject.toml
160
+ ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
161
+ ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
162
162
  ```
163
163
 
164
164
  **Using Git (SSH/HTTP)**
165
165
 
166
166
  ```console
167
167
  # Clones efficiently (depth 1, blob:none) to extract the config
168
- ruff-sync pull git@github.com:my-org/standards.git
168
+ ruff-sync git@github.com:my-org/standards.git
169
169
  ```
170
170
 
171
171
  **Excluding Specific Rules**
172
172
 
173
173
  ```console
174
174
  # Exclude specific sections from being overwritten using dotted paths
175
- ruff-sync pull --exclude lint.ignore
175
+ ruff-sync --exclude lint.ignore
176
176
  ```
177
177
 
178
178
  **Checking for Drift (CI)**
179
179
 
180
180
  ```console
181
- # Verify local config matches upstream. Exits 1 if out of sync.
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.
182
183
  ruff-sync check https://github.com/my-org/standards
183
184
 
184
185
  # Semantic check — ignores cosmetic differences like comments and whitespace
@@ -198,6 +199,7 @@ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for mor
198
199
  - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
199
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))
200
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.
201
203
 
202
204
  ## Configuration
203
205
 
@@ -208,6 +210,9 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
208
210
  # The source of truth for your Ruff configuration
209
211
  upstream = "https://github.com/my-org/standards"
210
212
 
213
+ # Automatically sync the pre-commit Ruff hook version
214
+ pre-commit-version-sync = true
215
+
211
216
  # Use simple names for top-level keys, and dotted paths for nested keys
212
217
  exclude = [
213
218
  "target-version", # Top-level [tool.ruff] key — projects target different Python versions
@@ -242,12 +247,15 @@ exclude = [
242
247
  # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
243
248
  branch = "develop"
244
249
 
245
- # 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: "")
246
251
  # Useful if the upstream config is not at the repository root.
247
252
  path = "config/ruff"
248
253
 
249
254
  # The local target directory or file to sync into. (Default: ".")
250
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
251
259
  ```
252
260
 
253
261
  ## Pre-commit Integration
@@ -292,6 +300,10 @@ $ ruff-sync check --semantic
292
300
  ]
293
301
  ```
294
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
+
295
307
  ## Example Workflow
296
308
 
297
309
  A typical setup for an organization:
@@ -359,7 +371,7 @@ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml
359
371
 
360
372
  ```console
361
373
  # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
362
- ruff-sync pull https://github.com/my-org/standards --init
374
+ ruff-sync https://github.com/my-org/standards --init
363
375
  ```
364
376
 
365
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.
@@ -389,18 +401,25 @@ flowchart TD
389
401
 
390
402
  Merge --> Comparison
391
403
 
392
- CompareVal --> ResultNode{Match?}
404
+ CompareVal --> ResultNode{Ruff Sync Match?}
393
405
  CompareFull --> ResultNode
394
406
 
395
- 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
+
396
413
  ResultNode -- No --> Diff[Generate Diff]
397
- Diff --> Fail([Exit 1: Out of Sync])
414
+ Diff --> Fail([Exit 1: Ruff Config Out of Sync])
398
415
 
399
416
  %% Styling
400
417
  style Start fill:#4a90e2,color:#fff,stroke:#357abd
401
418
  style Success fill:#48c774,color:#fff,stroke:#36975a
402
419
  style Fail fill:#f14668,color:#fff,stroke:#b2334b
420
+ style PCOut fill:#ff9800,color:#fff,stroke:#e65100
403
421
  style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
422
+ style PCNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
404
423
  style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
405
424
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
406
425
  ```
@@ -106,48 +106,49 @@ pip install ruff-sync
106
106
 
107
107
  ```console
108
108
  # Pull rules from a central repository into your current project
109
- ruff-sync pull https://github.com/my-org/standards
109
+ ruff-sync https://github.com/my-org/standards
110
110
  ```
111
111
 
112
112
  **Persistent Configuration**
113
113
 
114
114
  ```console
115
115
  # If configured in pyproject.toml (see Configuration), simply run:
116
- ruff-sync pull
116
+ ruff-sync
117
117
  ```
118
118
 
119
119
  **Initializing a New Project**
120
120
 
121
121
  ```console
122
122
  # Scaffold a new pyproject.toml if your directory is empty
123
- ruff-sync pull https://github.com/my-org/standards --init
123
+ ruff-sync https://github.com/my-org/standards --init
124
124
  ```
125
125
 
126
126
  **Syncing Subdirectories or Specific Files**
127
127
 
128
128
  ```console
129
- ruff-sync pull https://github.com/my-org/standards/tree/main/configs/shared
130
- ruff-sync pull https://github.com/my-org/standards/blob/main/pyproject.toml
129
+ ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
130
+ ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
131
131
  ```
132
132
 
133
133
  **Using Git (SSH/HTTP)**
134
134
 
135
135
  ```console
136
136
  # Clones efficiently (depth 1, blob:none) to extract the config
137
- ruff-sync pull git@github.com:my-org/standards.git
137
+ ruff-sync git@github.com:my-org/standards.git
138
138
  ```
139
139
 
140
140
  **Excluding Specific Rules**
141
141
 
142
142
  ```console
143
143
  # Exclude specific sections from being overwritten using dotted paths
144
- ruff-sync pull --exclude lint.ignore
144
+ ruff-sync --exclude lint.ignore
145
145
  ```
146
146
 
147
147
  **Checking for Drift (CI)**
148
148
 
149
149
  ```console
150
- # Verify local config matches upstream. Exits 1 if out of sync.
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.
151
152
  ruff-sync check https://github.com/my-org/standards
152
153
 
153
154
  # Semantic check — ignores cosmetic differences like comments and whitespace
@@ -167,6 +168,7 @@ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for mor
167
168
  - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
168
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))
169
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.
170
172
 
171
173
  ## Configuration
172
174
 
@@ -177,6 +179,9 @@ You can configure `ruff-sync` itself in your `pyproject.toml`:
177
179
  # The source of truth for your Ruff configuration
178
180
  upstream = "https://github.com/my-org/standards"
179
181
 
182
+ # Automatically sync the pre-commit Ruff hook version
183
+ pre-commit-version-sync = true
184
+
180
185
  # Use simple names for top-level keys, and dotted paths for nested keys
181
186
  exclude = [
182
187
  "target-version", # Top-level [tool.ruff] key — projects target different Python versions
@@ -211,12 +216,15 @@ exclude = [
211
216
  # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
212
217
  branch = "develop"
213
218
 
214
- # 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: "")
215
220
  # Useful if the upstream config is not at the repository root.
216
221
  path = "config/ruff"
217
222
 
218
223
  # The local target directory or file to sync into. (Default: ".")
219
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
220
228
  ```
221
229
 
222
230
  ## Pre-commit Integration
@@ -261,6 +269,10 @@ $ ruff-sync check --semantic
261
269
  ]
262
270
  ```
263
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
+
264
276
  ## Example Workflow
265
277
 
266
278
  A typical setup for an organization:
@@ -328,7 +340,7 @@ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml
328
340
 
329
341
  ```console
330
342
  # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
331
- ruff-sync pull https://github.com/my-org/standards --init
343
+ ruff-sync https://github.com/my-org/standards --init
332
344
  ```
333
345
 
334
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.
@@ -358,18 +370,25 @@ flowchart TD
358
370
 
359
371
  Merge --> Comparison
360
372
 
361
- CompareVal --> ResultNode{Match?}
373
+ CompareVal --> ResultNode{Ruff Sync Match?}
362
374
  CompareFull --> ResultNode
363
375
 
364
- 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
+
365
382
  ResultNode -- No --> Diff[Generate Diff]
366
- Diff --> Fail([Exit 1: Out of Sync])
383
+ Diff --> Fail([Exit 1: Ruff Config Out of Sync])
367
384
 
368
385
  %% Styling
369
386
  style Start fill:#4a90e2,color:#fff,stroke:#357abd
370
387
  style Success fill:#48c774,color:#fff,stroke:#36975a
371
388
  style Fail fill:#f14668,color:#fff,stroke:#b2334b
389
+ style PCOut fill:#ff9800,color:#fff,stroke:#e65100
372
390
  style ResultNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
391
+ style PCNode fill:#ffdd57,color:#4a4a4a,stroke:#d4b106
373
392
  style Comparison fill:none,stroke:#9e9e9e,stroke-dasharray: 5 5,stroke-width:2px
374
393
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
375
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:
@@ -80,6 +80,9 @@ See the [Pre-commit Guide](pre-commit.md) for details on using the official hook
80
80
 
81
81
  ## 💡 Best Practices
82
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
+
83
86
  ### Use `--semantic`
84
87
 
85
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.
@@ -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
@@ -20,6 +20,7 @@
20
20
  * **📥 Efficient Git Support**: Shallow clones and sparse checkouts for fast extraction.
21
21
  * **🚀 Zero-Config Bootstrapping**: Use `--init` to scaffold a new project in one command.
22
22
  * **✅ CI Ready**: Built-in `check` command with semantic comparison logic.
23
+ * **🔗 Pre-commit Sync**: Automatically keep your `.pre-commit-config.yaml` Ruff hook version matched with your project's Ruff version.
23
24
 
24
25
  ---
25
26
 
@@ -45,7 +46,7 @@ Internal "base" configurations or shared presets often fall out of sync, or requ
45
46
  If your local directory doesn't have a configuration file yet, you can fetch the standard and create one instantly:
46
47
 
47
48
  ```bash
48
- uv run ruff-sync pull https://github.com/my-org/standards --init
49
+ uv run ruff-sync https://github.com/my-org/standards --init
49
50
  ```
50
51
 
51
52
  ### 2. Configure an existing project
@@ -62,7 +63,14 @@ upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"
62
63
  Once configured, simply run:
63
64
 
64
65
  ```bash
65
- uv run ruff-sync pull
66
+ uv run ruff-sync
66
67
  ```
67
68
 
68
69
  This will download the upstream file, extract the `[tool.ruff]` section, and merge it into your local file while **preserving your artisanal comments and formatting**.
70
+
71
+ ---
72
+
73
+ ## 📚 Next Steps
74
+
75
+ - Check out our [Best Practices](best-practices.md) for recommendations on adopting `ruff-sync` across your organization.
76
+ - Read the [Usage](usage.md) guide for advanced examples.
@@ -11,7 +11,7 @@ The easiest way to use `ruff-sync` across all your projects is by installing it
11
11
  ```bash
12
12
  uv tool install ruff-sync
13
13
  # Then simply run:
14
- ruff-sync pull
14
+ ruff-sync
15
15
  ```
16
16
 
17
17
  ### One-off Invocation
@@ -19,7 +19,7 @@ ruff-sync pull
19
19
  If you just want to run `ruff-sync` once without installing it, use `uvx`:
20
20
 
21
21
  ```bash
22
- uvx ruff-sync pull
22
+ uvx ruff-sync
23
23
  ```
24
24
 
25
25
  ### Project-specific Development
@@ -29,7 +29,7 @@ To keep the version consistent across your team and locked to your project, add
29
29
  ```bash
30
30
  uv add --dev ruff-sync
31
31
  # Then run it with:
32
- uv run ruff-sync pull
32
+ uv run ruff-sync
33
33
  ```
34
34
 
35
35
  ---
@@ -58,7 +58,7 @@ Running `ruff-sync check` in pre-commit is fast because:
58
58
  2. It minimizes network overhead by only fetching exactly what it needs (e.g., using direct HTTP requests for single files or partial git cloning for repositories).
59
59
  3. By default, it uses `--semantic` to ignore formatting-only differences, reducing false positives.
60
60
 
61
- For more complex scenarios, such as syncing from multiple upstreams or using directory prefixes, see [Advanced Usage](usage.md#advanced-usage).
61
+ For more complex scenarios, such as syncing from multiple upstreams or using directory prefixes, see [Usage](usage.md).
62
62
 
63
63
  ## Manual Execution
64
64
 
@@ -67,3 +67,26 @@ You can always run the hooks manually using:
67
67
  ```bash
68
68
  pre-commit run ruff-sync-check --all-files
69
69
  ```
70
+
71
+ ## Syncing the Ruff Hook Version
72
+
73
+ In addition to syncing your Ruff configuration rules, `ruff-sync` can also automatically synchronize the version of the `astral-sh/ruff-pre-commit` hook in your `.pre-commit-config.yaml` to match the Ruff version installed in your project (e.g., from `uv.lock` or `pyproject.toml`).
74
+
75
+ To enable this, use the `--pre-commit` flag:
76
+
77
+ ```bash
78
+ ruff-sync --pre-commit
79
+ ```
80
+
81
+ Or enable it permanently in your `pyproject.toml`:
82
+
83
+ ```toml
84
+ [tool.ruff-sync]
85
+ pre-commit-version-sync = true
86
+ ```
87
+
88
+ If you have `pre-commit-version-sync` enabled in your configuration but need to explicitly disable it for a specific run (for example, during a CI step where pre-commit is turned off), you can bypass it using the `--no-pre-commit` flag:
89
+
90
+ ```bash
91
+ ruff-sync check --no-pre-commit
92
+ ```
@@ -16,7 +16,7 @@ If `ruff-sync` is not behaving as expected, you can increase the verbosity of th
16
16
  Example:
17
17
 
18
18
  ```bash
19
- ruff-sync pull -vv
19
+ ruff-sync -vv
20
20
  ```
21
21
 
22
22
  ### Upstream URL not found
@@ -26,14 +26,14 @@ ruff-sync pull -vv
26
26
  **Solution**:
27
27
 
28
28
  1. Ensure you have a `[tool.ruff-sync]` section in your `pyproject.toml` with an `upstream` key. See the [Configuration](configuration.md) guide for details.
29
- 2. Or provide the URL directly as an argument: `ruff-sync pull https://github.com/org/repo/blob/main/pyproject.toml`
29
+ 2. Or provide the URL directly as an argument: `ruff-sync https://github.com/org/repo/blob/main/pyproject.toml`
30
30
 
31
31
  ### Local file not found (with `check`)
32
32
 
33
33
  **Error**: `FileNotFoundError: pyproject.toml not found`
34
34
 
35
35
  **Solution**:
36
- The `check` command expects a local `pyproject.toml`, `ruff.toml`, or `.ruff.toml` to exist. If you are setting up a new project, use `pull --init` first.
36
+ The `check` command expects a local `pyproject.toml`, `ruff.toml`, or `.ruff.toml` to exist. If you are setting up a new project, use `ruff-sync --init` first.
37
37
 
38
38
  ### Merge conflicts in TOML
39
39
 
@@ -43,7 +43,7 @@ The `check` command expects a local `pyproject.toml`, `ruff.toml`, or `.ruff.tom
43
43
  Use the `--exclude` flag to keep your local settings:
44
44
 
45
45
  ```bash
46
- ruff-sync pull --exclude lint.line-length
46
+ ruff-sync --exclude lint.line-length
47
47
  ```
48
48
 
49
49
  ### Multi-upstream Fetch Failures
@@ -65,7 +65,7 @@ If you see an HTTP `403 Forbidden` or `404 Not Found` when trying to fetch from
65
65
  Use the git-clone alternative suggested in the error message:
66
66
 
67
67
  ```bash
68
- ruff-sync pull git@github.com:org/repo.git
68
+ ruff-sync git@github.com:org/repo.git
69
69
  ```
70
70
 
71
71
  This uses your local SSH keys and is often more reliable for internal or private repositories.