Add Debian, Ubuntu and Mint candidate profiles with scoped systemd 255 credentials
This commit is contained in:
1 parent
b23819fff1
commit
a48a20c936
28 files changed
+2527
-274
No files matched your search
@@ -0,0 +1,145 @@
|
||||
"""Explicit candidate Linux profiles and installed-artifact ABI checks.
|
||||
|
||||
Eligibility is not evidence of a successful installation on a target system.
|
||||
Never import vendored native code merely to diagnose an incompatible artifact.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROFILES = {
|
||||
"debian13-amd64-py313": ("debian", "13", "3.13", "cp313"),
|
||||
"ubuntu24.04-amd64-py312": ("ubuntu", "24.04", "3.12", "cp312"),
|
||||
"ubuntu26.04-amd64-py314": ("ubuntu", "26.04", "3.14", "cp314"),
|
||||
"mint22-amd64-py312": ("linuxmint", "22", "3.12", "cp312"),
|
||||
"lmde7-amd64-py313": ("linuxmint", "7", "3.13", "cp313"),
|
||||
}
|
||||
SYSTEM_PYTHONS = frozenset({"/usr/bin/python3", "/usr/bin/python3.12",
|
||||
"/usr/bin/python3.13", "/usr/bin/python3.14"})
|
||||
INSTALLED_PREFIX = Path("/usr/lib/framebridge-expanded")
|
||||
VENDOR_RELATIVE = Path("original/geranas/FrameBridge-Fleet-Hub/vendor")
|
||||
|
||||
|
||||
def profile_for(os_release):
|
||||
"""Match named releases only; ID_LIKE never opts other derivatives in."""
|
||||
distro = os_release.get("ID", "")
|
||||
version = os_release.get("VERSION_ID", "")
|
||||
for name, (expected, release, _minor, _abi) in PROFILES.items():
|
||||
matches = version == release
|
||||
if name == "mint22-amd64-py312":
|
||||
matches = bool(re.fullmatch(r"22(?:\.\d+)?", version))
|
||||
if distro == expected and matches:
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
def _libc():
|
||||
try:
|
||||
value = os.confstr("CS_GNU_LIBC_VERSION") or ""
|
||||
if value.startswith("glibc "):
|
||||
return "glibc", value.split()[-1]
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
return platform.libc_ver()
|
||||
|
||||
|
||||
def _vendor_issues(prefix, abi):
|
||||
vendor = prefix / VENDOR_RELATIVE
|
||||
issues = []
|
||||
expected = abi.removeprefix("cp")
|
||||
if not vendor.is_dir():
|
||||
return ["Bundled vendor directory is missing; use a complete artifact for this profile."]
|
||||
required = {
|
||||
"_cffi_backend": list(vendor.glob("_cffi_backend.cpython-*.so")),
|
||||
"pydantic_core": list((vendor / "pydantic_core").glob("_pydantic_core.cpython-*.so")),
|
||||
"rpds": list((vendor / "rpds").glob("rpds.cpython-*.so")),
|
||||
}
|
||||
for module, files in required.items():
|
||||
if len(files) != 1:
|
||||
issues.append("Expected exactly one bundled native extension for " + module + ".")
|
||||
for path in vendor.rglob("*.so"):
|
||||
match = re.search(r"\.cpython-([^-]+)-", path.name)
|
||||
if match and (match.group(1) != expected or "x86_64-linux-gnu" not in path.name):
|
||||
issues.append("Bundled native extension does not match " + abi + " amd64: " + str(path.relative_to(vendor)))
|
||||
return issues
|
||||
|
||||
|
||||
def assess(prefix, *, os_release=None, machine=None, python_version=None, libc=None, implementation=None, abi_flags=None):
|
||||
"""Check profile and artifact identity without importing native modules."""
|
||||
prefix = Path(prefix)
|
||||
issues = []
|
||||
warnings = []
|
||||
if os_release is None:
|
||||
try:
|
||||
os_release = platform.freedesktop_os_release()
|
||||
except OSError:
|
||||
os_release = {}
|
||||
machine = platform.machine() if machine is None else machine
|
||||
version = sys.version_info[:2] if python_version is None else python_version[:2]
|
||||
minor = ".".join(str(x) for x in version)
|
||||
abi = "cp" + "".join(str(x) for x in version)
|
||||
implementation = platform.python_implementation() if implementation is None else implementation
|
||||
abi_flags = getattr(sys, "abiflags", "") if abi_flags is None else abi_flags
|
||||
if implementation != "CPython" or abi_flags:
|
||||
issues.append("This artifact requires standard CPython; alternate, debug or free-threaded interpreter ABIs need separate native artifacts.")
|
||||
selected = profile_for(os_release)
|
||||
if selected is None:
|
||||
issues.append("No candidate profile for this release. Choose Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7.")
|
||||
if machine not in {"x86_64", "amd64"}:
|
||||
issues.append("This artifact supports amd64 only; ARM and other architectures need separate artifacts.")
|
||||
if selected and minor != PROFILES[selected][2]:
|
||||
issues.append("Profile " + selected + " requires Python " + PROFILES[selected][2] + "; running " + minor + ". Install its matching artifact without replacing the system Python.")
|
||||
libc_name, libc_version = _libc() if libc is None else libc
|
||||
match = re.match(r"(\d+)\.(\d+)", libc_version)
|
||||
if libc_name != "glibc" or not match or tuple(map(int, match.groups())) < (2, 34):
|
||||
issues.append("Bundled native libraries require glibc >= 2.34; this libc is not eligible.")
|
||||
manifest = None
|
||||
manifest_path = prefix / "release-profile.json"
|
||||
if manifest_path.exists():
|
||||
try:
|
||||
if manifest_path.stat().st_size > 16384:
|
||||
raise ValueError("manifest too large")
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
if not isinstance(manifest, dict) or type(manifest.get("schema")) is not int or manifest["schema"] != 1:
|
||||
raise ValueError("unsupported schema")
|
||||
if manifest.get("profile_id") not in PROFILES:
|
||||
raise ValueError("unknown profile")
|
||||
expected = PROFILES[manifest["profile_id"]]
|
||||
if (manifest.get("architecture"), manifest.get("python_minor"), manifest.get("python_abi")) != ("amd64", expected[2], expected[3]):
|
||||
raise ValueError("inconsistent profile ABI")
|
||||
if manifest.get("validation_status") not in {"candidate", "validated"}:
|
||||
raise ValueError("missing validation status")
|
||||
if manifest["profile_id"] != selected or manifest["python_minor"] != minor or manifest["python_abi"] != abi:
|
||||
issues.append("Installed artifact profile/ABI does not match this operating system and interpreter.")
|
||||
except (OSError, UnicodeError, ValueError, TypeError):
|
||||
issues.append("Invalid release-profile.json; install a complete matching artifact.")
|
||||
manifest = None
|
||||
else:
|
||||
# Source-tree fallback is intentionally the reviewed release's ABI only.
|
||||
if prefix.resolve() == INSTALLED_PREFIX or selected != "debian13-amd64-py313" or minor != "3.13":
|
||||
issues.append("Missing release-profile.json; this target requires a profile-specific artifact.")
|
||||
else:
|
||||
warnings.append("Source checkout has no artifact manifest; only the legacy Debian 13/cp313 profile is eligible.")
|
||||
issues.extend(_vendor_issues(prefix, abi))
|
||||
validation = manifest.get("validation_status") if manifest else "source-unvalidated"
|
||||
if validation != "validated":
|
||||
warnings.append("Candidate compatibility checks do not establish a tested installation on this target.")
|
||||
return {"ok": not issues, "profile_id": selected, "artifact_profile_id": manifest.get("profile_id") if manifest else None,
|
||||
"architecture": machine, "python_minor": minor, "python_abi": abi, "python_implementation": implementation, "python_abi_flags": abi_flags,
|
||||
"libc": {"name": libc_name, "version": libc_version}, "validation_status": validation,
|
||||
"issues": issues, "warnings": warnings}
|
||||
|
||||
|
||||
def display_support(env, context):
|
||||
wayland = env.get("XDG_SESSION_TYPE", "").lower() == "wayland" or bool(env.get("WAYLAND_DISPLAY"))
|
||||
notes = []
|
||||
if wayland:
|
||||
notes.append("Native Wayland desktop capture/control is not implemented. XWayland does not establish access to the full Wayland desktop; text tools remain available, or explicitly select a private virtual X11 display.")
|
||||
return {"session_type": "wayland" if wayland else env.get("XDG_SESSION_TYPE", "unknown"),
|
||||
"physical_x11_verified": context.get("kind") == "physical-x11",
|
||||
"private_virtual_x11": context.get("kind") == "virtual-x11",
|
||||
"native_wayland_cua": False, "notes": notes}
|
||||
Reference in new issue
Block a user