ruff-sync 0.0.3.dev1__tar.gz → 0.0.4.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 (53) hide show
  1. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.agents/TESTING.md +16 -0
  2. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.github/workflows/ci.yaml +26 -1
  3. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/PKG-INFO +47 -43
  4. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/README.md +46 -42
  5. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/pyproject.toml +1 -1
  6. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/ruff_sync.py +268 -39
  7. ruff_sync-0.0.4.dev1/scripts/gitclone_dogfood.sh +46 -0
  8. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_basic.py +2 -2
  9. ruff_sync-0.0.4.dev1/tests/test_config_validation.py +91 -0
  10. ruff_sync-0.0.4.dev1/tests/test_git_fetch.py +155 -0
  11. ruff_sync-0.0.4.dev1/tests/test_scaffold.py +257 -0
  12. ruff_sync-0.0.4.dev1/tests/test_url_parsing.py +31 -0
  13. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/uv.lock +1 -1
  14. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.agents/workflows/add-test-case.md +0 -0
  15. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.git-blame-ignore-revs +0 -0
  16. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.github/dependabot.yml +0 -0
  17. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.github/workflows/complexity.yaml +0 -0
  18. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.gitignore +0 -0
  19. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/.pre-commit-config.yaml +0 -0
  20. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/AGENTS.md +0 -0
  21. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/LICENSE.md +0 -0
  22. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/codecov.yml +0 -0
  23. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/ruff_sync_banner.png +0 -0
  24. /ruff_sync-0.0.3.dev1/scripts/dogfood_check.sh → /ruff_sync-0.0.4.dev1/scripts/check_dogfood.sh +0 -0
  25. /ruff_sync-0.0.3.dev1/scripts/dogfood.sh → /ruff_sync-0.0.4.dev1/scripts/pull_dogfood.sh +0 -0
  26. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tasks.py +0 -0
  27. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/__init__.py +0 -0
  28. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  29. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  30. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  31. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  32. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  33. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  34. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
  35. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  36. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  37. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
  38. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
  39. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
  40. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
  41. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  42. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  43. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/ruff.toml +0 -0
  44. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_check.py +0 -0
  45. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_corner_cases.py +0 -0
  46. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_e2e.py +0 -0
  47. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_project.py +0 -0
  48. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_toml_operations.py +0 -0
  49. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_url_handling.py +0 -0
  50. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/test_whitespace.py +0 -0
  51. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  52. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  53. {ruff_sync-0.0.3.dev1 → ruff_sync-0.0.4.dev1}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -48,6 +48,22 @@ def test_merge_scenarios(source, upstream, expected_keys):
48
48
  # ... test logic ...
49
49
  ```
50
50
 
51
+ ### 3.3 No Autouse Fixtures
52
+ `autouse=True` fixtures are **never allowed**. They hide setup logic and can cause non-obvious side effects or dependencies between tests. All fixtures used by a test must be explicitly requested in the test function's arguments.
53
+
54
+ ### 3.4 Main Entry Point
55
+ Every test file **must** end with a main entry point block. This ensures each file is independently executable as a script (`python tests/test_foo.py`).
56
+
57
+ ```python
58
+ if __name__ == "__main__":
59
+ pytest.main([__file__, "-vv"])
60
+ ```
61
+
62
+ **Why this matters:**
63
+ 1. **Direct Execution**: Developers can run a single test file using standard Python without needing to remember complex `pytest` filter flags.
64
+ 2. **IDE Workflow Integration**: Many IDEs (like VS Code or PyCharm) allow you to run the "Current File" with a single click or keyboard shortcut. Having a main block ensures this works out of the box with the correct verbosity and scope.
65
+ 3. **Cleaner Diffs**: By terminating the file with this standard block, it prevents "no newline at end of file" warnings and ensures that new tests added above it produce clean, isolated diff segments. It also ensures that when debugging with `--icdiff` or similar tools, the output is scoped correctly to the specific file.
66
+
51
67
  ## 4. Handling TOML and `tomlkit`
52
68
 
53
69
  `tomlkit` is central to this project but its dynamic type system can be tricky for mypy.
@@ -64,10 +64,35 @@ jobs:
64
64
  token: ${{ secrets.CODECOV_TOKEN }}
65
65
  slug: Kilo59/ruff-sync
66
66
 
67
+ pre-publish:
68
+ name: Test package installation
69
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
70
+ needs: [static-analysis, tests]
71
+ runs-on: ubuntu-latest
72
+ steps:
73
+ - name: Checkout
74
+ uses: actions/checkout@v4
75
+
76
+ - name: Install uv
77
+ uses: astral-sh/setup-uv@v5
78
+
79
+ - name: Set up Python
80
+ run: uv python install 3.10
81
+
82
+ - name: Build package
83
+ run: uv build
84
+
85
+ - name: Install and test packaged program
86
+ run: |
87
+ uv tool install $(ls dist/*.whl)
88
+ ruff-sync --version
89
+ ruff-sync https://github.com/Kilo59/ruff-sync
90
+ ruff-sync check https://github.com/Kilo59/ruff-sync
91
+
67
92
  publish:
68
93
  name: Build and publish to PyPI
69
94
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
70
- needs: [static-analysis, tests]
95
+ needs: [pre-publish]
71
96
  runs-on: ubuntu-latest
72
97
  permissions:
73
98
  # This permission is required for trusted publishing
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ruff-sync
3
- Version: 0.0.3.dev1
3
+ Version: 0.0.4.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://github.com/Kilo59/ruff-sync#readme
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  **Keep your Ruff config consistent across multiple projects.**
44
44
 
45
- `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/ruff/) configuration from an upstream `pyproject.toml` (hosted anywhere — GitHub, GitLab, or any raw URL) and merges it into your local project, preserving your comments, formatting, and project-specific overrides.
45
+ `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/ruff/) configuration from an upstream `pyproject.toml` or `ruff.toml` (hosted anywhere — GitHub, GitLab, or any raw URL) and merges it into your local project, preserving your comments, formatting, and project-specific overrides.
46
46
 
47
47
  ---
48
48
 
@@ -141,21 +141,20 @@ uv tool install git+https://github.com/Kilo59/ruff-sync
141
141
  ### Usage
142
142
 
143
143
  ```console
144
- # Sync from a GitHub repository (defaults to main/pyproject.toml)
144
+ # Sync from a GitHub/GitLab repository (defaults to main/pyproject.toml)
145
145
  ruff-sync https://github.com/my-org/standards
146
146
 
147
147
  # Or a direct blob/file URL (auto-converts to raw)
148
148
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
149
149
 
150
- # GitLab support (including nested projects)
151
- ruff-sync https://gitlab.com/my-org/my-group/nested/standards
150
+ # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
151
+ # You can use the --branch flag to specify a branch (default: main)
152
+ ruff-sync git@github.com:my-org/standards.git
153
+ ruff-sync ssh://git@gitlab.com/my-org/standards.git
152
154
 
153
- # Once configured in pyproject.toml (see Configuration), simply run:
155
+ # Or if configured in pyproject.toml (see Configuration), simply run:
154
156
  ruff-sync
155
157
 
156
- # Sync into a specific project directory
157
- ruff-sync --source ./my-project
158
-
159
158
  # Exclude specific sections from being overwritten using dotted paths
160
159
  ruff-sync --exclude lint.per-file-ignores lint.ignore
161
160
 
@@ -170,12 +169,13 @@ Run `ruff-sync --help` for full details on all available options.
170
169
 
171
170
  ## Key Features
172
171
 
173
- - **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
174
- - **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
175
- - **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
176
- - **Works with any host** — GitHub, GitLab, Bitbucket, or any raw URL that serves a `pyproject.toml`.
177
- - **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))
178
- - **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
172
+ - 🏗️ **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
173
+ - 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
174
+ - 📥 **Git clone support** — If the URL starts with `git@` or uses the `ssh://`, `git://`, or `git+ssh://` schemes, `ruff-sync` will perform an efficient shallow clone (using `--filter=blob:none` and `--no-checkout`) to safely extract the configuration with minimal network traffic.
175
+ - 🛡️ **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
176
+ - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
177
+ - 🤖 **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))
178
+ - 🧠 **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
179
179
 
180
180
  ## Configuration
181
181
 
@@ -202,16 +202,25 @@ This sets the default upstream and exclusions so you don't need to pass them on
202
202
 
203
203
  ### Advanced Configuration
204
204
 
205
- For more complex setups, you can also configure the default branch and parent directory used when resolving repository URLs (e.g. `https://github.com/my-org/standards`):
205
+ Here are all the possible values that can be provided in `[tool.ruff-sync]` along with their explanations and defaults:
206
206
 
207
207
  ```toml
208
208
  [tool.ruff-sync]
209
+ # The source of truth URL for your Ruff configuration. (Required, unless passed via CLI)
209
210
  upstream = "https://github.com/my-org/standards"
210
211
 
211
- # Use a specific branch or tag (default: "main")
212
+ # A list of config keys to exclude from being synced. (Default: ["lint.per-file-ignores"])
213
+ # Use simple names for top-level keys, and dotted paths for nested keys.
214
+ exclude = [
215
+ "target-version",
216
+ "lint.per-file-ignores",
217
+ ]
218
+
219
+ # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
212
220
  branch = "develop"
213
221
 
214
- # Specify a parent directory if pyproject.toml is not at the repo root
222
+ # A directory prefix to use when looking for a configuration file in a repository. (Default: "")
223
+ # Useful if the upstream pyproject.toml is not at the repository root.
215
224
  path = "config/ruff"
216
225
  ```
217
226
 
@@ -259,6 +268,18 @@ git diff pyproject.toml # review the changes
259
268
  git commit -am "sync ruff config from upstream"
260
269
  ```
261
270
 
271
+ ## Bootstrapping a New Project
272
+
273
+ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml` or `ruff.toml`) to merge into. If you are starting a fresh project and want to initialize it with your organization's Ruff settings, you can use the `--init` flag to scaffold a new file automatically.
274
+
275
+ ```console
276
+ # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
277
+ ruff-sync pull https://github.com/my-org/standards --init
278
+ ```
279
+
280
+ `ruff-sync` seamlessly supports both `pyproject.toml` and standalone `ruff.toml` (or `.ruff.toml`) files. 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.
281
+
282
+
262
283
  ## Detailed Check Logic
263
284
 
264
285
  When you run `ruff-sync check`, it follows this process to determine if your project has drifted from the upstream source:
@@ -297,40 +318,23 @@ flowchart TD
297
318
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
298
319
  ```
299
320
 
300
- ## Contributing
301
-
302
- This project uses:
303
-
304
- - [uv](https://docs.astral.sh/uv/) for dependency management
305
- - [Ruff](https://docs.astral.sh/ruff/) for linting and formatting
306
- - [mypy](https://mypy-lang.org/) for type checking (strict mode)
307
- - [pytest](https://docs.pytest.org/) for testing
308
-
309
- ```console
310
- # Setup
311
- uv sync --group dev
312
-
313
- # Run checks
314
- uv run ruff check . --fix # lint
315
- uv run ruff format . # format
316
- uv run mypy . # type check
317
- uv run pytest -vv # test
318
- ```
319
-
320
321
  ## Dogfooding
321
322
 
322
- To see `ruff-sync` in action, you can "dogfood" it on this project's own config.
323
+ To see `ruff-sync` in action, you can ["dogfood" it on this project's own config](./scripts).
323
324
 
324
- **Check if this project is in sync with its upstream:**
325
+ [**Check if this project is in sync with its upstream:**](./scripts/check_dogfood.sh)
325
326
 
326
327
  ```console
327
- ./scripts/dogfood_check.sh
328
+ ./scripts/check_dogfood.sh
328
329
  ```
329
330
 
330
- **Or sync from a large upstream like Pydantic's config:**
331
+ [**Or sync from a large upstream like Pydantic's config:**](./scripts/pull_dogfood.sh)
331
332
 
332
333
  ```console
333
- ./scripts/dogfood.sh
334
+ # Using a HTTP URL
335
+ ./scripts/pull_dogfood.sh
336
+ # Using a Git URL
337
+ ./scripts/gitclone_dogfood.sh
334
338
  ```
335
339
 
336
340
  This will download Pydantic's Ruff configuration and merge it into the local `pyproject.toml`. You can then use `git diff` to see how it merged the keys while preserving the existing structure and comments.
@@ -12,7 +12,7 @@
12
12
 
13
13
  **Keep your Ruff config consistent across multiple projects.**
14
14
 
15
- `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/ruff/) configuration from an upstream `pyproject.toml` (hosted anywhere — GitHub, GitLab, or any raw URL) and merges it into your local project, preserving your comments, formatting, and project-specific overrides.
15
+ `ruff-sync` is a CLI tool that pulls a canonical [Ruff](https://docs.astral.sh/ruff/) configuration from an upstream `pyproject.toml` or `ruff.toml` (hosted anywhere — GitHub, GitLab, or any raw URL) and merges it into your local project, preserving your comments, formatting, and project-specific overrides.
16
16
 
17
17
  ---
18
18
 
@@ -111,21 +111,20 @@ uv tool install git+https://github.com/Kilo59/ruff-sync
111
111
  ### Usage
112
112
 
113
113
  ```console
114
- # Sync from a GitHub repository (defaults to main/pyproject.toml)
114
+ # Sync from a GitHub/GitLab repository (defaults to main/pyproject.toml)
115
115
  ruff-sync https://github.com/my-org/standards
116
116
 
117
117
  # Or a direct blob/file URL (auto-converts to raw)
118
118
  ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
119
119
 
120
- # GitLab support (including nested projects)
121
- ruff-sync https://gitlab.com/my-org/my-group/nested/standards
120
+ # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
121
+ # You can use the --branch flag to specify a branch (default: main)
122
+ ruff-sync git@github.com:my-org/standards.git
123
+ ruff-sync ssh://git@gitlab.com/my-org/standards.git
122
124
 
123
- # Once configured in pyproject.toml (see Configuration), simply run:
125
+ # Or if configured in pyproject.toml (see Configuration), simply run:
124
126
  ruff-sync
125
127
 
126
- # Sync into a specific project directory
127
- ruff-sync --source ./my-project
128
-
129
128
  # Exclude specific sections from being overwritten using dotted paths
130
129
  ruff-sync --exclude lint.per-file-ignores lint.ignore
131
130
 
@@ -140,12 +139,13 @@ Run `ruff-sync --help` for full details on all available options.
140
139
 
141
140
  ## Key Features
142
141
 
143
- - **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
144
- - **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
145
- - **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
146
- - **Works with any host** — GitHub, GitLab, Bitbucket, or any raw URL that serves a `pyproject.toml`.
147
- - **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))
148
- - **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
142
+ - 🏗️ **Format-preserving merges** — Uses [tomlkit](https://github.com/sdispater/tomlkit) under the hood, so your comments, whitespace, and TOML structure are preserved. No reformatting surprises.
143
+ - 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs or blob URLs to raw content URLs.
144
+ - 📥 **Git clone support** — If the URL starts with `git@` or uses the `ssh://`, `git://`, or `git+ssh://` schemes, `ruff-sync` will perform an efficient shallow clone (using `--filter=blob:none` and `--no-checkout`) to safely extract the configuration with minimal network traffic.
145
+ - 🛡️ **Selective exclusions** — Keep project-specific overrides (like `per-file-ignores` or `target-version`) from being clobbered by the upstream config.
146
+ - 🌍 **Works with any host** — GitHub, GitLab, Bitbucket, private SSH servers, or any raw URL that serves a `pyproject.toml` or `ruff.toml`.
147
+ - 🤖 **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))
148
+ - 🧠 **Semantic mode** — Use `--semantic` to ignore cosmetic differences (comments, whitespace) and only fail on real value changes.
149
149
 
150
150
  ## Configuration
151
151
 
@@ -172,16 +172,25 @@ This sets the default upstream and exclusions so you don't need to pass them on
172
172
 
173
173
  ### Advanced Configuration
174
174
 
175
- For more complex setups, you can also configure the default branch and parent directory used when resolving repository URLs (e.g. `https://github.com/my-org/standards`):
175
+ Here are all the possible values that can be provided in `[tool.ruff-sync]` along with their explanations and defaults:
176
176
 
177
177
  ```toml
178
178
  [tool.ruff-sync]
179
+ # The source of truth URL for your Ruff configuration. (Required, unless passed via CLI)
179
180
  upstream = "https://github.com/my-org/standards"
180
181
 
181
- # Use a specific branch or tag (default: "main")
182
+ # A list of config keys to exclude from being synced. (Default: ["lint.per-file-ignores"])
183
+ # Use simple names for top-level keys, and dotted paths for nested keys.
184
+ exclude = [
185
+ "target-version",
186
+ "lint.per-file-ignores",
187
+ ]
188
+
189
+ # The branch, tag, or commit hash to use when resolving a Git repository URL. (Default: "main")
182
190
  branch = "develop"
183
191
 
184
- # Specify a parent directory if pyproject.toml is not at the repo root
192
+ # A directory prefix to use when looking for a configuration file in a repository. (Default: "")
193
+ # Useful if the upstream pyproject.toml is not at the repository root.
185
194
  path = "config/ruff"
186
195
  ```
187
196
 
@@ -229,6 +238,18 @@ git diff pyproject.toml # review the changes
229
238
  git commit -am "sync ruff config from upstream"
230
239
  ```
231
240
 
241
+ ## Bootstrapping a New Project
242
+
243
+ By default, `ruff-sync` requires an existing configuration file (`pyproject.toml` or `ruff.toml`) to merge into. If you are starting a fresh project and want to initialize it with your organization's Ruff settings, you can use the `--init` flag to scaffold a new file automatically.
244
+
245
+ ```console
246
+ # Create a new pyproject.toml (or ruff.toml) pre-configured with upstream settings
247
+ ruff-sync pull https://github.com/my-org/standards --init
248
+ ```
249
+
250
+ `ruff-sync` seamlessly supports both `pyproject.toml` and standalone `ruff.toml` (or `.ruff.toml`) files. 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.
251
+
252
+
232
253
  ## Detailed Check Logic
233
254
 
234
255
  When you run `ruff-sync check`, it follows this process to determine if your project has drifted from the upstream source:
@@ -267,40 +288,23 @@ flowchart TD
267
288
  style SemanticNode fill:#f4f4f4,color:#363636,stroke:#dbdbdb
268
289
  ```
269
290
 
270
- ## Contributing
271
-
272
- This project uses:
273
-
274
- - [uv](https://docs.astral.sh/uv/) for dependency management
275
- - [Ruff](https://docs.astral.sh/ruff/) for linting and formatting
276
- - [mypy](https://mypy-lang.org/) for type checking (strict mode)
277
- - [pytest](https://docs.pytest.org/) for testing
278
-
279
- ```console
280
- # Setup
281
- uv sync --group dev
282
-
283
- # Run checks
284
- uv run ruff check . --fix # lint
285
- uv run ruff format . # format
286
- uv run mypy . # type check
287
- uv run pytest -vv # test
288
- ```
289
-
290
291
  ## Dogfooding
291
292
 
292
- To see `ruff-sync` in action, you can "dogfood" it on this project's own config.
293
+ To see `ruff-sync` in action, you can ["dogfood" it on this project's own config](./scripts).
293
294
 
294
- **Check if this project is in sync with its upstream:**
295
+ [**Check if this project is in sync with its upstream:**](./scripts/check_dogfood.sh)
295
296
 
296
297
  ```console
297
- ./scripts/dogfood_check.sh
298
+ ./scripts/check_dogfood.sh
298
299
  ```
299
300
 
300
- **Or sync from a large upstream like Pydantic's config:**
301
+ [**Or sync from a large upstream like Pydantic's config:**](./scripts/pull_dogfood.sh)
301
302
 
302
303
  ```console
303
- ./scripts/dogfood.sh
304
+ # Using a HTTP URL
305
+ ./scripts/pull_dogfood.sh
306
+ # Using a Git URL
307
+ ./scripts/gitclone_dogfood.sh
304
308
  ```
305
309
 
306
310
  This will download Pydantic's Ruff configuration and merge it into the local `pyproject.toml`. You can then use `git diff` to see how it merged the keys while preserving the existing structure and comments.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ruff-sync"
3
- version = "0.0.3.dev1"
3
+ version = "0.0.4.dev1"
4
4
  description = "Synchronize Ruff linter configuration across projects"
5
5
  keywords = ["ruff", "linter", "config", "synchronize", "python", "linting", "automation", "tomlkit"]
6
6
  authors = [