feat(termux): pinned termux toolchain via pm (linux-arm64-bionic)
A seventh pm target (linux-arm64-bionic) stages the TUR python3.11 .deb, the termux nodejs/uv .debs, and their runtime-lib deps into the payload -- same pm-consumer shape as the desktop legs: pm owns the pin, the hardened download (redirect-safe, retry-wrapped), the ar+tar DebPackage extraction, and file-evidence verify for binaries the staging host cannot execute.
This commit is contained in:
108
pm/packages.py
108
pm/packages.py
@@ -11,6 +11,7 @@ from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from pm.package import (
|
||||
DebPackage,
|
||||
InstallError,
|
||||
Package,
|
||||
StatePackage,
|
||||
@@ -115,17 +116,64 @@ class BinaryPackage(Package):
|
||||
return env_for(*self.deps)
|
||||
|
||||
|
||||
class _BionicDebArm:
|
||||
"""Shared bionic-arm behavior for the termux tool packages: extract as a
|
||||
.deb on the bionic target (DebPackage's hardened ar+tar), as the binary
|
||||
package otherwise, and never host-exec-probe a bionic binary (it cannot
|
||||
run on the staging host)."""
|
||||
|
||||
def unpack(self, archive: Path, staged: Path, target: str) -> None:
|
||||
if target == "linux-arm64-bionic":
|
||||
DebPackage.unpack(self, archive, staged, target)
|
||||
else:
|
||||
BinaryPackage.unpack(self, archive, staged, target)
|
||||
|
||||
def binary(self, entry: Path, target: str) -> Optional[Path]:
|
||||
if target == "linux-arm64-bionic":
|
||||
return None # bionic binaries cannot exec on the staging host
|
||||
return BinaryPackage.binary(self, entry, target)
|
||||
|
||||
def verify(self, entry: Path, target: str) -> str:
|
||||
# MRO order puts BinaryPackage.verify (exec-probe semantics) ahead of
|
||||
# DebPackage.verify (file-evidence semantics); bionic needs the
|
||||
# latter -- one dispatch here replaces the per-class copies.
|
||||
if target == "linux-arm64-bionic":
|
||||
return DebPackage.verify(self, entry, target)
|
||||
return BinaryPackage.verify(self, entry, target)
|
||||
|
||||
|
||||
@register
|
||||
class Uv(BinaryPackage):
|
||||
class Uv(_BionicDebArm, BinaryPackage, DebPackage):
|
||||
"""astral's prebuilt tarballs for glibc/mac/win; the Termux main-repo
|
||||
uv .deb for bionic (termux builds uv from source -- no astral bionic
|
||||
artifact exists). The bionic arm is a runtime tool on the phone (lazy
|
||||
plugin installs) and the wheelhouse's resolver in the build container."""
|
||||
|
||||
name = "uv"
|
||||
internal = True
|
||||
binary_rel = {"win32": "uv.exe", "posix": "uv"}
|
||||
# The staged .deb's main binary: DebPackage.verify checks it.
|
||||
main_bin_rel = "bin/uv"
|
||||
|
||||
def main_rel(self, target: str) -> str:
|
||||
return self.main_bin_rel
|
||||
|
||||
deb_package = "uv"
|
||||
|
||||
def fetch_url(self, version: str, target: str) -> str:
|
||||
if target == "linux-arm64-bionic":
|
||||
return f"https://packages.termux.dev/apt/termux-main/pool/main/u/uv/uv_{version}_aarch64.deb"
|
||||
triple = _RUST_TRIPLE[target]
|
||||
ext = "zip" if target.startswith("win32") else "tar.gz"
|
||||
return f"https://github.com/astral-sh/uv/releases/download/{version}/uv-{triple}.{ext}"
|
||||
|
||||
def env(self, entry: Path, target: str) -> dict:
|
||||
"""The bionic arm exposes uv through PATH composition -- the same
|
||||
mechanism every other pm package uses. No system PATH install."""
|
||||
if target == "linux-arm64-bionic":
|
||||
return {"PATH": str(entry / self.prefix_rel / "bin")}
|
||||
return BinaryPackage.env(self, entry, target)
|
||||
|
||||
def latest_versions(self, target: str, locked=None) -> list[str]:
|
||||
return github_release_tags("astral-sh/uv")
|
||||
|
||||
@@ -179,7 +227,7 @@ def _macos_sign_managed_python(python: Path) -> bool:
|
||||
|
||||
|
||||
@register
|
||||
class Python(BinaryPackage):
|
||||
class Python(_BionicDebArm, BinaryPackage, DebPackage):
|
||||
"""The payload interpreter (python-build-standalone install_only).
|
||||
Optional: dev installs use their own venv's python; bundles stage this
|
||||
and point the relocatable venv's pyvenv.cfg at it (pm adopt)."""
|
||||
@@ -188,6 +236,13 @@ class Python(BinaryPackage):
|
||||
optional = True
|
||||
probe_version = False
|
||||
binary_rel = {"win32": "python.exe", "posix": "bin/python3"}
|
||||
# The staged .deb's main binary: DebPackage.verify checks it.
|
||||
main_bin_rel = "bin/python3.11"
|
||||
|
||||
def main_rel(self, target: str) -> str:
|
||||
return self.main_bin_rel
|
||||
|
||||
deb_package = "python3.11"
|
||||
|
||||
def stage(self, store: Store, staged: Path, version: str, target: str) -> None:
|
||||
super().stage(store, staged, version, target)
|
||||
@@ -196,6 +251,9 @@ class Python(BinaryPackage):
|
||||
_macos_sign_managed_python(binary)
|
||||
|
||||
def fetch_url(self, version: str, target: str) -> str:
|
||||
if target == "linux-arm64-bionic":
|
||||
pyver = version.partition("+")[0]
|
||||
return f"https://tur.kcubeterm.com/pool/tur/python3.11_{pyver}_aarch64.deb"
|
||||
# lock version is "<python>+<release tag>", e.g. "3.11.13+202****0807"
|
||||
pyver, _, tag = version.partition("+")
|
||||
if not tag:
|
||||
@@ -482,15 +540,58 @@ class Venv(StatePackage):
|
||||
|
||||
|
||||
@register
|
||||
class Nodejs(BinaryPackage):
|
||||
class Nodejs(_BionicDebArm, BinaryPackage, DebPackage):
|
||||
"""nodejs.org tarballs for glibc/mac/win; the Termux main-repo nodejs
|
||||
.deb for bionic (same major line, termux-built)."""
|
||||
|
||||
name = "node"
|
||||
binary_rel = {"win32": "node.exe", "posix": "bin/node"}
|
||||
# The staged .deb's main binary: DebPackage.verify checks it.
|
||||
main_bin_rel = "bin/node"
|
||||
|
||||
def main_rel(self, target: str) -> str:
|
||||
return self.main_bin_rel
|
||||
|
||||
deb_package = "nodejs"
|
||||
|
||||
def fetch_url(self, version: str, target: str) -> str:
|
||||
if target == "linux-arm64-bionic":
|
||||
# termux's deb carries a -1 revision after the upstream version
|
||||
return f"https://packages.termux.dev/apt/termux-main/pool/main/n/nodejs/nodejs_{version}-1_aarch64.deb"
|
||||
plat = _NODE_PLAT[target]
|
||||
ext = "zip" if target.startswith("win32") else "tar.xz"
|
||||
return f"https://nodejs.org/dist/v{version}/node-v{version}-{plat}.{ext}"
|
||||
|
||||
@register
|
||||
class TermuxDocker(Package):
|
||||
"""The termux/termux-docker container image, pinned by registry digest.
|
||||
|
||||
The image is never downloaded or unpacked by pm -- docker pulls it by
|
||||
digest reference at build time. The lock row exists so the digest is
|
||||
pinned in the single pin authority beside every other third-party
|
||||
artifact: consumers read the digest string from the lock's url field
|
||||
(termux/termux-docker@sha256:...). verify() is presence-shaped: this
|
||||
package stages nothing.
|
||||
"""
|
||||
|
||||
name = "termux-docker"
|
||||
optional = True
|
||||
# Pure pin: no bytes are staged, so stage_only()/install skip the store
|
||||
# entirely -- the digest's consumers (docker pull) verify it.
|
||||
pin_only = True
|
||||
|
||||
def missing_reason(self, target: str) -> Optional[str]:
|
||||
return None if target == "linux-arm64-bionic" else "docker image target is linux-arm64-bionic"
|
||||
|
||||
def fetch_url(self, version: str, target: str) -> str:
|
||||
return f"docker://termux/termux-docker@{version}"
|
||||
|
||||
def unpack(self, archive: Path, staged: Path, target: str) -> None:
|
||||
raise InstallError(self.name, "a docker image digest is a pin, not a downloadable artifact")
|
||||
|
||||
def verify(self, entry: Path, target: str) -> str:
|
||||
return ""
|
||||
|
||||
def latest_versions(self, target: str, locked=None) -> list[str]:
|
||||
return node_latest_versions()
|
||||
|
||||
@@ -704,7 +805,6 @@ class Ffmpeg(BinaryPackage):
|
||||
)
|
||||
|
||||
|
||||
|
||||
@register
|
||||
class Ripgrep(BinaryPackage):
|
||||
name = "ripgrep"
|
||||
|
||||
Reference in New Issue
Block a user