linux-mirror-bench 0.1.1__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.
@@ -0,0 +1,410 @@
1
+ Metadata-Version: 2.4
2
+ Name: linux-mirror-bench
3
+ Version: 0.1.1
4
+ Summary: Cross-distribution Linux package mirror benchmarking tool
5
+ Project-URL: Homepage, https://github.com/MysticRyuujin/mirror-bench
6
+ Project-URL: Issues, https://github.com/MysticRyuujin/mirror-bench/issues
7
+ Author-email: MysticRyuujin <MysticRyuujin@protonmail.com>
8
+ License: AGPL-3.0-or-later
9
+ License-File: LICENSE
10
+ Keywords: apt,arch,benchmark,debian,dnf,fedora,linux-mint,mirror,pacman,ubuntu
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: System :: Installation/Setup
20
+ Requires-Python: >=3.14
21
+ Requires-Dist: httpx[http2]>=0.27
22
+ Requires-Dist: rich>=13
23
+ Requires-Dist: typer>=0.12
24
+ Description-Content-Type: text/markdown
25
+
26
+ # mirror-bench
27
+
28
+ A cross-distribution Linux package mirror benchmarking tool.
29
+
30
+ Ranks mirrors by **latency**, **throughput**, and **security** (HTTPS / TLS 1.3 / cert validity / HTTP/2).
31
+
32
+ `mirror-bench` is **read-only**: it discovers mirrors, probes them, prints a
33
+ ranked table, and exits. It never touches `/etc/apt/sources.list`,
34
+ `/etc/yum.repos.d/`, pacman's `mirrorlist`, or any other system configuration.
35
+
36
+ - Cross-distro: **Ubuntu, Debian, Fedora, Linux Mint, Arch**.
37
+ - Modern Python: 3.14, strict typing, async I/O (`httpx` + `asyncio.TaskGroup`).
38
+ - Shippable everywhere: `pipx`, `uv tool`, PyPI wheels, and a minimal
39
+ multi-arch container on GHCR.
40
+
41
+ ## Table of contents
42
+
43
+ - [Quick start](#quick-start)
44
+ - [Installation](#installation)
45
+ - [Usage](#usage)
46
+ - [Running in Docker](#running-in-docker)
47
+ - [Shell completion](#shell-completion)
48
+ - [How it works](#how-it-works)
49
+ - [Development](#development)
50
+ - [Security](#security)
51
+ - [License](#license)
52
+
53
+ ## Quick start
54
+
55
+ Options go after the subcommand (typer / git / kubectl convention).
56
+
57
+ ```sh
58
+ # Auto-detect distro, benchmark top 15 mirrors
59
+ mirror-bench bench
60
+
61
+ # List discovered mirrors without benchmarking
62
+ mirror-bench list
63
+
64
+ # Benchmark Ubuntu mirrors from the US and Canada
65
+ mirror-bench bench --distro ubuntu --country US,CA
66
+
67
+ # Only consider mirrors that serve HTTPS with TLS 1.3
68
+ mirror-bench bench --https-only --tls13-only
69
+
70
+ # Adjust scoring weights (auto-normalized)
71
+ mirror-bench bench --weights lat=0.6,thr=0.3,sec=0.1
72
+
73
+ # Machine-readable output for scripts
74
+ mirror-bench bench --json | jq '.results[0]'
75
+ mirror-bench bench --csv > mirrors.csv
76
+ ```
77
+
78
+ ## Installation
79
+
80
+ The PyPI distribution is **`linux-mirror-bench`** (the short `mirror-bench`
81
+ name was too similar to an existing project). The installed CLI is still
82
+ `mirror-bench`.
83
+
84
+ ### With `uv` (recommended)
85
+
86
+ ```sh
87
+ uv tool install linux-mirror-bench
88
+ mirror-bench --help
89
+ ```
90
+
91
+ ### With `pipx`
92
+
93
+ ```sh
94
+ pipx install linux-mirror-bench
95
+ mirror-bench --help
96
+ ```
97
+
98
+ ### One-shot via `uvx`
99
+
100
+ ```sh
101
+ uvx --from linux-mirror-bench mirror-bench bench --distro fedora
102
+ ```
103
+
104
+ ### From source
105
+
106
+ ```sh
107
+ git clone https://github.com/MysticRyuujin/mirror-bench
108
+ cd mirror-bench
109
+ uv sync
110
+ uv run mirror-bench bench
111
+ ```
112
+
113
+ ## Usage
114
+
115
+ ### Subcommands
116
+
117
+ | Command | What it does |
118
+ | -------------------- | ---------------------------------------------------------------- |
119
+ | `mirror-bench bench` | Discover, probe (two-phase), and print a ranked table. (default) |
120
+ | `mirror-bench list` | Discover only. Print the mirror roster as a table. |
121
+
122
+ Running `mirror-bench` with no subcommand is equivalent to `mirror-bench bench`.
123
+
124
+ ### Common flags
125
+
126
+ | Flag | Description |
127
+ | ----------------------------- | ------------------------------------------------------------------------------------------- |
128
+ | `--distro, -d NAME` | One of `ubuntu`, `debian`, `fedora`, `mint`, `arch`. |
129
+ | `--release, -r VALUE` | Release override: codename for apt (noble, bookworm, wilma) or numeric for Fedora (41, 42). |
130
+ | `--country, -c US,CA,GB` | ISO 3166-1 alpha-2 country codes to filter on. |
131
+ | `--top, -n N` | How many mirrors to include in phase 2 / display (default 15). |
132
+ | `--concurrency N` | Max parallel HTTP probes (default 20). |
133
+ | `--https-only` | Hard-exclude mirrors without HTTPS. |
134
+ | `--tls13-only` | Hard-exclude mirrors that don't negotiate TLS 1.3. |
135
+ | `--weights lat=…,thr=…,sec=…` | Override scoring weights (auto-normalized). |
136
+ | `--no-throughput` | Skip phase 2. Phase-1 latency screen only. |
137
+ | `--json` | Emit JSON to stdout. Suppresses the table + progress. |
138
+ | `--csv` | Emit CSV to stdout. Suppresses the table + progress. |
139
+
140
+ ## Running in Docker
141
+
142
+ A minimal multi-arch (linux/amd64 + linux/arm64) image is published to GHCR:
143
+
144
+ ```sh
145
+ docker pull ghcr.io/MysticRyuujin/mirror-bench:latest
146
+ ```
147
+
148
+ The image is built on `python:3.14-slim-bookworm`, runs as a non-root user
149
+ (`uid 65532`), and ships only the wheel + runtime deps (no uv, no sources,
150
+ no build chain). `ENTRYPOINT` is `mirror-bench`, so you can pass CLI args
151
+ directly.
152
+
153
+ > The image renders a colored, properly-widened table even without `-it`.
154
+ > If you want **rich to use your host terminal's exact width** (and react to
155
+ > resizes), add `-it`; otherwise the image falls back to a sensible 120-col
156
+ > table. Set `NO_COLOR=1` if you'd rather have plain text — e.g. when
157
+ > redirecting to a file.
158
+
159
+ ### Basic invocation
160
+
161
+ ```sh
162
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest --help
163
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest bench --distro ubuntu --country US
164
+ ```
165
+
166
+ ### Per-distribution examples
167
+
168
+ Pick a `--distro` — the image is the same for all of them.
169
+
170
+ ```sh
171
+ # Ubuntu — default per-country sweep if no --country provided
172
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
173
+ bench --distro ubuntu --country US,CA --top 10
174
+
175
+ # Debian
176
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
177
+ bench --distro debian --country DE,FR --https-only
178
+
179
+ # Fedora — release version inferred from defaults if not passed via --distro value
180
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
181
+ bench --distro fedora --tls13-only --top 10
182
+
183
+ # Linux Mint
184
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
185
+ bench --distro mint --top 15
186
+
187
+ # Arch Linux
188
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
189
+ bench --distro arch --country DE,NL,SE
190
+ ```
191
+
192
+ ### Machine-readable output
193
+
194
+ ```sh
195
+ docker run --rm ghcr.io/MysticRyuujin/mirror-bench:latest \
196
+ bench --distro arch --json | jq '.results[] | {host: .mirror.host, latency_ms, score: .composite}'
197
+ ```
198
+
199
+ ### Running with auto-detection inside distro base images
200
+
201
+ If you'd rather let `mirror-bench` auto-detect the host distribution (useful in
202
+ CI containers or inside a running container of a specific distro):
203
+
204
+ ```sh
205
+ # Inside an Ubuntu container — distro auto-detected from /etc/os-release
206
+ docker run --rm --entrypoint bash ubuntu:24.04 -c \
207
+ "apt-get update -qq && apt-get install -y -qq python3.14 pipx && pipx install linux-mirror-bench && mirror-bench bench"
208
+
209
+ # Inside a Fedora container
210
+ docker run --rm --entrypoint bash fedora:41 -c \
211
+ "dnf install -y python3.14 pipx && pipx install linux-mirror-bench && mirror-bench bench"
212
+
213
+ # Inside an Arch container
214
+ docker run --rm --entrypoint bash archlinux:latest -c \
215
+ "pacman -Sy --noconfirm python python-pipx && pipx install linux-mirror-bench && mirror-bench bench"
216
+ ```
217
+
218
+ For most uses the `ghcr.io/MysticRyuujin/mirror-bench` image is simpler — it already
219
+ has the CLI and lets you benchmark _any_ distro via `--distro`.
220
+
221
+ ### Building the image yourself
222
+
223
+ The `Dockerfile` is a two-stage build:
224
+
225
+ 1. **Builder** — `python:3.14-slim-bookworm` + the pinned `uv` from
226
+ `ghcr.io/astral-sh/uv`. Generates the wheel, then installs it into a
227
+ self-contained virtualenv at `/opt/venv`.
228
+ 2. **Runtime** — fresh `python:3.14-slim-bookworm`, copies only `/opt/venv`
229
+ from the builder, refreshes the CA bundle, creates a non-root user (`uid
230
+ 65532`), and sets `mirror-bench` as the `ENTRYPOINT`.
231
+
232
+ No dev dependencies, source code, or uv binary lands in the final image.
233
+
234
+ #### Standard local build
235
+
236
+ ```sh
237
+ git clone https://github.com/MysticRyuujin/mirror-bench && cd mirror-bench
238
+
239
+ # Shortest path — Makefile shortcut tags as `mirror-bench:dev`.
240
+ make docker-build
241
+ docker run --rm mirror-bench:dev bench --distro arch --top 5
242
+
243
+ # Or run `docker build` directly for full control over tags and args.
244
+ docker build -t mirror-bench:local .
245
+ docker run --rm mirror-bench:local --help
246
+ ```
247
+
248
+ #### Build args
249
+
250
+ All arguments are optional; defaults match what CI ships.
251
+
252
+ | Arg | Default | Purpose |
253
+ | ---------------- | -------- | ------------------------------------------------------------------- |
254
+ | `PYTHON_VERSION` | `3.14` | Base image tag (`python:<PYTHON_VERSION>-slim-bookworm`). |
255
+ | `UV_VERSION` | `0.11.7` | uv release pulled from `ghcr.io/astral-sh/uv:<UV_VERSION>`. |
256
+ | `VERSION` | `0.1.0` | Value embedded in the `org.opencontainers.image.version` OCI label. |
257
+
258
+ Example:
259
+
260
+ ```sh
261
+ docker build \
262
+ --build-arg VERSION="$(uv version --short)" \
263
+ --build-arg PYTHON_VERSION=3.14 \
264
+ --build-arg UV_VERSION=0.11.7 \
265
+ -t mirror-bench:"$(uv version --short)" \
266
+ -t mirror-bench:latest \
267
+ .
268
+ ```
269
+
270
+ #### Multi-arch (amd64 + arm64) build via buildx
271
+
272
+ The released image on GHCR is built for both architectures. To produce the
273
+ same locally (useful for testing on Apple Silicon before publishing):
274
+
275
+ ```sh
276
+ docker buildx create --name mb --use --bootstrap # one-time
277
+ docker buildx build \
278
+ --platform linux/amd64,linux/arm64 \
279
+ --build-arg VERSION="$(uv version --short)" \
280
+ -t mirror-bench:multiarch \
281
+ --load . # or --push for a registry
282
+ ```
283
+
284
+ `--load` only supports a single platform; use `--push` (against a registry
285
+ you control) to actually materialize a multi-arch manifest.
286
+
287
+ #### Inspecting the result
288
+
289
+ ```sh
290
+ docker image inspect mirror-bench:local \
291
+ --format '{{json .Config.Labels}}' | jq # OCI labels
292
+ docker image ls mirror-bench:local # size (~170 MB)
293
+ docker run --rm mirror-bench:local list --distro arch --json | jq '.mirrors | length'
294
+ ```
295
+
296
+ #### Makefile shortcuts
297
+
298
+ ```sh
299
+ make docker-build # docker build -t mirror-bench:dev .
300
+ make docker-test # build + run --help + `list --distro arch --json` smoke test
301
+ ```
302
+
303
+ ## Shell completion
304
+
305
+ `mirror-bench` ships completion scripts for **bash**, **zsh**, and **fish**.
306
+ Two paths, depending on whether shell auto-detection works in your environment:
307
+
308
+ ### Recommended: the `completion` subcommand (no auto-detect)
309
+
310
+ Always works, including in containers, CI, and sandboxed shells:
311
+
312
+ ```sh
313
+ # bash
314
+ mirror-bench completion bash > ~/.local/share/bash-completion/completions/mirror-bench
315
+
316
+ # zsh — make sure `~/.zfunc` is on your fpath
317
+ mirror-bench completion zsh > ~/.zfunc/_mirror-bench
318
+
319
+ # fish
320
+ mirror-bench completion fish > ~/.config/fish/completions/mirror-bench.fish
321
+ ```
322
+
323
+ Restart your shell (or `source` the file) and completion is live.
324
+
325
+ ### Auto-install via typer (when run in a real terminal)
326
+
327
+ ```sh
328
+ mirror-bench --install-completion # typer detects the shell and installs
329
+ mirror-bench --show-completion # prints the script for the detected shell
330
+ ```
331
+
332
+ This relies on [shellingham](https://github.com/sarugaku/shellingham) to
333
+ detect your shell from the parent process. It works in normal interactive
334
+ shells but can fail inside `uv run`, nested sandboxes, or minimal containers —
335
+ use the `completion` subcommand in those cases.
336
+
337
+ ### After install
338
+
339
+ Tab-complete subcommands, options, and their values:
340
+
341
+ ```text
342
+ $ mirror-bench <TAB>
343
+ bench list completion
344
+ $ mirror-bench bench --<TAB>
345
+ --distro --country --top --concurrency --https-only --tls13-only …
346
+ ```
347
+
348
+ ## How it works
349
+
350
+ 1. **Discover** — fetch the canonical mirror list for the target distribution:
351
+ - Ubuntu: `mirrors.ubuntu.com/<CC>.txt` swept across a default country
352
+ set (`mirrors.ubuntu.com/mirrors.txt` returns a geo-selected _single_
353
+ mirror, not a full list — so we don't rely on it alone).
354
+ - Debian: `mirror-master.debian.org/status/Mirrors.masterlist` (RFC822
355
+ multi-record format).
356
+ - Fedora: `mirrors.fedoraproject.org/mirrorlist?repo=&arch=&country=`.
357
+ - Linux Mint: scraped from `linuxmint.com/mirrors.php`.
358
+ - Arch: `archlinux.org/mirrors/status/json/` (JSON, filtered to active).
359
+ 2. **Phase 1 — latency screen.** Concurrent ranged GET (`bytes=0-1023`) on the
360
+ distro's canonical small metadata file (`InRelease`, `repomd.xml`,
361
+ `lastsync`). Three samples per mirror, **median TTFB** used for ranking.
362
+ 3. **Phase 2 — throughput test.** Full streaming GET of a representative file
363
+ (`Contents-<arch>.gz` for apt, `core.db` for Arch, etc.) on the top N from
364
+ phase 1, capped at 5 MiB. `bytes_per_sec` is measured from first-byte to
365
+ end-of-stream so steady-state bandwidth isn't polluted by connect/TLS
366
+ overhead.
367
+ 4. **Score.** Weighted composite of normalized latency (inverse), throughput,
368
+ and security (HTTPS + TLS 1.3 + cert-valid + HTTP/2, max 3.5). Weights
369
+ default to `lat=0.4, thr=0.4, sec=0.2` and are user-overridable.
370
+ 5. **Render.** A `rich.Table`, or JSON / CSV for scripting.
371
+
372
+ ## Development
373
+
374
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full dev loop.
375
+
376
+ ```sh
377
+ make install # uv sync --all-extras
378
+ make hooks # install pre-commit + commit-msg hooks
379
+ make ci-local # lint + typecheck + tests + security + docker-test
380
+ ```
381
+
382
+ Integration tests (opt-in, real network):
383
+
384
+ ```sh
385
+ MIRROR_BENCH_INTEGRATION=1 uv run pytest -m integration
386
+ ```
387
+
388
+ ## Security
389
+
390
+ Vulnerability reports — see [`SECURITY.md`](SECURITY.md) for the private
391
+ disclosure process. **Please do not open public issues for security problems.**
392
+
393
+ Supply-chain guarantees (starting at 0.1.0):
394
+
395
+ - Releases published via GitHub Actions OIDC Trusted Publishing to PyPI — no
396
+ long-lived tokens.
397
+ - Wheels and container images carry **SLSA build provenance** attestations.
398
+ - Container image published to `ghcr.io/MysticRyuujin/mirror-bench` with SBOM
399
+ generated at build.
400
+ - `uv.lock` pins dependency graph; Dependabot monitors it weekly.
401
+
402
+ ## License
403
+
404
+ [GNU Affero General Public License v3.0 or later](LICENSE) (AGPL-3.0-or-later).
405
+
406
+ The AGPL requires that anyone who runs a modified version of `mirror-bench`
407
+ accessible over a network (for example, as part of a hosted service) must make
408
+ the modified source available to the users of that service. If you are
409
+ embedding or redistributing `mirror-bench`, read the [license text](LICENSE)
410
+ for the full terms.
@@ -0,0 +1,23 @@
1
+ mirror_bench/__init__.py,sha256=2BiPAwZu04kd5049KF5QA9bNnWEYnlBMNQWljhOiKco,100
2
+ mirror_bench/__main__.py,sha256=QtELjkFNtBwBXbzHQix3AZZfBQcvIsm4WQtXIPT9gBQ,71
3
+ mirror_bench/cli.py,sha256=QxoZ6iW8zCIqkHIv5sMqTWunjQUZVHJXg8al22Rpp10,10822
4
+ mirror_bench/display.py,sha256=zKLDgN7fQyy6ANxxuT96oqdLtv_UFw-fO923TOZzyGU,7944
5
+ mirror_bench/distro.py,sha256=11EMrV6KsHtahVRNFidYZ1U3KttBH-xDihUQdFU6NqE,5660
6
+ mirror_bench/models.py,sha256=KQdO-Nv0vpXJ20LBiTC8wi5XulPnfN8pJ8rKFMn1TrE,2625
7
+ mirror_bench/benchmark/__init__.py,sha256=wDElBCKtoOdEXA3qA35UaPsaBAzZHTE7JRh0QY60xtE,44
8
+ mirror_bench/benchmark/probe.py,sha256=3o1hEU-FgUaa1sUoa6KMFND482WqJ16K3cq8PFOuJC8,8081
9
+ mirror_bench/benchmark/scorer.py,sha256=eBtJGI347YjwpFcK4GQd57lw89XJLMD26nKVS3zLSc0,4214
10
+ mirror_bench/benchmark/tls.py,sha256=Yf4Y1s4ePkPK0-FYlWf4UWNRpXeEnEqR7Djz8BARmxY,1063
11
+ mirror_bench/benchmark/transport.py,sha256=6AuLk9H9nbOwuWuSAgWajNU0E3Wr_CCAJs0X8Sb2QWI,1195
12
+ mirror_bench/discovery/__init__.py,sha256=Kus3lWSIVA6SL0wKrzM2rUydm0cCOUnx1_fu8ZuaAOw,1298
13
+ mirror_bench/discovery/arch.py,sha256=pas81Dmn4Dxp9BP-40LRgz1MF8c9S9_pAARfSQEFi8A,1859
14
+ mirror_bench/discovery/base.py,sha256=ryi08l9IglwHJbo_6MekBCePvOMRW6sldlm_honjM-M,1182
15
+ mirror_bench/discovery/debian.py,sha256=RApHRflgvGrSsw8wR-V07tSU5zMQ2zcflX1VgTuaHqc,2766
16
+ mirror_bench/discovery/fedora.py,sha256=mUU2-VBNBrUwtdY6gQc5vOUVJMn05o2vCsVuv1-VjcI,5770
17
+ mirror_bench/discovery/mint.py,sha256=tjsx1tS1OG2nkKH25dQBERTz9_00-yLSgiMnKxDgNgc,3450
18
+ mirror_bench/discovery/ubuntu.py,sha256=uK8fvnXRK83JD4df2_O39P1X9EAyvLkla0lZs0oMxFg,3078
19
+ linux_mirror_bench-0.1.1.dist-info/METADATA,sha256=p0gnIMt4MUu-hg5APjikOlnQEQxSZsvw9yeQlrW3xB0,15352
20
+ linux_mirror_bench-0.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
21
+ linux_mirror_bench-0.1.1.dist-info/entry_points.txt,sha256=NNOkAi27ulzfdRZreHCLyJOaxTKsVlEd5cAcDNTPx9s,54
22
+ linux_mirror_bench-0.1.1.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
23
+ linux_mirror_bench-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mirror-bench = mirror_bench.cli:app