Ship the reader wheels inside the package for offline install

A device with no internet could not set itself up: the auto-install needed
PyPI. The rdm6300 and pyserial wheels now ship inside the package, so first
start works with no network at all.

Both wheels are pure Python (py3-none-any / py2.py3-none-any), which is what
makes this possible - the same files work on armv7l, aarch64 and x86_64. This
is the difference from the workstation app's Files/repository cache, which
held aarch64 manylinux binaries and so could not be committed; that cache only
ever existed on the Pi devices.

- bundled_wheel_dir() locates them; ensure_dependencies() prefers them
  automatically, so the service needs no --wheel-dir.
- verify_wheels() checks SHA256SUMS before installing. A wheel that fails its
  checksum is refused rather than installed, then PyPI is tried, and if that is
  unreachable it reports exit 2 instead of proceeding unverified.
- local_wheel_missing() falls back to PyPI when the bundle does not cover a
  requirement, instead of attempting a --no-index install that must fail.
- An explicit --wheel-dir that is incomplete or unverified is called out, not
  silently swapped for PyPI.

Verified end to end: built wheel and sdist both contain the wheels; a venv
with no rdm6300 self-provisions with a dead proxy and no PIP_NO_INDEX; and a
tampered wheel is refused with nothing installed.
This commit is contained in:
2026-10-01 15:48:34 +03:00
parent eaf3f394fc
commit 23cd8f2df5
9 changed files with 413 additions and 21 deletions
+27 -3
View File
@@ -7,6 +7,7 @@ clearly instead of crashing.
import os
import sys
import tempfile
SRC = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "src"
@@ -161,10 +162,11 @@ def test_no_install_flag_reports_manual_hint(monkeypatch):
def test_wheel_dir_is_passed_through(monkeypatch):
"""An explicit --wheel-dir that covers everything is used as given."""
seen = {}
monkeypatch.setattr(
bootstrap, "missing_packages",
lambda packages=None: [] if seen.get("done") else [("x", "x")],
lambda packages=None: [] if seen.get("done") else [("x", "x>=1")],
)
def fake_install(requirements, wheel_dir=None, logger=None, timeout=300):
@@ -173,5 +175,27 @@ def test_wheel_dir_is_passed_through(monkeypatch):
return True
monkeypatch.setattr(bootstrap, "install", fake_install)
ensure_dependencies(wheel_dir="/opt/wheels")
assert seen["wheel_dir"] == "/opt/wheels"
with tempfile.TemporaryDirectory() as folder:
open(os.path.join(folder, "x-1.0-py3-none-any.whl"), "wb").close()
ensure_dependencies(wheel_dir=folder)
assert seen["wheel_dir"] == folder
def test_incomplete_explicit_wheel_dir_is_flagged(monkeypatch):
"""An unusable --wheel-dir should be called out, not silently ignored."""
seen = {}
monkeypatch.setattr(
bootstrap, "missing_packages",
lambda packages=None: [] if seen.get("done") else [("x", "x>=1")],
)
def fake_install(requirements, wheel_dir=None, logger=None, timeout=300):
seen["done"] = True
return True
monkeypatch.setattr(bootstrap, "install", fake_install)
lines = []
with tempfile.TemporaryDirectory() as folder:
ensure_dependencies(wheel_dir=folder, logger=lines.append)
assert any("you gave is incomplete" in line for line in lines)