path-sync 0.4.1__py3-none-any.whl → 0.6.0__py3-none-any.whl

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.
path_sync/dep_update.py CHANGED
@@ -1,18 +1,18 @@
1
1
  # Generated by pkg-ext
2
2
  from path_sync._internal.cmd_dep_update import dep_update as _dep_update
3
- from path_sync._internal.models_dep import CommitConfig as _CommitConfig
3
+ from path_sync._internal.models import CommitConfig as _CommitConfig
4
+ from path_sync._internal.models import OnFailStrategy as _OnFailStrategy
5
+ from path_sync._internal.models import VerifyConfig as _VerifyConfig
6
+ from path_sync._internal.models import VerifyStep as _VerifyStep
4
7
  from path_sync._internal.models_dep import DepConfig as _DepConfig
5
- from path_sync._internal.models_dep import OnFailStrategy as _OnFailStrategy
6
8
  from path_sync._internal.models_dep import PRConfig as _PRConfig
7
9
  from path_sync._internal.models_dep import UpdateEntry as _UpdateEntry
8
- from path_sync._internal.models_dep import VerifyConfig as _VerifyConfig
9
- from path_sync._internal.models_dep import VerifyStep as _VerifyStep
10
10
 
11
11
  dep_update = _dep_update
12
12
  CommitConfig = _CommitConfig
13
- DepConfig = _DepConfig
14
13
  OnFailStrategy = _OnFailStrategy
15
- PRConfig = _PRConfig
16
- UpdateEntry = _UpdateEntry
17
14
  VerifyConfig = _VerifyConfig
18
15
  VerifyStep = _VerifyStep
16
+ DepConfig = _DepConfig
17
+ PRConfig = _PRConfig
18
+ UpdateEntry = _UpdateEntry
path_sync/sections.py CHANGED
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from zero_3rdparty.sections import (
6
6
  Section,
7
7
  SectionChanges,
8
+ SectionPart,
8
9
  get_comment_config,
9
10
  )
10
11
  from zero_3rdparty.sections import (
@@ -25,16 +26,22 @@ from zero_3rdparty.sections import (
25
26
  from zero_3rdparty.sections import (
26
27
  wrap_in_default_section as _wrap_in_default_section,
27
28
  )
29
+ from zero_3rdparty.sections import (
30
+ wrap_section as _wrap_section,
31
+ )
28
32
 
29
33
  __all__ = [
30
34
  "Section",
31
35
  "SectionChanges",
36
+ "SectionPart",
37
+ "build_sections_content",
32
38
  "changed_sections",
33
39
  "extract_sections",
34
40
  "has_sections",
35
41
  "parse_sections",
36
42
  "replace_sections",
37
43
  "wrap_in_default_section",
44
+ "wrap_in_synced_section",
38
45
  ]
39
46
 
40
47
  TOOL_NAME = "path-sync"
@@ -52,13 +59,17 @@ def wrap_in_default_section(content: str, path: Path) -> str:
52
59
  return _wrap_in_default_section(content, TOOL_NAME, get_comment_config(path))
53
60
 
54
61
 
62
+ def wrap_in_synced_section(content: str, path: Path) -> str:
63
+ return _wrap_section(content, "synced", TOOL_NAME, get_comment_config(path))
64
+
65
+
55
66
  def extract_sections(content: str, path: Path) -> dict[str, str]:
56
67
  return _extract_sections(content, TOOL_NAME, get_comment_config(path), str(path))
57
68
 
58
69
 
59
70
  def replace_sections(
60
71
  dest_content: str,
61
- src_sections: dict[str, str],
72
+ src_sections: list[Section],
62
73
  path: Path,
63
74
  skip_sections: list[str] | None = None,
64
75
  *,
@@ -81,3 +92,10 @@ def changed_sections(
81
92
  skip: set[str] | None = None,
82
93
  ) -> SectionChanges:
83
94
  return _changed_sections(baseline_content, current_content, TOOL_NAME, get_comment_config(path), skip, str(path))
95
+
96
+
97
+ def build_sections_content(section_list: list[Section], path: Path) -> str:
98
+ """Build file content from a list of Section objects."""
99
+ config = get_comment_config(path)
100
+ parts = [_wrap_section(s.content, s.id, TOOL_NAME, config) for s in section_list]
101
+ return "\n".join(parts)
@@ -0,0 +1,4 @@
1
+ # Generated by pkg-ext
2
+ from path_sync._internal.cmd_validate import validate_no_changes as _validate_no_changes
3
+
4
+ validate_no_changes = _validate_no_changes
@@ -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 |
@@ -0,0 +1,29 @@
1
+ path_sync/__init__.py,sha256=cUWYc7YQIT-32_mOxRYyTBCiZlI5ZPf_6H0nCyMOQuM,273
2
+ path_sync/__main__.py,sha256=zAjlCOVF1duQi_RSVWbncfmnjoqaik9YPl2jf5mfcR0,342
3
+ path_sync/config.py,sha256=XYuEK_bjSpAk_nZN0oxpEA-S3t9F6Qn0yznYLoQHIw8,568
4
+ path_sync/copy.py,sha256=fsTKQtSuphNw7fHGpK5E56JGLYcalslr5MHnY0bn-NU,188
5
+ path_sync/dep_update.py,sha256=Lzj09StST253rXnsh9XpGDgc1Y_vhYvLO2-fAU3huxM,788
6
+ path_sync/sections.py,sha256=SsTeOa3Ep2WuzyO6ckfnxs-Gzv70r3mmnz296_ZPZZA,2726
7
+ path_sync/validate_no_changes.py,sha256=QJtjSsFoqqMT9jeSgZXgq4gGUxDjIn5GUqhOixjhDlg,156
8
+ path_sync/_internal/__init__.py,sha256=iPkMhrpiyXBijo2Hp-y_2zEYxAXnHLnStKM0X0HHd4U,56
9
+ path_sync/_internal/cmd_boot.py,sha256=cFomUyPOhNX9fi5_BYL2Sm7O1oq7vntD8b7clQ7mL1E,3104
10
+ path_sync/_internal/cmd_copy.py,sha256=r1gymDP-I-rUdfbMglHElmJ4WjtmzqRCVtwJq0TbUXE,19601
11
+ path_sync/_internal/cmd_dep_update.py,sha256=JQmaUKkg9gbfvPKNvpov_b6OI788hpAlU86AMJGJ5tU,8172
12
+ path_sync/_internal/cmd_options.py,sha256=0GZKaBtZ8ae3yKdkAJrfWFHvu--BZvhLI4Gl8s_dJes,617
13
+ path_sync/_internal/cmd_validate.py,sha256=e6m-JZlXAGr0ZRqfLhhrlmjs4w79p2WWnZo4I05PGpo,1798
14
+ path_sync/_internal/file_utils.py,sha256=5C33qzKFQdwChi5YwUWBujj126t0P6dbGSU_5hWExpE,194
15
+ path_sync/_internal/git_ops.py,sha256=Xme_kuIVqCekj6YxWLWxvQu693wPcYHycAIiB_4l_Ow,7500
16
+ path_sync/_internal/header.py,sha256=evgY2q_gfDdEytEt_jyJ7M_KdGzCpfdKBUnoh3v-0Go,2593
17
+ path_sync/_internal/log_capture.py,sha256=IlCX4BBwWvA_FPIRn_8XyeOGE5sLp-kawlIk7vFQMK4,1187
18
+ path_sync/_internal/models.py,sha256=x5tgebVWdfgNhc8ngsPnbPm4mlW_Qa7iOx1cHTLmOdw,5585
19
+ path_sync/_internal/models_dep.py,sha256=WIhgrKexDNKrVvPusC2z-LL0bG9Lf3VcByRK5DvR_jM,1526
20
+ path_sync/_internal/prompt_utils.py,sha256=YChOxsoP11-r7D2k6G_kMrvzbvF0sk9sEPynrhkPkVc,703
21
+ path_sync/_internal/typer_app.py,sha256=lEGMRXql3Se3VbmwAohvpUaL2cbY-RwhPUq8kL7bPbc,177
22
+ path_sync/_internal/validation.py,sha256=23kwtmsiHiYlKbVU8mtwr8J0MqSlnvbuRRR5NQAsJ08,2446
23
+ path_sync/_internal/verify.py,sha256=_5-utNI4s5uERnT5V4uaaM8_k_zeVofFjXvgbl0gZE0,3244
24
+ path_sync/_internal/yaml_utils.py,sha256=yj6Bl54EltjLEcVKaiA5Ahb9byT6OUMh0xIEzTsrvnQ,498
25
+ path_sync-0.6.0.dist-info/METADATA,sha256=6QKbnrMopkcJIG9Bjc8l-jTO-6c1uHwlCz2sZKnSAa4,15020
26
+ path_sync-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
+ path_sync-0.6.0.dist-info/entry_points.txt,sha256=jTsL0c-9gP-4_Jt3EPgihtpLcwQR0AFAf1AUpD50AlI,54
28
+ path_sync-0.6.0.dist-info/licenses/LICENSE,sha256=MnHjsc6ccjI5Iiw2R3jLEAApIcrEpLdIcZxkilhSPxc,1069
29
+ path_sync-0.6.0.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- path_sync/__init__.py,sha256=OHLo0rlS3tquOhUn2sHNgEfAYvtsJaQmT1MYmM94Nuk,204
2
- path_sync/__main__.py,sha256=zAjlCOVF1duQi_RSVWbncfmnjoqaik9YPl2jf5mfcR0,342
3
- path_sync/config.py,sha256=XYuEK_bjSpAk_nZN0oxpEA-S3t9F6Qn0yznYLoQHIw8,568
4
- path_sync/copy.py,sha256=BpflW4086XJFSHHK4taYPgXtF07xHB8qrgm8TYqdM4E,120
5
- path_sync/dep_update.py,sha256=UjjoUIIaZyGwTYmDo3OWQDdpY1zZ4dbdXGwy4xLDuRc,804
6
- path_sync/sections.py,sha256=dB0RGUhRWcZj9c1364UULEEnYHMf1LkWUBZ8EayIyKM,2122
7
- path_sync/_internal/__init__.py,sha256=iPkMhrpiyXBijo2Hp-y_2zEYxAXnHLnStKM0X0HHd4U,56
8
- path_sync/_internal/cmd_boot.py,sha256=cFomUyPOhNX9fi5_BYL2Sm7O1oq7vntD8b7clQ7mL1E,3104
9
- path_sync/_internal/cmd_copy.py,sha256=0iuhq3vlL1bah2EJEvkHZlXqdsOwdkKktt8V7lVjKIY,18241
10
- path_sync/_internal/cmd_dep_update.py,sha256=7mKs-ZdPoAfl3L2Q57345lPtOFtua5taF8dYAe0U1d8,8315
11
- path_sync/_internal/cmd_validate.py,sha256=e6m-JZlXAGr0ZRqfLhhrlmjs4w79p2WWnZo4I05PGpo,1798
12
- path_sync/_internal/file_utils.py,sha256=5C33qzKFQdwChi5YwUWBujj126t0P6dbGSU_5hWExpE,194
13
- path_sync/_internal/git_ops.py,sha256=OJ82-TUUqlFGSCcaeya9J-x8_10aG5iu55SZew_dzbk,7085
14
- path_sync/_internal/header.py,sha256=evgY2q_gfDdEytEt_jyJ7M_KdGzCpfdKBUnoh3v-0Go,2593
15
- path_sync/_internal/models.py,sha256=IA6lb_BFXntcZnn9bWJZYenlnDvk9ddNBA67l5qFrmA,4577
16
- path_sync/_internal/models_dep.py,sha256=KKgcOEUgWPa_speyp_XgwBQDp8L1hS24WN1C4SMMG-w,2014
17
- path_sync/_internal/typer_app.py,sha256=lEGMRXql3Se3VbmwAohvpUaL2cbY-RwhPUq8kL7bPbc,177
18
- path_sync/_internal/validation.py,sha256=23kwtmsiHiYlKbVU8mtwr8J0MqSlnvbuRRR5NQAsJ08,2446
19
- path_sync/_internal/yaml_utils.py,sha256=yj6Bl54EltjLEcVKaiA5Ahb9byT6OUMh0xIEzTsrvnQ,498
20
- path_sync-0.4.1.dist-info/METADATA,sha256=1nIEtOCDLLRoti_7s-OK0x-rHPWtNZqTJd350laIHTw,10430
21
- path_sync-0.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
22
- path_sync-0.4.1.dist-info/entry_points.txt,sha256=jTsL0c-9gP-4_Jt3EPgihtpLcwQR0AFAf1AUpD50AlI,54
23
- path_sync-0.4.1.dist-info/licenses/LICENSE,sha256=MnHjsc6ccjI5Iiw2R3jLEAApIcrEpLdIcZxkilhSPxc,1069
24
- path_sync-0.4.1.dist-info/RECORD,,