ruff-sync 0.1.0.dev1__tar.gz → 0.1.1.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 (85) hide show
  1. ruff_sync-0.1.1.dev1/.agents/skills/release-notes-generation/SKILL.md +82 -0
  2. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.pre-commit-config.yaml +2 -1
  3. ruff_sync-0.1.1.dev1/.pre-commit-hooks.yaml +15 -0
  4. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/AGENTS.md +1 -0
  5. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/PKG-INFO +66 -21
  6. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/README.md +65 -20
  7. ruff_sync-0.1.1.dev1/docs/assets/favicon.png +0 -0
  8. ruff_sync-0.1.1.dev1/docs/assets/logo.png +0 -0
  9. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/ci-integration.md +7 -18
  10. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/configuration.md +16 -1
  11. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/index.md +20 -7
  12. ruff_sync-0.1.1.dev1/docs/pre-commit.md +69 -0
  13. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/troubleshooting.md +39 -0
  14. ruff_sync-0.1.1.dev1/docs/usage.md +145 -0
  15. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/mkdocs.yml +7 -3
  16. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/pyproject.toml +2 -2
  17. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/src/ruff_sync/cli.py +63 -24
  18. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/src/ruff_sync/core.py +146 -43
  19. ruff_sync-0.1.1.dev1/tests/lifecycle_tomls/multi_upstream_final.toml +8 -0
  20. ruff_sync-0.1.1.dev1/tests/lifecycle_tomls/multi_upstream_initial.toml +7 -0
  21. ruff_sync-0.1.1.dev1/tests/lifecycle_tomls/multi_upstream_up1.toml +5 -0
  22. ruff_sync-0.1.1.dev1/tests/lifecycle_tomls/multi_upstream_up2.toml +5 -0
  23. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_basic.py +322 -100
  24. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_check.py +65 -6
  25. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_deprecation.py +1 -1
  26. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_e2e.py +51 -6
  27. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_scaffold.py +6 -6
  28. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/uv.lock +141 -125
  29. ruff_sync-0.1.0.dev1/docs/usage.md +0 -98
  30. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/TESTING.md +0 -0
  31. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
  32. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
  33. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
  34. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
  35. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
  36. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
  37. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.agents/workflows/add-test-case.md +0 -0
  38. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.git-blame-ignore-revs +0 -0
  39. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.github/dependabot.yml +0 -0
  40. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.github/workflows/ci.yaml +0 -0
  41. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.github/workflows/complexity.yaml +0 -0
  42. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.github/workflows/docs.yaml +0 -0
  43. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/.gitignore +0 -0
  44. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/LICENSE.md +0 -0
  45. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/codecov.yml +0 -0
  46. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/configs/fastapi/ruff.toml +0 -0
  47. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/configs/kitchen-sink/ruff.toml +0 -0
  48. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/assets/ruff_sync_banner.png +0 -0
  49. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/gen_ref_pages.py +0 -0
  50. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/docs/installation.md +0 -0
  51. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/scripts/check_dogfood.sh +0 -0
  52. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/scripts/gitclone_dogfood.sh +0 -0
  53. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/scripts/pull_dogfood.sh +0 -0
  54. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/skills-lock.json +0 -0
  55. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/src/ruff_sync/__init__.py +0 -0
  56. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/src/ruff_sync/__main__.py +0 -0
  57. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tasks.py +0 -0
  58. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/__init__.py +0 -0
  59. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/conftest.py +0 -0
  60. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  61. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  62. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  63. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  64. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  65. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  66. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
  67. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  68. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  69. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
  70. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
  71. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
  72. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
  73. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  74. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  75. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/ruff.toml +0 -0
  76. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_config_validation.py +0 -0
  77. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_corner_cases.py +0 -0
  78. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_git_fetch.py +0 -0
  79. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_project.py +0 -0
  80. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_toml_operations.py +0 -0
  81. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_url_handling.py +0 -0
  82. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/test_whitespace.py +0 -0
  83. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  84. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  85. {ruff_sync-0.1.0.dev1 → ruff_sync-0.1.1.dev1}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: release-notes-generation
3
+ description: Draft professional and categorized release notes for ruff-sync using GitHub CLI, git history, and the `invoke release` task.
4
+ ---
5
+
6
+ # Release Notes Generation
7
+
8
+ This skill guides you through drafting high-quality release notes for `ruff-sync`. It leverages the `invoke release` task and GitHub CLI for context.
9
+
10
+ ## Prerequisites
11
+
12
+ - **GitHub CLI (`gh`)**: Must be authenticated.
13
+ - **Invoke**: Dev tasks are defined in `tasks.py`.
14
+ - **Git**: Recent history and tags must be available.
15
+
16
+ ## Workflow
17
+
18
+ ### 1. Gather Context
19
+
20
+ Before drafting, understand what has changed since the last release.
21
+
22
+ ```bash
23
+ # Get the latest release tag and notes
24
+ gh release list --limit 1
25
+ gh release view <tag>
26
+
27
+ # List merged PRs since the last tag
28
+ # Replace <tag> with the tag found above
29
+ gh pr list --state merged --search "merged:><tag-date>"
30
+
31
+ # Or simple git log
32
+ git log <tag>..HEAD --oneline
33
+ ```
34
+
35
+ ### 2. Create Draft Release
36
+
37
+ Use the project's built-in release task to scaffold the release. This task automatically tags the release based on the version in `pyproject.toml` and uses GitHub's `--generate-notes` feature to create a starting point.
38
+
39
+ ```bash
40
+ # Create a draft release (default behavior of invoke release)
41
+ uv run invoke release --draft --skip-tests
42
+ ```
43
+
44
+ > [!NOTE]
45
+ > `invoke release` will:
46
+ > 1. Check if you are on `main`.
47
+ > 2. Check for clean git state.
48
+ > 3. Create a GitHub Release with `--generate-notes`.
49
+
50
+ ### 3. Refine Release Notes
51
+
52
+ GitHub's automatically generated notes are a good start but often lack professional categorization and narrative. Use the following structure for final refinement:
53
+
54
+ #### Categorization
55
+ - **🚀 Features**: New capabilities added to `ruff_sync`.
56
+ - **🐞 Bug Fixes**: Issues resolved in CLI, merging logic, or HTTP handling.
57
+ - **✨ Improvements**: Enhancements to existing features, performance, or logging.
58
+ - **📖 Documentation**: Updates to `README.md`, `docs/`, or docstrings.
59
+ - **🛠️ Maintenance**: Dependency updates, CI changes, or test refactoring.
60
+
61
+ #### Writing Style
62
+ - Use clear, action-oriented language (e.g., "Add support...", "Fix issue where...", "Refactor...").
63
+ - Link to PRs and contributors using their GitHub handles.
64
+ - Include a "Breaking Changes" section if applicable (use `[!WARNING]` alerts).
65
+
66
+ ### 4. Finalize
67
+
68
+ Once the notes are drafted and refined, you can view the draft on GitHub or update it via CLI.
69
+
70
+ ```bash
71
+ # View the draft notes
72
+ gh release view v<version>
73
+
74
+ # Edit the draft (opens your editor)
75
+ gh release edit v<version> --notes "your new notes"
76
+ ```
77
+
78
+ ## Tips
79
+
80
+ - **Consistency**: Refer to the `AGENTS.md` for project-specific terminology (e.g., "Upstream Layers").
81
+ - **Screenshots**: If the release includes significantly visible changes (e.g., new logging or CLI output formats), consider embedding a screenshot or recording in the notes.
82
+ - **Automated Summary**: You can ask the AI assistant to "Draft release notes based on the git log since <tag>" to get a structured summary before applying it to the release.
@@ -7,12 +7,13 @@ repos:
7
7
  - id: check-yaml
8
8
  args: ["--unsafe"]
9
9
  exclude: .agents/skills/mkdocs-generation/templates/mkdocs.yml
10
+ - id: check-toml
10
11
  - id: end-of-file-fixer
11
12
  - id: trailing-whitespace
12
13
  - id: no-commit-to-branch
13
14
  args: [--branch, develop, --branch, main]
14
15
  - repo: https://github.com/astral-sh/ruff-pre-commit
15
- rev: "v0.15.5"
16
+ rev: "v0.15.6"
16
17
  hooks:
17
18
  - id: ruff-check
18
19
  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
@@ -150,6 +150,7 @@ uv run coverage run -m pytest -vv
150
150
  - Use `pathlib` over `os.path` (enforced by `PTH` rules).
151
151
  - Prefer f-strings for logging (we ignore `G004`).
152
152
  - Do not create custom exception classes for simple errors (`TRY003` is ignored).
153
+ - **Prefer `NamedTuple` for return types** over plain tuples to improve readability and type safety.
153
154
 
154
155
  ### TOML Handling
155
156
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ruff-sync
3
- Version: 0.1.0.dev1
3
+ Version: 0.1.1.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,37 +133,64 @@ pip install ruff-sync
132
133
 
133
134
  ### Usage
134
135
 
136
+ **The Basic Sync**
137
+
138
+ ```console
139
+ # Pull rules from a central repository into your current project
140
+ ruff-sync pull https://github.com/my-org/standards
141
+ ```
142
+
143
+ **Persistent Configuration**
144
+
145
+ ```console
146
+ # If configured in pyproject.toml (see Configuration), simply run:
147
+ ruff-sync pull
148
+ ```
149
+
150
+ **Initializing a New Project**
151
+
152
+ ```console
153
+ # Scaffold a new pyproject.toml if your directory is empty
154
+ ruff-sync pull https://github.com/my-org/standards --init
155
+ ```
156
+
157
+ **Syncing Subdirectories or Specific Files**
158
+
135
159
  ```console
136
- # Sync from a GitHub/GitLab repository (root or specific directory)
137
- ruff-sync https://github.com/my-org/standards
138
- ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
160
+ ruff-sync pull https://github.com/my-org/standards/tree/main/configs/shared
161
+ ruff-sync pull https://github.com/my-org/standards/blob/main/pyproject.toml
162
+ ```
139
163
 
140
- # Or a direct blob/file URL (auto-converts to raw)
141
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
164
+ **Using Git (SSH/HTTP)**
142
165
 
143
- # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
144
- # You can use the --branch flag to specify a branch (default: main)
145
- ruff-sync git@github.com:my-org/standards.git
146
- ruff-sync ssh://git@gitlab.com/my-org/standards.git
166
+ ```console
167
+ # Clones efficiently (depth 1, blob:none) to extract the config
168
+ ruff-sync pull git@github.com:my-org/standards.git
169
+ ```
147
170
 
148
- # Or if configured in pyproject.toml (see Configuration), simply run:
149
- ruff-sync
171
+ **Excluding Specific Rules**
150
172
 
173
+ ```console
151
174
  # Exclude specific sections from being overwritten using dotted paths
152
- ruff-sync --exclude lint.per-file-ignores lint.ignore
175
+ ruff-sync pull --exclude lint.ignore
176
+ ```
177
+
178
+ **Checking for Drift (CI)**
153
179
 
154
- # Check if your local config is in sync (useful in CI)
180
+ ```console
181
+ # Verify local config matches upstream. Exits 1 if out of sync.
155
182
  ruff-sync check https://github.com/my-org/standards
156
183
 
157
- # Semantic check — ignore cosmetic differences like comments and whitespace
184
+ # Semantic check — ignores cosmetic differences like comments and whitespace
158
185
  ruff-sync check --semantic
159
186
  ```
160
187
 
161
- Run `ruff-sync --help` for full details on all available options.
188
+ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for more detailed examples and advanced workflows.
162
189
 
163
190
  ## Key Features
164
191
 
165
192
  - 🏗️ **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.
193
+ - 📂 **Upstream Layers** — Merge configurations from several sources sequentially (e.g., base company config + team-specific overrides).
166
194
  - 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs, tree (directory) URLs, or blob (file) URLs to raw content URLs.
167
195
  - 🔍 **Smart configuration discovery** — Point at a directory and `ruff-sync` will automatically find your config. It checks `pyproject.toml`, `ruff.toml`, and `.ruff.toml` (in that order).
168
196
  - 📥 **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.
@@ -200,8 +228,9 @@ Here are all the possible values that can be provided in `[tool.ruff-sync]` alon
200
228
 
201
229
  ```toml
202
230
  [tool.ruff-sync]
203
- # The source of truth URL for your Ruff configuration. (Required, unless passed via CLI)
204
- upstream = "https://github.com/my-org/standards"
231
+ # The source of truth URL(s) for your Ruff configuration. (Required, unless passed via CLI)
232
+ # Accepts a single string URL or a list of URLs.
233
+ upstream = ["https://github.com/my-org/standards", "https://github.com/my-org/team-tweaks"]
205
234
 
206
235
  # A list of config keys to exclude from being synced. (Default: ["lint.per-file-ignores"])
207
236
  # Use simple names for top-level keys, and dotted paths for nested keys.
@@ -221,6 +250,19 @@ path = "config/ruff"
221
250
  to = "."
222
251
  ```
223
252
 
253
+ ## Pre-commit Integration
254
+
255
+ Ensure your configuration is always in sync before every commit. Add this to your `.pre-commit-config.yaml`:
256
+
257
+ ```yaml
258
+ - repo: https://github.com/Kilo59/ruff-sync
259
+ rev: v0.1.0 # Use the latest version
260
+ hooks:
261
+ - id: ruff-sync-check
262
+ ```
263
+
264
+ See the [Pre-commit Guide](https://kilo59.github.io/ruff-sync/pre-commit/) for more details.
265
+
224
266
  ## CI Integration
225
267
 
226
268
  The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
@@ -329,10 +371,13 @@ When you run `ruff-sync check`, it follows this process to determine if your pro
329
371
  ```mermaid
330
372
  flowchart TD
331
373
  Start([Start]) --> Local[Read Local Configuration]
332
- Local --> Upstream[Download Upstream Configuration]
333
- Upstream --> Extract[Extract tool.ruff section if needed]
374
+ Local --> Upstreams{For each Upstream}
375
+ Upstreams --> Download[Download/Clone Configuration]
376
+ Download --> Extract[Extract section if needed]
334
377
  Extract --> Exclude[Apply Exclusions]
335
- Exclude --> Merge[Perform in-memory Merge]
378
+ Exclude --> Merge[Merge into in-memory Doc]
379
+ Merge --> Upstreams
380
+ Upstreams -- Done --> Comparison
336
381
 
337
382
  subgraph Comparison [Comparison Logic]
338
383
  direction TB
@@ -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,37 +102,64 @@ pip install ruff-sync
101
102
 
102
103
  ### Usage
103
104
 
105
+ **The Basic Sync**
106
+
107
+ ```console
108
+ # Pull rules from a central repository into your current project
109
+ ruff-sync pull https://github.com/my-org/standards
110
+ ```
111
+
112
+ **Persistent Configuration**
113
+
114
+ ```console
115
+ # If configured in pyproject.toml (see Configuration), simply run:
116
+ ruff-sync pull
117
+ ```
118
+
119
+ **Initializing a New Project**
120
+
121
+ ```console
122
+ # Scaffold a new pyproject.toml if your directory is empty
123
+ ruff-sync pull https://github.com/my-org/standards --init
124
+ ```
125
+
126
+ **Syncing Subdirectories or Specific Files**
127
+
104
128
  ```console
105
- # Sync from a GitHub/GitLab repository (root or specific directory)
106
- ruff-sync https://github.com/my-org/standards
107
- ruff-sync https://github.com/my-org/standards/tree/main/configs/shared
129
+ ruff-sync pull https://github.com/my-org/standards/tree/main/configs/shared
130
+ ruff-sync pull https://github.com/my-org/standards/blob/main/pyproject.toml
131
+ ```
108
132
 
109
- # Or a direct blob/file URL (auto-converts to raw)
110
- ruff-sync https://github.com/my-org/standards/blob/main/pyproject.toml
133
+ **Using Git (SSH/HTTP)**
111
134
 
112
- # Clone from any git repository (using SSH or HTTP, defaults to --depth 1)
113
- # You can use the --branch flag to specify a branch (default: main)
114
- ruff-sync git@github.com:my-org/standards.git
115
- ruff-sync ssh://git@gitlab.com/my-org/standards.git
135
+ ```console
136
+ # Clones efficiently (depth 1, blob:none) to extract the config
137
+ ruff-sync pull git@github.com:my-org/standards.git
138
+ ```
116
139
 
117
- # Or if configured in pyproject.toml (see Configuration), simply run:
118
- ruff-sync
140
+ **Excluding Specific Rules**
119
141
 
142
+ ```console
120
143
  # Exclude specific sections from being overwritten using dotted paths
121
- ruff-sync --exclude lint.per-file-ignores lint.ignore
144
+ ruff-sync pull --exclude lint.ignore
145
+ ```
146
+
147
+ **Checking for Drift (CI)**
122
148
 
123
- # Check if your local config is in sync (useful in CI)
149
+ ```console
150
+ # Verify local config matches upstream. Exits 1 if out of sync.
124
151
  ruff-sync check https://github.com/my-org/standards
125
152
 
126
- # Semantic check — ignore cosmetic differences like comments and whitespace
153
+ # Semantic check — ignores cosmetic differences like comments and whitespace
127
154
  ruff-sync check --semantic
128
155
  ```
129
156
 
130
- Run `ruff-sync --help` for full details on all available options.
157
+ See the [Usage documentation](https://kilo59.github.io/ruff-sync/usage/) for more detailed examples and advanced workflows.
131
158
 
132
159
  ## Key Features
133
160
 
134
161
  - 🏗️ **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.
162
+ - 📂 **Upstream Layers** — Merge configurations from several sources sequentially (e.g., base company config + team-specific overrides).
135
163
  - 🌐 **GitHub & GitLab URL support** — Automatically converts GitHub/GitLab repository URLs, tree (directory) URLs, or blob (file) URLs to raw content URLs.
136
164
  - 🔍 **Smart configuration discovery** — Point at a directory and `ruff-sync` will automatically find your config. It checks `pyproject.toml`, `ruff.toml`, and `.ruff.toml` (in that order).
137
165
  - 📥 **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.
@@ -169,8 +197,9 @@ Here are all the possible values that can be provided in `[tool.ruff-sync]` alon
169
197
 
170
198
  ```toml
171
199
  [tool.ruff-sync]
172
- # The source of truth URL for your Ruff configuration. (Required, unless passed via CLI)
173
- upstream = "https://github.com/my-org/standards"
200
+ # The source of truth URL(s) for your Ruff configuration. (Required, unless passed via CLI)
201
+ # Accepts a single string URL or a list of URLs.
202
+ upstream = ["https://github.com/my-org/standards", "https://github.com/my-org/team-tweaks"]
174
203
 
175
204
  # A list of config keys to exclude from being synced. (Default: ["lint.per-file-ignores"])
176
205
  # Use simple names for top-level keys, and dotted paths for nested keys.
@@ -190,6 +219,19 @@ path = "config/ruff"
190
219
  to = "."
191
220
  ```
192
221
 
222
+ ## Pre-commit Integration
223
+
224
+ Ensure your configuration is always in sync before every commit. Add this to your `.pre-commit-config.yaml`:
225
+
226
+ ```yaml
227
+ - repo: https://github.com/Kilo59/ruff-sync
228
+ rev: v0.1.0 # Use the latest version
229
+ hooks:
230
+ - id: ruff-sync-check
231
+ ```
232
+
233
+ See the [Pre-commit Guide](https://kilo59.github.io/ruff-sync/pre-commit/) for more details.
234
+
193
235
  ## CI Integration
194
236
 
195
237
  The `check` command is designed for use in CI pipelines. Add it as a step to catch config drift before it merges:
@@ -298,10 +340,13 @@ When you run `ruff-sync check`, it follows this process to determine if your pro
298
340
  ```mermaid
299
341
  flowchart TD
300
342
  Start([Start]) --> Local[Read Local Configuration]
301
- Local --> Upstream[Download Upstream Configuration]
302
- Upstream --> Extract[Extract tool.ruff section if needed]
343
+ Local --> Upstreams{For each Upstream}
344
+ Upstreams --> Download[Download/Clone Configuration]
345
+ Download --> Extract[Extract section if needed]
303
346
  Extract --> Exclude[Apply Exclusions]
304
- Exclude --> Merge[Perform in-memory Merge]
347
+ Exclude --> Merge[Merge into in-memory Doc]
348
+ Merge --> Upstreams
349
+ Upstreams -- Done --> Comparison
305
350
 
306
351
  subgraph Comparison [Comparison Logic]
307
352
  direction TB
@@ -72,26 +72,9 @@ ruff-sync-check:
72
72
 
73
73
  ---
74
74
 
75
- ## 🛠️ Pre-commit Integration
76
-
77
75
  You can use `ruff-sync` with `pre-commit` to ensure your configuration is always in sync before pushing.
78
76
 
79
- Add this to your `.pre-commit-config.yaml`:
80
-
81
- ```yaml
82
- repos:
83
- - repo: local
84
- hooks:
85
- - id: ruff-sync-check
86
- name: ruff-sync-check
87
- entry: uvx ruff-sync check --semantic
88
- language: system
89
- files: ^pyproject\.toml$
90
- pass_filenames: false
91
- ```
92
-
93
- !!! note
94
- Running `ruff-sync check` in pre-commit is fast because it only performs a network request if the local `pyproject.toml` is older than the upstream or if no cache exists.
77
+ See the [Pre-commit Guide](pre-commit.md) for details on using the official hooks.
95
78
 
96
79
  ---
97
80
 
@@ -101,6 +84,12 @@ repos:
101
84
 
102
85
  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
86
 
87
+ ### Handle Exclusions Properly
88
+
89
+ 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`.
90
+
91
+ 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!
92
+
104
93
  ### Use a Dedicated Workflow
105
94
 
106
95
  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.
@@ -6,7 +6,7 @@
6
6
 
7
7
  | Key | Type | Default | Description |
8
8
  | :--- | :--- | :--- | :--- |
9
- | `upstream` | `str` | *Required* | The URL of the upstream `pyproject.toml` or `ruff.toml`. |
9
+ | `upstream` | `str \| list[str]` | *Required* | The URL(s) of the upstream `pyproject.toml` or `ruff.toml`. |
10
10
  | `to` | `str` | `"."` | The local directory or file where configuration should be merged. |
11
11
  | `exclude` | `list[str]` | `["lint.per-file-ignores"]` | A list of configuration keys to preserve locally. |
12
12
  | `branch` | `str` | `"main"` | The default branch to use when resolving repository URLs. |
@@ -58,6 +58,21 @@ If your projects are on different Python versions but share linting rules:
58
58
  exclude = ["target-version"]
59
59
  ```
60
60
 
61
+ #### Sequential merging of multiple sources
62
+
63
+ 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
+
65
+ ```toml
66
+ [tool.ruff-sync]
67
+ upstream = [
68
+ "https://github.com/my-org/shared-config", # 1. Base rules
69
+ "https://github.com/my-org/team-overrides", # 2. Team-specific tweaks (wins)
70
+ ]
71
+ ```
72
+
73
+ !!! tip "Last One Wins"
74
+ 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`.
75
+
61
76
  ## Deprecation Notes
62
77
 
63
78
  - The key `source` in `[tool.ruff-sync]` is deprecated and will be removed in a future version. Use `to` instead.
@@ -13,10 +13,13 @@
13
13
  ## 🚀 Key Features
14
14
 
15
15
  * **⚡ Fast & Lightweight**: Zero-config needed for most projects.
16
- * **✨ Formatting Preserved**: Uses `tomlkit` to keep your comments, indentation, and whitespace exactly as they are.
17
- * **🛡️ Smart Merging**: Safely merges nested tables (like `lint.per-file-ignores`) without overwriting local overrides.
18
- * **🔗 Flexible Sources**: Sync from GitHub, GitLab, raw URLs, or local files.
19
- * **✅ CI Ready**: Built-in `check` command with semantic diffs for automated pipelines.
16
+ * **✨ Formatting Preserved**: Keeps all comments and whitespace via `tomlkit`.
17
+ * **🛡️ Smart Merging**: Safely merges nested tables without overwriting local overrides.
18
+ * **📂 Upstream Layers**: Combine and merge configurations from several sources sequentially.
19
+ * **🌐 Flexible Sources**: Sync from GitHub, GitLab, raw URLs, or local files.
20
+ * **📥 Efficient Git Support**: Shallow clones and sparse checkouts for fast extraction.
21
+ * **🚀 Zero-Config Bootstrapping**: Use `--init` to scaffold a new project in one command.
22
+ * **✅ CI Ready**: Built-in `check` command with semantic comparison logic.
20
23
 
21
24
  ---
22
25
 
@@ -37,16 +40,26 @@ Internal "base" configurations or shared presets often fall out of sync, or requ
37
40
 
38
41
  ## 🏁 Quick Start
39
42
 
40
- ### 1. Configure your project
43
+ ### 1. Initialize a new project (Optional)
41
44
 
42
- Add the upstream URL to your `pyproject.toml`:
45
+ If your local directory doesn't have a configuration file yet, you can fetch the standard and create one instantly:
46
+
47
+ ```bash
48
+ uv run ruff-sync pull https://github.com/my-org/standards --init
49
+ ```
50
+
51
+ ### 2. Configure an existing project
52
+
53
+ Add the upstream URL to your `pyproject.toml` to make it the default:
43
54
 
44
55
  ```toml
45
56
  [tool.ruff-sync]
46
57
  upstream = "https://github.com/my-org/standards/blob/main/pyproject.toml"
47
58
  ```
48
59
 
49
- ### 2. Pull the configuration
60
+ ### 3. Pull the configuration
61
+
62
+ Once configured, simply run:
50
63
 
51
64
  ```bash
52
65
  uv run ruff-sync pull
@@ -0,0 +1,69 @@
1
+ # Pre-commit Integration
2
+
3
+ Using `ruff-sync` with [pre-commit](https://pre-commit.com/) ensures that your Ruff configuration stays in sync with your organization's standards automatically.
4
+
5
+ ## Official Hooks
6
+
7
+ `ruff-sync` provides two official hooks:
8
+
9
+ ### `ruff-sync-check`
10
+
11
+ Verifies that your local `pyproject.toml` or `ruff.toml` matches the upstream configuration. It is recommended to use this hook to prevent accidental drift.
12
+
13
+ ```yaml
14
+ - repo: https://github.com/Kilo59/ruff-sync
15
+ rev: v0.1.0 # Use the latest version
16
+ hooks:
17
+ - id: ruff-sync-check
18
+ ```
19
+
20
+ ### `ruff-sync-pull`
21
+
22
+ Automatically pulls and applies the upstream configuration if a drift is detected.
23
+
24
+ ```yaml
25
+ - repo: https://github.com/Kilo59/ruff-sync
26
+ rev: v0.1.0 # Use the latest version
27
+ hooks:
28
+ - id: ruff-sync-pull
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ The hooks will automatically respect the configuration defined in your `pyproject.toml` under `[tool.ruff-sync]`. Any arguments passed via `args` in `.pre-commit-config.yaml` will override these settings.
34
+
35
+ > [!NOTE]
36
+ > For a full list of configuration options, see the [Configuration Guide](configuration.md).
37
+
38
+ ### Example `.pre-commit-config.yaml`
39
+
40
+ ```yaml
41
+ repos:
42
+ - repo: https://github.com/Kilo59/ruff-sync
43
+ rev: v0.1.0
44
+ hooks:
45
+ - id: ruff-sync-check
46
+ # Common arguments:
47
+ # --semantic: ignore cosmetic changes (default in check hook)
48
+ # --no-diff: hide the unified diff
49
+ # --to PATH: sync to a specific file or directory
50
+ args: ["--semantic", "--no-diff"]
51
+ ```
52
+
53
+ ## Why use `ruff-sync-check`?
54
+
55
+ Running `ruff-sync check` in pre-commit is fast because:
56
+
57
+ 1. It only checks the `[tool.ruff]` section of the configuration.
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
+ 3. By default, it uses `--semantic` to ignore formatting-only differences, reducing false positives.
60
+
61
+ For more complex scenarios, such as syncing from multiple upstreams or using directory prefixes, see [Advanced Usage](usage.md#advanced-usage).
62
+
63
+ ## Manual Execution
64
+
65
+ You can always run the hooks manually using:
66
+
67
+ ```bash
68
+ pre-commit run ruff-sync-check --all-files
69
+ ```
@@ -4,6 +4,21 @@ Common issues and questions when using `ruff-sync`.
4
4
 
5
5
  ## Common Issues
6
6
 
7
+ ### Debug Logging
8
+
9
+ If `ruff-sync` is not behaving as expected, you can increase the verbosity of the logs to see what's happening under the hood.
10
+
11
+ **Usage**:
12
+
13
+ - `-v`: Shows `INFO` level logs (e.g., which configuration file is being used, where upstreams are being sourced from).
14
+ - `-vv`: Shows `DEBUG` level logs (e.g., detailed TOML merging operations and raw HTTP/Git requests).
15
+
16
+ Example:
17
+
18
+ ```bash
19
+ ruff-sync pull -vv
20
+ ```
21
+
7
22
  ### Upstream URL not found
8
23
 
9
24
  **Error**: `Error: Upstream Required. No upstream URL found in pyproject.toml or provided as argument.`
@@ -31,6 +46,30 @@ Use the `--exclude` flag to keep your local settings:
31
46
  ruff-sync pull --exclude lint.line-length
32
47
  ```
33
48
 
49
+ ### Multi-upstream Fetch Failures
50
+
51
+ **Error**: `❌ <N> upstream fetches failed`
52
+
53
+ This happens when one or more of the specified upstream URLs cannot be reached, do not exist, or return an error (e.g., 404 or 403). `ruff-sync` fetches all upstreams concurrently for speed, but requires ALL of them to succeed before it will attempt to merge.
54
+
55
+ **Solution**:
56
+ 1. Check each URL in the terminal output to see which specific one failed.
57
+ 2. Verify you have network access and the correct permissions for each source.
58
+ 3. If an HTTP source is blocked or private, consider using a Git SSH URL instead.
59
+
60
+ ### Git SSH Workaround for Fetch Errors
61
+
62
+ If you see an HTTP `403 Forbidden` or `404 Not Found` when trying to fetch from GitHub or GitLab, it might be due to authentication requirements.
63
+
64
+ **Solution**:
65
+ Use the git-clone alternative suggested in the error message:
66
+
67
+ ```bash
68
+ ruff-sync pull git@github.com:org/repo.git
69
+ ```
70
+
71
+ This uses your local SSH keys and is often more reliable for internal or private repositories.
72
+
34
73
  ## FAQ
35
74
 
36
75
  ### Does it support `ruff.toml`?