Package onboard K1 separately with a private portable installer
This commit is contained in:
@@ -4,16 +4,17 @@
|
||||
No install operation, sudo, container, package-manager mutation or network I/O.
|
||||
"""
|
||||
import argparse
|
||||
import gzip
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tarfile
|
||||
import sys
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
VERSION = "0.7.1"
|
||||
VERSION = "0.8.0"
|
||||
sys.path.insert(0, str(ROOT.parents[1] / "scripts/packaging"))
|
||||
from debian import package
|
||||
|
||||
BRAND_SHA256 = "8bfee8ca9f98e0db48d98aae3af4b32493b8593e18b064a0239d513d824182af"
|
||||
|
||||
|
||||
@@ -31,29 +32,6 @@ def desktop_icon(brand):
|
||||
+ brand + b'</svg>\n')
|
||||
|
||||
|
||||
def tarball(files):
|
||||
stream = io.BytesIO()
|
||||
with tarfile.open(fileobj=stream, mode="w", format=tarfile.GNU_FORMAT) as archive:
|
||||
directories = {str(parent) for name, _, _ in files for parent in Path(name).parents if str(parent) != "."}
|
||||
for name in sorted(directories):
|
||||
item = tarfile.TarInfo(name + "/")
|
||||
item.type, item.mode = tarfile.DIRTYPE, 0o755
|
||||
item.uname = item.gname = "root"
|
||||
archive.addfile(item)
|
||||
for name, data, mode in sorted(files):
|
||||
item = tarfile.TarInfo(name)
|
||||
item.size, item.mode, item.uid, item.gid = len(data), mode, 0, 0
|
||||
item.uname = item.gname = "root"
|
||||
archive.addfile(item, io.BytesIO(data))
|
||||
return gzip.compress(stream.getvalue(), mtime=0)
|
||||
|
||||
|
||||
def ar_member(name, data):
|
||||
header = f"{name + '/':<16}{0:<12}{0:<6}{0:<6}{'100644':<8}{len(data):<10}`\n".encode()
|
||||
assert len(header) == 60
|
||||
return header + data + (b"\n" if len(data) % 2 else b"")
|
||||
|
||||
|
||||
def build(binary, destination):
|
||||
payload = binary.read_bytes()
|
||||
if payload[:4] != b"\x7fELF" or payload[4:6] != b"\x02\x01" or payload[18:20] != b"\x3e\x00":
|
||||
@@ -65,7 +43,7 @@ Architecture: amd64
|
||||
Maintainer: NODE.DC local build <noreply@example.invalid>
|
||||
Section: admin
|
||||
Priority: optional
|
||||
Depends: adduser, systemd, python3, python3-gi, gir1.2-gtk-3.0, gir1.2-webkit2-4.1, pkexec, polkitd, ca-certificates, hicolor-icon-theme, bluez, network-manager, iproute2, ffmpeg
|
||||
Depends: adduser, systemd, python3, python3-gi, gir1.2-gtk-3.0, gir1.2-webkit2-4.1, pkexec, polkitd, ca-certificates, hicolor-icon-theme, network-manager, iproute2
|
||||
Description: Mission Core onboard computer configuration
|
||||
Local graphical setup, host inventory, SSH access and persistent node identity.
|
||||
""".encode()
|
||||
@@ -107,35 +85,12 @@ Description: Mission Core onboard computer configuration
|
||||
files.append(("usr/share/mission-core-node/realsense/" + item["name"], data, 0o644))
|
||||
for path in (ROOT / "sensors").glob("*.py"):
|
||||
files.append(("usr/lib/mission-core-node/sensors/" + path.name, path.read_bytes(), 0o644))
|
||||
for name in ("k1_prepare.py", "k1_bootstrap.py"):
|
||||
files.append(("usr/lib/mission-core-node/" + name, (p / name).read_bytes(), 0o644))
|
||||
files.append(("usr/lib/mission-core-node/install-k1-credential", (p / "install-k1-credential").read_bytes(), 0o755))
|
||||
files.append(("usr/lib/systemd/system/mission-core-k1.service", (p / "mission-core-k1.service").read_bytes(), 0o644))
|
||||
files.append(("usr/share/polkit-1/rules.d/50-mission-core-k1.rules", (p / "50-mission-core-k1.rules").read_bytes(), 0o644))
|
||||
k1_bundle = json.loads((p / "k1-bundle.json").read_text())
|
||||
files.append(("usr/share/mission-core-node/k1/bundle.json", (p / "k1-bundle.json").read_bytes(), 0o644))
|
||||
for item in k1_bundle["wheels"]:
|
||||
data = (ROOT / "build/k1-wheels" / item["name"]).read_bytes()
|
||||
if hashlib.sha256(data).hexdigest() != item["sha256"]:
|
||||
raise ValueError("K1 bundle hash mismatch")
|
||||
files.append(("usr/share/mission-core-node/k1/" + item["name"], data, 0o644))
|
||||
repository = ROOT.parents[1]
|
||||
# Reuse the admitted plugin runtime and the transport-neutral renderer.
|
||||
# No separate Core web service is started on the board.
|
||||
for path in (repository / "src/k1link").rglob("*"):
|
||||
if path.is_file() and path.suffix in (".py", ".json"):
|
||||
files.append(("usr/lib/mission-core-node/k1/src/k1link/" + str(path.relative_to(repository / "src/k1link")), path.read_bytes(), 0o644))
|
||||
for relative in ("plugins/xgrids-k1/profile_loader.py", "plugins/xgrids-k1/plugin.manifest.json",
|
||||
"config/observatory-equipment-models.json",
|
||||
"config/observatory-recorded-capture-profiles.json",
|
||||
"plugins/xgrids-k1/profiles/fw-3.0.2/local-network.v2.json"):
|
||||
files.append(("usr/lib/mission-core-node/k1/" + relative, (repository / relative).read_bytes(), 0o644))
|
||||
sdk = ROOT.parents[1] / "packages/plugin-sdk/python/missioncore_plugin_sdk"
|
||||
for path in sdk.rglob("*.py"):
|
||||
files.append(("usr/lib/mission-core-node/sdk/missioncore_plugin_sdk/" + str(path.relative_to(sdk)), path.read_bytes(), 0o644))
|
||||
if (ROOT / "build/provenance.json").exists():
|
||||
files.append(("usr/share/doc/mission-core-node/provenance.json", (ROOT / "build/provenance.json").read_bytes(), 0o644))
|
||||
archive = b"!<arch>\n" + ar_member("debian-binary", b"2.0\n") + ar_member("control.tar.gz", tarball(controls)) + ar_member("data.tar.gz", tarball(files))
|
||||
archive = package(controls, files)
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
destination.write_bytes(archive)
|
||||
digest = hashlib.sha256(archive).hexdigest()
|
||||
|
||||
Reference in New Issue
Block a user