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.
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.pre-commit-config.yaml +3 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/AGENTS.md +1 -1
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/PKG-INFO +33 -14
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/README.md +32 -13
- ruff_sync-0.1.2.dev1/configs/data-science-engineering/ruff.toml +25 -0
- ruff_sync-0.1.2.dev1/docs/best-practices.md +79 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/ci-integration.md +4 -1
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/configuration.md +4 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/index.md +10 -2
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/installation.md +3 -3
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/pre-commit.md +24 -1
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/troubleshooting.md +5 -5
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/usage.md +74 -12
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/mkdocs.yml +1 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/pyproject.toml +4 -1
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/cli.py +24 -4
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/core.py +57 -21
- ruff_sync-0.1.2.dev1/src/ruff_sync/pre_commit.py +149 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_check.py +47 -0
- ruff_sync-0.1.2.dev1/tests/test_pre_commit.py +117 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/uv.lock +1 -1
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/TESTING.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/skills/release-notes-generation/SKILL.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.agents/workflows/add-test-case.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.git-blame-ignore-revs +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/dependabot.yml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/ci.yaml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/complexity.yaml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.github/workflows/docs.yaml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.gitignore +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/.pre-commit-hooks.yaml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/LICENSE.md +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/codecov.yml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/configs/fastapi/ruff.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/configs/kitchen-sink/ruff.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/favicon.png +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/logo.png +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/assets/ruff_sync_banner.png +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/docs/gen_ref_pages.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/check_dogfood.sh +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/gitclone_dogfood.sh +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/scripts/pull_dogfood.sh +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/skills-lock.json +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__init__.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__main__.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tasks.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/__init__.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/conftest.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/ruff.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_basic.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_config_validation.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_corner_cases.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_deprecation.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_e2e.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_git_fetch.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_project.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_scaffold.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_toml_operations.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_url_handling.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/test_whitespace.py +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.1.dev1 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
- {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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
161
|
-
ruff-sync
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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 -->
|
|
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
|
|
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
|
|
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
|
|
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
|
|
130
|
-
ruff-sync
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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 -->
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 [
|
|
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
|
|
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
|
|
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 `
|
|
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
|
|
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
|
|
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.
|