model-mirror-cli 0.2.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 (54) hide show
  1. model_mirror_cli-0.2.0/.github/workflows/ci.yml +45 -0
  2. model_mirror_cli-0.2.0/.github/workflows/release.yml +47 -0
  3. model_mirror_cli-0.2.0/.gitignore +59 -0
  4. model_mirror_cli-0.2.0/CONTRIBUTORS.md +87 -0
  5. model_mirror_cli-0.2.0/LICENSE +21 -0
  6. model_mirror_cli-0.2.0/PKG-INFO +550 -0
  7. model_mirror_cli-0.2.0/README.md +524 -0
  8. model_mirror_cli-0.2.0/RELEASING.md +61 -0
  9. model_mirror_cli-0.2.0/docs/torrent-backend-spike.md +70 -0
  10. model_mirror_cli-0.2.0/docs/torrent-distribution-requirements.md +432 -0
  11. model_mirror_cli-0.2.0/model_mirror/__init__.py +7 -0
  12. model_mirror_cli-0.2.0/model_mirror/audit.py +214 -0
  13. model_mirror_cli-0.2.0/model_mirror/checksums.py +371 -0
  14. model_mirror_cli-0.2.0/model_mirror/cli.py +2436 -0
  15. model_mirror_cli-0.2.0/model_mirror/config.py +300 -0
  16. model_mirror_cli-0.2.0/model_mirror/hub.py +901 -0
  17. model_mirror_cli-0.2.0/model_mirror/lock.py +119 -0
  18. model_mirror_cli-0.2.0/model_mirror/mirror.py +201 -0
  19. model_mirror_cli-0.2.0/model_mirror/progress.py +260 -0
  20. model_mirror_cli-0.2.0/model_mirror/removal.py +164 -0
  21. model_mirror_cli-0.2.0/model_mirror/repair.py +399 -0
  22. model_mirror_cli-0.2.0/model_mirror/state.py +150 -0
  23. model_mirror_cli-0.2.0/model_mirror/torrent.py +453 -0
  24. model_mirror_cli-0.2.0/model_mirror/torrent_coverage.py +400 -0
  25. model_mirror_cli-0.2.0/model_mirror/torrent_hashes.py +168 -0
  26. model_mirror_cli-0.2.0/model_mirror/torrent_import.py +620 -0
  27. model_mirror_cli-0.2.0/model_mirror/torrent_publication.py +582 -0
  28. model_mirror_cli-0.2.0/model_mirror/torrent_seed.py +228 -0
  29. model_mirror_cli-0.2.0/model_mirror/verify.py +153 -0
  30. model_mirror_cli-0.2.0/pyproject.toml +65 -0
  31. model_mirror_cli-0.2.0/scripts/check_release.py +24 -0
  32. model_mirror_cli-0.2.0/tests/smoke_test.py +28 -0
  33. model_mirror_cli-0.2.0/tests/test_audit.py +294 -0
  34. model_mirror_cli-0.2.0/tests/test_checksums.py +373 -0
  35. model_mirror_cli-0.2.0/tests/test_cli.py +2542 -0
  36. model_mirror_cli-0.2.0/tests/test_config.py +420 -0
  37. model_mirror_cli-0.2.0/tests/test_hub.py +1276 -0
  38. model_mirror_cli-0.2.0/tests/test_lock.py +66 -0
  39. model_mirror_cli-0.2.0/tests/test_mirror.py +376 -0
  40. model_mirror_cli-0.2.0/tests/test_progress.py +130 -0
  41. model_mirror_cli-0.2.0/tests/test_removal.py +395 -0
  42. model_mirror_cli-0.2.0/tests/test_repair.py +551 -0
  43. model_mirror_cli-0.2.0/tests/test_state.py +109 -0
  44. model_mirror_cli-0.2.0/tests/test_torrent.py +422 -0
  45. model_mirror_cli-0.2.0/tests/test_torrent_cli.py +337 -0
  46. model_mirror_cli-0.2.0/tests/test_torrent_coverage.py +221 -0
  47. model_mirror_cli-0.2.0/tests/test_torrent_fencing.py +215 -0
  48. model_mirror_cli-0.2.0/tests/test_torrent_hashes.py +116 -0
  49. model_mirror_cli-0.2.0/tests/test_torrent_hub_integration.py +67 -0
  50. model_mirror_cli-0.2.0/tests/test_torrent_import.py +595 -0
  51. model_mirror_cli-0.2.0/tests/test_torrent_publication.py +307 -0
  52. model_mirror_cli-0.2.0/tests/test_torrent_seed.py +348 -0
  53. model_mirror_cli-0.2.0/tests/test_verify.py +164 -0
  54. model_mirror_cli-0.2.0/uv.lock +512 -0
@@ -0,0 +1,45 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version:
16
+ - "3.11"
17
+ - "3.12"
18
+ - "3.13"
19
+ steps:
20
+ - name: Check out source
21
+ uses: actions/checkout@v7
22
+ - name: Install uv and Python
23
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ - name: Install locked dependencies
27
+ run: uv sync --locked --all-extras --dev
28
+ - name: Run test and coverage gate
29
+ run: |
30
+ uv run coverage run -m pytest -q
31
+ uv run coverage report -m
32
+
33
+ build:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - name: Check out source
37
+ uses: actions/checkout@v7
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
40
+ - name: Build wheel and source distribution
41
+ run: uv build --no-sources
42
+ - name: Smoke-test wheel
43
+ run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
44
+ - name: Smoke-test source distribution
45
+ run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
@@ -0,0 +1,47 @@
1
+ name: Publish release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment:
12
+ name: pypi
13
+ permissions:
14
+ contents: read
15
+ id-token: write
16
+ steps:
17
+ - name: Check out source
18
+ uses: actions/checkout@v7
19
+ with:
20
+ fetch-depth: 0
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
23
+ - name: Validate release tag
24
+ run: |
25
+ python scripts/check_release.py "$GITHUB_REF_NAME"
26
+ if [ "$(git cat-file -t "$GITHUB_REF_NAME")" != "tag" ]; then
27
+ echo "::error::release tag must be annotated"
28
+ exit 1
29
+ fi
30
+ git fetch --no-tags origin main:refs/remotes/origin/main
31
+ if ! git merge-base --is-ancestor "$(git rev-list -n 1 "$GITHUB_REF_NAME")" origin/main; then
32
+ echo "::error::release tag must point to a commit on main"
33
+ exit 1
34
+ fi
35
+ - name: Run test and coverage gate
36
+ run: |
37
+ uv sync --locked --all-extras --dev
38
+ uv run coverage run -m pytest -q
39
+ uv run coverage report -m
40
+ - name: Build wheel and source distribution
41
+ run: uv build --no-sources
42
+ - name: Smoke-test wheel
43
+ run: uv run --isolated --no-project --with dist/*.whl tests/smoke_test.py
44
+ - name: Smoke-test source distribution
45
+ run: uv run --isolated --no-project --with dist/*.tar.gz tests/smoke_test.py
46
+ - name: Publish to PyPI
47
+ run: uv publish
@@ -0,0 +1,59 @@
1
+ # Secrets and local configuration
2
+ .env
3
+ .env.*
4
+ *.pem
5
+ *.key
6
+ *.token
7
+ .model-mirror.yaml
8
+ model-mirror.yaml
9
+ .model-mirror/
10
+ .verification
11
+ .checksums
12
+ .manifest
13
+
14
+ # Model archives and large local state
15
+ models/
16
+ datasets/
17
+ spaces/
18
+ archives/
19
+ downloads/
20
+ .cache/
21
+ cache/
22
+ tmp/
23
+ logs/
24
+ *.safetensors
25
+ *.bin
26
+ *.pt
27
+ *.pth
28
+ *.ckpt
29
+ *.gguf
30
+ *.onnx
31
+ *.msgpack
32
+ *.h5
33
+ *.incomplete
34
+ SHA256SUMS
35
+ MANIFEST.jsonl
36
+
37
+ # Python
38
+ __pycache__/
39
+ *.py[cod]
40
+ *.egg-info/
41
+ .eggs/
42
+ dist/
43
+ build/
44
+ .venv/
45
+ venv/
46
+
47
+ # Test and tooling output
48
+ .pytest_cache/
49
+ .coverage
50
+ coverage.xml
51
+ htmlcov/
52
+ .mypy_cache/
53
+ .ruff_cache/
54
+
55
+ # OS/editor noise
56
+ .DS_Store
57
+ Thumbs.db
58
+ *~
59
+ *.swp
@@ -0,0 +1,87 @@
1
+ # Contributors
2
+
3
+ This document holds implementation notes and development details. User-facing
4
+ usage belongs in `README.md`.
5
+
6
+ ## Development
7
+
8
+ ```bash
9
+ uv sync
10
+ uv run coverage run -m pytest
11
+ uv run coverage report -m
12
+ ```
13
+
14
+ The test suite is expected to hold 100% statement and branch coverage. The
15
+ coverage gate is enforced in `pyproject.toml`.
16
+
17
+ ## State Files
18
+
19
+ Each mirrored repo owns its state inside the repo directory:
20
+
21
+ - `.verification`: YAML status, timestamps, commit metadata, offline-only flag,
22
+ issues, repair paths
23
+ - `.manifest`: versioned JSONL records with local size, mtime, SHA-256, and Git blob SHA-1
24
+ - `.verification.lock`: advisory lock metadata while an operation is active
25
+ - `.model-mirror/snapshot.json`: authoritative commit-pinned expected file list
26
+ - `.model-mirror/torrent/coverage/`: atomic, resumable profile-specific piece
27
+ and Merkle hash coverage
28
+ - `.model-mirror/torrent/publications/`: torrent artifacts, recovery records,
29
+ and durable publication/desired-seed state
30
+ - `.model-mirror/torrent/fence.json`: the active commit update fence
31
+
32
+ Deleting a model directory deletes its verification state with it. There is no
33
+ global model state database.
34
+
35
+ ## Commit Handling
36
+
37
+ Online operations resolve the requested revision to a concrete Hub commit before
38
+ downloading or verifying. A clean local mirror is trusted for its resolved
39
+ commit. If upstream moves, verification records `upstream_status: changed` but
40
+ does not mutate local files. `repair --update` is the explicit command for
41
+ moving to the new upstream commit recorded by verification.
42
+
43
+ If upstream is unavailable, verification exits non-zero and preserves the local
44
+ verification status when one already exists. `offline` sets `offline_only: true`
45
+ for that repo, clears the upstream-unavailable issue, and makes future
46
+ verification local-only until `online` clears the flag.
47
+
48
+ ## Checksums
49
+
50
+ Manifest writes are incremental. After each file is hashed, `.manifest` is
51
+ atomically rewritten with a schema/version header and one record per payload
52
+ file. Each file is read once while both SHA-256 and Git blob SHA-1 are computed.
53
+ Later runs skip files whose size, mtime, and hash fields match the manifest
54
+ record. Downloads accumulate these hashes and any enabled torrent coverage
55
+ while streaming into the destination's `.incomplete` file. This makes an
56
+ interrupted download resumable without a mandatory second full-file hash pass.
57
+
58
+ ## Locking
59
+
60
+ Mutating mirror, verification, repair, upgrade, and torrent control-plane
61
+ transitions take an advisory lock on `.verification.lock` for the target repo.
62
+ The first mirror operation writes `.verification` with `status: in_progress`
63
+ before downloading. The seeder acquires the lock only for short reconciliation
64
+ transitions, never for its lifetime. `list` does not block; it reports lock
65
+ metadata when a repo is busy.
66
+
67
+ ## Hugging Face And Xet
68
+
69
+ `model-mirror` uses `huggingface_hub` to resolve pinned metadata and transport
70
+ URLs, then streams HTTP or Xet bytes into its own resumable hashing writer.
71
+ Environment setup is derived from model-mirror config and is intentionally
72
+ authoritative: if a config boolean is false, inherited Xet environment
73
+ variables for that feature are removed before importing or using the transport.
74
+
75
+ The conservative default is:
76
+
77
+ - high-performance Xet mode off
78
+ - range-get concurrency `1`
79
+ - optional sequential reconstruction writes for HDD-backed archives
80
+
81
+ This keeps the default path usable on lower-memory machines and lets power users
82
+ tune throughput explicitly.
83
+
84
+ ## Design Documents
85
+
86
+ - [Torrent Distribution And Archive Upgrade Requirements](docs/torrent-distribution-requirements.md)
87
+ - [Torrent Backend Spike](docs/torrent-backend-spike.md)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xlr8harder
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.