path-sync 0.4.1__tar.gz → 0.6.0__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 (29) hide show
  1. {path_sync-0.4.1 → path_sync-0.6.0}/PKG-INFO +172 -4
  2. {path_sync-0.4.1 → path_sync-0.6.0}/README.md +171 -3
  3. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/__init__.py +5 -3
  4. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/cmd_copy.py +94 -89
  5. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/cmd_dep_update.py +82 -97
  6. path_sync-0.6.0/path_sync/_internal/cmd_options.py +22 -0
  7. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/git_ops.py +9 -0
  8. path_sync-0.6.0/path_sync/_internal/log_capture.py +38 -0
  9. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/models.py +41 -4
  10. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/models_dep.py +8 -26
  11. path_sync-0.6.0/path_sync/_internal/prompt_utils.py +25 -0
  12. path_sync-0.6.0/path_sync/_internal/verify.py +97 -0
  13. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/copy.py +2 -0
  14. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/dep_update.py +7 -7
  15. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/sections.py +19 -1
  16. path_sync-0.6.0/path_sync/validate_no_changes.py +4 -0
  17. {path_sync-0.4.1 → path_sync-0.6.0}/pyproject.toml +1 -1
  18. {path_sync-0.4.1 → path_sync-0.6.0}/.gitignore +0 -0
  19. {path_sync-0.4.1 → path_sync-0.6.0}/LICENSE +0 -0
  20. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/__main__.py +0 -0
  21. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/__init__.py +0 -0
  22. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/cmd_boot.py +0 -0
  23. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/cmd_validate.py +0 -0
  24. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/file_utils.py +0 -0
  25. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/header.py +0 -0
  26. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/typer_app.py +0 -0
  27. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/validation.py +0 -0
  28. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/_internal/yaml_utils.py +0 -0
  29. {path_sync-0.4.1 → path_sync-0.6.0}/path_sync/config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: path-sync
3
- Version: 0.4.1
3
+ Version: 0.6.0
4
4
  Summary: Sync files from a source repo to multiple destination repos
5
5
  Author-email: EspenAlbert <espen.albert1@gmail.com>
6
6
  License-Expression: MIT
@@ -77,13 +77,14 @@ By default, prompts before each git operation. See [Usage Scenarios](#usage-scen
77
77
  | `-d dest1,dest2` | Filter specific destinations |
78
78
  | `--dry-run` | Preview without writing (requires existing repos) |
79
79
  | `-y, --no-prompt` | Skip confirmations (for CI) |
80
- | `--local` | No git ops after sync (no commit/push/PR) |
80
+ | `--skip-commit` | No git ops after sync (no commit/push/PR). Alias: `--local` |
81
81
  | `--no-checkout` | Skip branch switching (assumes already on correct branch) |
82
82
  | `--checkout-from-default` | Reset to origin/default before sync |
83
83
  | `--no-pr` | Push but skip PR creation |
84
84
  | `--force-overwrite` | Overwrite files even if header removed (opted out) |
85
85
  | `--detailed-exit-code` | Exit 0=no changes, 1=changes, 2=error |
86
86
  | `--skip-orphan-cleanup` | Skip deletion of orphaned synced files |
87
+ | `--skip-verify` | Skip verification steps after syncing |
87
88
  | `--pr-title` | Override PR title (supports `{name}`, `{dest_name}`) |
88
89
  | `--pr-labels` | Comma-separated PR labels |
89
90
  | `--pr-reviewers` | Comma-separated PR reviewers |
@@ -106,12 +107,12 @@ Options:
106
107
  | Interactive sync | `copy -n cfg` |
107
108
  | CI fresh sync | `copy -n cfg --checkout-from-default -y` |
108
109
  | Local preview | `copy -n cfg --dry-run` |
109
- | Local test files | `copy -n cfg --local` |
110
+ | Local test files | `copy -n cfg --skip-commit` |
110
111
  | Already on branch | `copy -n cfg --no-checkout` |
111
112
  | Push, manual PR | `copy -n cfg --no-pr -y` |
112
113
  | Force opted-out | `copy -n cfg --force-overwrite` |
113
114
 
114
- **Interactive prompt behavior**: Declining the checkout prompt syncs files but skips commit/push/PR (same as `--local`). Use `--no-checkout` when you're already on the correct branch and want to proceed with git operations.
115
+ **Interactive prompt behavior**: Each git operation (checkout, commit, push, PR) prompts independently. Use `--no-checkout` to skip the branch switch prompt. Use `--skip-commit` to skip all git operations after sync.
115
116
 
116
117
  ## Section Markers
117
118
 
@@ -139,6 +140,36 @@ destinations:
139
140
  justfile: [coverage] # keep local coverage recipe
140
141
  ```
141
142
 
143
+ ## Wrapping Synced Files
144
+
145
+ For files without section markers, `wrap_synced_files` automatically wraps content in a `synced` section. This lets destinations add content before/after the synced content.
146
+
147
+ **Without wrapping** (default):
148
+ ```python
149
+ # path-sync copy -n myconfig
150
+ def hello():
151
+ pass
152
+ ```
153
+
154
+ **With wrapping** (`wrap_synced_files: true`):
155
+ ```python
156
+ # path-sync copy -n myconfig
157
+ # === DO_NOT_EDIT: path-sync synced ===
158
+ def hello():
159
+ pass
160
+ # === OK_EDIT: path-sync synced ===
161
+ ```
162
+
163
+ Destinations can add content outside the section markers. Per-path override via `wrap: false`:
164
+
165
+ ```yaml
166
+ wrap_synced_files: true
167
+ paths:
168
+ - src_path: templates/base.py # wrapped
169
+ - src_path: .editorconfig
170
+ wrap: false # not wrapped
171
+ ```
172
+
142
173
  ## Skipping Files per Destination
143
174
 
144
175
  Use `skip_file_patterns` to exclude files for specific destinations. Patterns match against the **destination path** (after `dest_path` remapping):
@@ -195,6 +226,8 @@ destinations:
195
226
  | `destinations` | Target repos with sync settings |
196
227
  | `header_config` | Comment style per extension (has defaults) |
197
228
  | `pr_defaults` | PR title, labels, reviewers, assignees |
229
+ | `wrap_synced_files` | Wrap synced files in section markers (default: `false`) |
230
+ | `verify` | Verification steps to run after syncing (see [Verify Steps](#verify-steps-in-copy)) |
198
231
 
199
232
  **Path options**:
200
233
 
@@ -205,6 +238,7 @@ destinations:
205
238
  | `sync_mode` | `sync` (default), `replace`, or `scaffold` |
206
239
  | `exclude_dirs` | Directory names to skip (defaults: `__pycache__`, `.git`, `.venv`, etc.) |
207
240
  | `exclude_file_patterns` | Filename patterns to skip, supports globs (`*.pyc`, `test_*.py`) |
241
+ | `wrap` | Override global `wrap_synced_files` for this path (`true`/`false`) |
208
242
 
209
243
  **Destination options**:
210
244
 
@@ -217,6 +251,37 @@ destinations:
217
251
  | `default_branch` | Default branch to compare against (defaults to `main`) |
218
252
  | `skip_sections` | Map of `{dest_path: [section_ids]}` to preserve locally |
219
253
  | `skip_file_patterns` | Patterns to skip for this destination (matches dest path, fnmatch syntax) |
254
+ | `verify` | Per-destination verify config (overrides source-level verify) |
255
+
256
+ ## Verify Steps in Copy
257
+
258
+ Run verification steps after syncing files. Synced files are committed first, then verify steps run and can make additional commits.
259
+
260
+ ```yaml
261
+ name: myconfig
262
+ verify:
263
+ on_fail: warn # default: warn (also: skip, fail)
264
+ steps:
265
+ - run: just fmt
266
+ commit:
267
+ message: "style: format synced files"
268
+ add_paths: ["."]
269
+ on_fail: warn
270
+ - run: just test
271
+ ```
272
+
273
+ Per-destination override:
274
+
275
+ ```yaml
276
+ destinations:
277
+ - name: dest1
278
+ dest_path_relative: ../dest1
279
+ verify:
280
+ steps:
281
+ - run: npm run build
282
+ ```
283
+
284
+ Use `--skip-verify` to disable verification steps.
220
285
 
221
286
  ## Header Format
222
287
 
@@ -314,6 +379,109 @@ Add as repository secret: `GH_PAT`
314
379
  | `GraphQL: Resource not accessible` | Use GH_PAT, not GITHUB_TOKEN |
315
380
  | `HTTP 422: Required status check` | Exclude `sync/*` from branch protection |
316
381
 
382
+ ## Dependency Updates
383
+
384
+ The `dep-update` command runs dependency updates across multiple repositories. It clones repos, runs update commands, verifies changes, and creates PRs.
385
+
386
+ ### Quick Start
387
+
388
+ ```bash
389
+ # Create config at .github/myconfig.dep.yaml (see example below)
390
+ # Then run:
391
+ path-sync dep-update -n myconfig
392
+
393
+ # Preview without creating PRs
394
+ path-sync dep-update -n myconfig --dry-run
395
+
396
+ # Filter specific destinations
397
+ path-sync dep-update -n myconfig -d repo1,repo2
398
+ ```
399
+
400
+ ### Dep Config Reference
401
+
402
+ **Config file**: `.github/{name}.dep.yaml`
403
+
404
+ ```yaml
405
+ name: uv-deps
406
+ from_config: python-template # references .github/python-template.src.yaml for destinations
407
+ exclude_destinations:
408
+ - path-sync # skip self
409
+
410
+ updates:
411
+ - command: uv lock --upgrade
412
+ - workdir: packages/sub # optional subdirectory
413
+ command: uv lock --upgrade
414
+
415
+ verify:
416
+ on_fail: skip # default strategy: skip, fail, warn
417
+ steps:
418
+ - run: uv sync
419
+ - run: just fmt
420
+ commit:
421
+ message: "chore: format after update"
422
+ add_paths: [".", "!uv.lock"] # ! prefix excludes
423
+ on_fail: warn
424
+ - run: just test
425
+
426
+ pr:
427
+ branch: deps/uv-lock-update
428
+ title: "chore(deps): update uv.lock"
429
+ labels: [dependencies]
430
+ reviewers: [] # optional
431
+ assignees: [] # optional
432
+ auto_merge: true
433
+ ```
434
+
435
+ | Field | Description |
436
+ |-------|-------------|
437
+ | `from_config` | Source config name for destination list |
438
+ | `include_destinations` | Only process these destinations |
439
+ | `exclude_destinations` | Skip these destinations |
440
+ | `updates` | Commands to run (in order) |
441
+ | `verify.on_fail` | Default failure strategy: `skip`, `fail`, `warn` |
442
+ | `verify.steps` | Verification commands with optional commit/on_fail |
443
+ | `pr.auto_merge` | Enable GitHub auto-merge after PR creation |
444
+
445
+ ### CLI Flags
446
+
447
+ | Flag | Description |
448
+ |------|-------------|
449
+ | `-n, --name` | Config name (required) |
450
+ | `-d, --dest` | Filter destinations (comma-separated) |
451
+ | `--work-dir` | Clone directory for repos without `dest_path_relative` |
452
+ | `--dry-run` | Preview without creating PRs |
453
+ | `--skip-verify` | Skip verification steps |
454
+ | `--pr-reviewers` | Override PR reviewers (comma-separated) |
455
+ | `--pr-assignees` | Override PR assignees (comma-separated) |
456
+
457
+ ### Failure Strategies
458
+
459
+ - **skip**: Skip PR for this repo, continue with others (default)
460
+ - **fail**: Stop all processing immediately
461
+ - **warn**: Create PR anyway with warning in body
462
+
463
+ Per-step `on_fail` overrides the verify-level default.
464
+
465
+ ### GitHub Actions
466
+
467
+ ```yaml
468
+ name: dep-update
469
+ on:
470
+ schedule:
471
+ - cron: "0 6 * * 1" # Weekly Monday
472
+ workflow_dispatch:
473
+
474
+ jobs:
475
+ update:
476
+ runs-on: ubuntu-latest
477
+ steps:
478
+ - uses: actions/checkout@v4
479
+ - uses: astral-sh/setup-uv@v5
480
+ - run: uvx path-sync dep-update -n myconfig
481
+ env:
482
+ GH_TOKEN: ${{ secrets.GH_PAT }}
483
+ ```
484
+
317
485
  ## Alternatives Considered
318
486
 
319
487
  | Tool | Why Not |
@@ -59,13 +59,14 @@ By default, prompts before each git operation. See [Usage Scenarios](#usage-scen
59
59
  | `-d dest1,dest2` | Filter specific destinations |
60
60
  | `--dry-run` | Preview without writing (requires existing repos) |
61
61
  | `-y, --no-prompt` | Skip confirmations (for CI) |
62
- | `--local` | No git ops after sync (no commit/push/PR) |
62
+ | `--skip-commit` | No git ops after sync (no commit/push/PR). Alias: `--local` |
63
63
  | `--no-checkout` | Skip branch switching (assumes already on correct branch) |
64
64
  | `--checkout-from-default` | Reset to origin/default before sync |
65
65
  | `--no-pr` | Push but skip PR creation |
66
66
  | `--force-overwrite` | Overwrite files even if header removed (opted out) |
67
67
  | `--detailed-exit-code` | Exit 0=no changes, 1=changes, 2=error |
68
68
  | `--skip-orphan-cleanup` | Skip deletion of orphaned synced files |
69
+ | `--skip-verify` | Skip verification steps after syncing |
69
70
  | `--pr-title` | Override PR title (supports `{name}`, `{dest_name}`) |
70
71
  | `--pr-labels` | Comma-separated PR labels |
71
72
  | `--pr-reviewers` | Comma-separated PR reviewers |
@@ -88,12 +89,12 @@ Options:
88
89
  | Interactive sync | `copy -n cfg` |
89
90
  | CI fresh sync | `copy -n cfg --checkout-from-default -y` |
90
91
  | Local preview | `copy -n cfg --dry-run` |
91
- | Local test files | `copy -n cfg --local` |
92
+ | Local test files | `copy -n cfg --skip-commit` |
92
93
  | Already on branch | `copy -n cfg --no-checkout` |
93
94
  | Push, manual PR | `copy -n cfg --no-pr -y` |
94
95
  | Force opted-out | `copy -n cfg --force-overwrite` |
95
96
 
96
- **Interactive prompt behavior**: Declining the checkout prompt syncs files but skips commit/push/PR (same as `--local`). Use `--no-checkout` when you're already on the correct branch and want to proceed with git operations.
97
+ **Interactive prompt behavior**: Each git operation (checkout, commit, push, PR) prompts independently. Use `--no-checkout` to skip the branch switch prompt. Use `--skip-commit` to skip all git operations after sync.
97
98
 
98
99
  ## Section Markers
99
100
 
@@ -121,6 +122,36 @@ destinations:
121
122
  justfile: [coverage] # keep local coverage recipe
122
123
  ```
123
124
 
125
+ ## Wrapping Synced Files
126
+
127
+ For files without section markers, `wrap_synced_files` automatically wraps content in a `synced` section. This lets destinations add content before/after the synced content.
128
+
129
+ **Without wrapping** (default):
130
+ ```python
131
+ # path-sync copy -n myconfig
132
+ def hello():
133
+ pass
134
+ ```
135
+
136
+ **With wrapping** (`wrap_synced_files: true`):
137
+ ```python
138
+ # path-sync copy -n myconfig
139
+ # === DO_NOT_EDIT: path-sync synced ===
140
+ def hello():
141
+ pass
142
+ # === OK_EDIT: path-sync synced ===
143
+ ```
144
+
145
+ Destinations can add content outside the section markers. Per-path override via `wrap: false`:
146
+
147
+ ```yaml
148
+ wrap_synced_files: true
149
+ paths:
150
+ - src_path: templates/base.py # wrapped
151
+ - src_path: .editorconfig
152
+ wrap: false # not wrapped
153
+ ```
154
+
124
155
  ## Skipping Files per Destination
125
156
 
126
157
  Use `skip_file_patterns` to exclude files for specific destinations. Patterns match against the **destination path** (after `dest_path` remapping):
@@ -177,6 +208,8 @@ destinations:
177
208
  | `destinations` | Target repos with sync settings |
178
209
  | `header_config` | Comment style per extension (has defaults) |
179
210
  | `pr_defaults` | PR title, labels, reviewers, assignees |
211
+ | `wrap_synced_files` | Wrap synced files in section markers (default: `false`) |
212
+ | `verify` | Verification steps to run after syncing (see [Verify Steps](#verify-steps-in-copy)) |
180
213
 
181
214
  **Path options**:
182
215
 
@@ -187,6 +220,7 @@ destinations:
187
220
  | `sync_mode` | `sync` (default), `replace`, or `scaffold` |
188
221
  | `exclude_dirs` | Directory names to skip (defaults: `__pycache__`, `.git`, `.venv`, etc.) |
189
222
  | `exclude_file_patterns` | Filename patterns to skip, supports globs (`*.pyc`, `test_*.py`) |
223
+ | `wrap` | Override global `wrap_synced_files` for this path (`true`/`false`) |
190
224
 
191
225
  **Destination options**:
192
226
 
@@ -199,6 +233,37 @@ destinations:
199
233
  | `default_branch` | Default branch to compare against (defaults to `main`) |
200
234
  | `skip_sections` | Map of `{dest_path: [section_ids]}` to preserve locally |
201
235
  | `skip_file_patterns` | Patterns to skip for this destination (matches dest path, fnmatch syntax) |
236
+ | `verify` | Per-destination verify config (overrides source-level verify) |
237
+
238
+ ## Verify Steps in Copy
239
+
240
+ Run verification steps after syncing files. Synced files are committed first, then verify steps run and can make additional commits.
241
+
242
+ ```yaml
243
+ name: myconfig
244
+ verify:
245
+ on_fail: warn # default: warn (also: skip, fail)
246
+ steps:
247
+ - run: just fmt
248
+ commit:
249
+ message: "style: format synced files"
250
+ add_paths: ["."]
251
+ on_fail: warn
252
+ - run: just test
253
+ ```
254
+
255
+ Per-destination override:
256
+
257
+ ```yaml
258
+ destinations:
259
+ - name: dest1
260
+ dest_path_relative: ../dest1
261
+ verify:
262
+ steps:
263
+ - run: npm run build
264
+ ```
265
+
266
+ Use `--skip-verify` to disable verification steps.
202
267
 
203
268
  ## Header Format
204
269
 
@@ -296,6 +361,109 @@ Add as repository secret: `GH_PAT`
296
361
  | `GraphQL: Resource not accessible` | Use GH_PAT, not GITHUB_TOKEN |
297
362
  | `HTTP 422: Required status check` | Exclude `sync/*` from branch protection |
298
363
 
364
+ ## Dependency Updates
365
+
366
+ The `dep-update` command runs dependency updates across multiple repositories. It clones repos, runs update commands, verifies changes, and creates PRs.
367
+
368
+ ### Quick Start
369
+
370
+ ```bash
371
+ # Create config at .github/myconfig.dep.yaml (see example below)
372
+ # Then run:
373
+ path-sync dep-update -n myconfig
374
+
375
+ # Preview without creating PRs
376
+ path-sync dep-update -n myconfig --dry-run
377
+
378
+ # Filter specific destinations
379
+ path-sync dep-update -n myconfig -d repo1,repo2
380
+ ```
381
+
382
+ ### Dep Config Reference
383
+
384
+ **Config file**: `.github/{name}.dep.yaml`
385
+
386
+ ```yaml
387
+ name: uv-deps
388
+ from_config: python-template # references .github/python-template.src.yaml for destinations
389
+ exclude_destinations:
390
+ - path-sync # skip self
391
+
392
+ updates:
393
+ - command: uv lock --upgrade
394
+ - workdir: packages/sub # optional subdirectory
395
+ command: uv lock --upgrade
396
+
397
+ verify:
398
+ on_fail: skip # default strategy: skip, fail, warn
399
+ steps:
400
+ - run: uv sync
401
+ - run: just fmt
402
+ commit:
403
+ message: "chore: format after update"
404
+ add_paths: [".", "!uv.lock"] # ! prefix excludes
405
+ on_fail: warn
406
+ - run: just test
407
+
408
+ pr:
409
+ branch: deps/uv-lock-update
410
+ title: "chore(deps): update uv.lock"
411
+ labels: [dependencies]
412
+ reviewers: [] # optional
413
+ assignees: [] # optional
414
+ auto_merge: true
415
+ ```
416
+
417
+ | Field | Description |
418
+ |-------|-------------|
419
+ | `from_config` | Source config name for destination list |
420
+ | `include_destinations` | Only process these destinations |
421
+ | `exclude_destinations` | Skip these destinations |
422
+ | `updates` | Commands to run (in order) |
423
+ | `verify.on_fail` | Default failure strategy: `skip`, `fail`, `warn` |
424
+ | `verify.steps` | Verification commands with optional commit/on_fail |
425
+ | `pr.auto_merge` | Enable GitHub auto-merge after PR creation |
426
+
427
+ ### CLI Flags
428
+
429
+ | Flag | Description |
430
+ |------|-------------|
431
+ | `-n, --name` | Config name (required) |
432
+ | `-d, --dest` | Filter destinations (comma-separated) |
433
+ | `--work-dir` | Clone directory for repos without `dest_path_relative` |
434
+ | `--dry-run` | Preview without creating PRs |
435
+ | `--skip-verify` | Skip verification steps |
436
+ | `--pr-reviewers` | Override PR reviewers (comma-separated) |
437
+ | `--pr-assignees` | Override PR assignees (comma-separated) |
438
+
439
+ ### Failure Strategies
440
+
441
+ - **skip**: Skip PR for this repo, continue with others (default)
442
+ - **fail**: Stop all processing immediately
443
+ - **warn**: Create PR anyway with warning in body
444
+
445
+ Per-step `on_fail` overrides the verify-level default.
446
+
447
+ ### GitHub Actions
448
+
449
+ ```yaml
450
+ name: dep-update
451
+ on:
452
+ schedule:
453
+ - cron: "0 6 * * 1" # Weekly Monday
454
+ workflow_dispatch:
455
+
456
+ jobs:
457
+ update:
458
+ runs-on: ubuntu-latest
459
+ steps:
460
+ - uses: actions/checkout@v4
461
+ - uses: astral-sh/setup-uv@v5
462
+ - run: uvx path-sync dep-update -n myconfig
463
+ env:
464
+ GH_TOKEN: ${{ secrets.GH_PAT }}
465
+ ```
466
+
299
467
  ## Alternatives Considered
300
468
 
301
469
  | Tool | Why Not |
@@ -1,12 +1,14 @@
1
1
  # Generated by pkg-ext
2
2
  # flake8: noqa
3
- from path_sync import config
4
3
  from path_sync import copy
5
4
  from path_sync import dep_update
5
+ from path_sync import validate_no_changes
6
+ from path_sync import config
6
7
 
7
- VERSION = "0.4.1"
8
+ VERSION = "0.6.0"
8
9
  __all__ = [
9
- "config",
10
10
  "copy",
11
11
  "dep_update",
12
+ "validate_no_changes",
13
+ "config",
12
14
  ]