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.
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.pre-commit-config.yaml +5 -1
- ruff_sync-0.1.2.dev1/.pre-commit-hooks.yaml +15 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/AGENTS.md +1 -1
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/PKG-INFO +77 -18
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/README.md +76 -17
- ruff_sync-0.1.2.dev1/configs/data-science-engineering/ruff.toml +25 -0
- ruff_sync-0.1.2.dev1/docs/assets/favicon.png +0 -0
- ruff_sync-0.1.2.dev1/docs/assets/logo.png +0 -0
- ruff_sync-0.1.2.dev1/docs/best-practices.md +79 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/ci-integration.md +11 -19
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/configuration.md +10 -3
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/index.md +28 -8
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/installation.md +3 -3
- ruff_sync-0.1.2.dev1/docs/pre-commit.md +92 -0
- ruff_sync-0.1.2.dev1/docs/troubleshooting.md +85 -0
- ruff_sync-0.1.2.dev1/docs/usage.md +207 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/mkdocs.yml +8 -3
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/pyproject.toml +5 -2
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/cli.py +38 -8
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/core.py +156 -26
- ruff_sync-0.1.2.dev1/src/ruff_sync/pre_commit.py +149 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_basic.py +169 -1
- {ruff_sync-0.1.0.dev2 → 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.0.dev2 → ruff_sync-0.1.2.dev1}/uv.lock +141 -125
- ruff_sync-0.1.0.dev2/docs/troubleshooting.md +0 -46
- ruff_sync-0.1.0.dev2/docs/usage.md +0 -100
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/TESTING.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/skills/release-notes-generation/SKILL.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.agents/workflows/add-test-case.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.git-blame-ignore-revs +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/dependabot.yml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/ci.yaml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/complexity.yaml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.github/workflows/docs.yaml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/.gitignore +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/LICENSE.md +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/codecov.yml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/configs/fastapi/ruff.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/configs/kitchen-sink/ruff.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/assets/ruff_sync_banner.png +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/docs/gen_ref_pages.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/check_dogfood.sh +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/gitclone_dogfood.sh +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/scripts/pull_dogfood.sh +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/skills-lock.json +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__init__.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/src/ruff_sync/__main__.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tasks.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/__init__.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/conftest.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/ruff.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_config_validation.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_corner_cases.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_deprecation.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_e2e.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_git_fetch.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_project.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_scaffold.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_toml_operations.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_url_handling.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/test_whitespace.py +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.0.dev2 → ruff_sync-0.1.2.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
- {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.
|
|
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.
|
|
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
|
-
#
|
|
139
|
+
# Pull rules from a central repository into your current project
|
|
137
140
|
ruff-sync https://github.com/my-org/standards
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
169
|
+
```
|
|
147
170
|
|
|
148
|
-
|
|
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.
|
|
175
|
+
ruff-sync --exclude lint.ignore
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Checking for Drift (CI)**
|
|
153
179
|
|
|
154
|
-
|
|
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 —
|
|
185
|
+
# Semantic check — ignores cosmetic differences like comments and whitespace
|
|
158
186
|
ruff-sync check --semantic
|
|
159
187
|
```
|
|
160
188
|
|
|
161
|
-
|
|
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
|
-
#
|
|
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
|
|
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 -->
|
|
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
|
-
#
|
|
108
|
+
# Pull rules from a central repository into your current project
|
|
106
109
|
ruff-sync https://github.com/my-org/standards
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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
|
-
|
|
138
|
+
```
|
|
116
139
|
|
|
117
|
-
|
|
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.
|
|
144
|
+
ruff-sync --exclude lint.ignore
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Checking for Drift (CI)**
|
|
122
148
|
|
|
123
|
-
|
|
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 —
|
|
154
|
+
# Semantic check — ignores cosmetic differences like comments and whitespace
|
|
127
155
|
ruff-sync check --semantic
|
|
128
156
|
```
|
|
129
157
|
|
|
130
|
-
|
|
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
|
-
#
|
|
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
|
|
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 -->
|
|
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
|
+
]
|
|
Binary file
|
|
Binary file
|
|
@@ -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:
|
|
@@ -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
|
-
|
|
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
|
|
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.
|