mirror of
https://github.com/commaai/agnos-builder.git
synced 2026-09-08 17:33:42 +08:00
10 KiB
10 KiB
Alpine Migration Plan (Draft 1)
What We Have Today
build_system.shpullsubuntu-base-24.04.3-base-arm64.tar.gz, loads it intoDockerfile.agnos, and bakes the finalsystem.img.Dockerfile.agnosanduserspace/*.shassume Ubuntu/Debian tooling:apt-fast,dpkg,.debblobs inuserspace/debs, and systemd units inuserspace/files.Dockerfile.builderis an Ubuntu 20.04 helper image that mounts the workspace and provides build-essential tools.- Services are managed exclusively with systemd (
userspace/services.sh), and dozens of*.service/*.timerfiles are copied into/lib/systemd/system.
The goal: swap the Ubuntu base for Alpine while keeping device functionality (openpilot, hardware services, flashing) unchanged.
Guiding Principles
- Parallelize, don’t destabilize. Leave the Ubuntu build path untouched; all Alpine work happens in new
build_alpine.sh,Dockerfile.alpine, etc., until we prove parity. - Change one layer at a time. Migrate builder tooling before touching the rootfs so failures are easy to bisect.
- Stay reproducible. Every replacement must be scripted (no manual
apk addon a live device). - Prefer rebuilding from source. Debian-only
.debartifacts (qtwayland5, modemmanager, etc.) should become source builds or Alpineapks. - Keep service semantics. Whether we keep systemd on Alpine or port to OpenRC must be decided up front and applied consistently.
Phase A – Minimal Alpine Bring-up (Parallel Path)
Goal: produce a new build_alpine.sh that builds a tiny Alpine-based rootfs, flashes separately, boots to userspace, and runs usr/comma/magic.py to show the logo. Existing Ubuntu images/scripts remain the default.
- Create
Dockerfile.alpinethat starts from the Alpine minirootfs and installs just enough to boot OpenRC and launch the logo service. - Clone
build_system.shintobuild_alpine.sh. Adjust it to:- Download
alpine-minirootfs-<ver>-aarch64.tar.gz. - Use new Alpine docker build to produce a
system-alpine.img. - Keep all outputs in
output/alpine/so it doesn’t collide with Ubuntu artifacts.
- Download
- Inside the Alpine image:
- Configure networking basics, hostname, and ensure
/usr/commais copied from the existing repository. - Install just enough packages (
python3,py3-pip, DRM/mesa bits) plus pip-installpyrayformagic.py. - Create an OpenRC service (
/etc/init.d/magic) that launches/usr/comma/magic.pyon boot and streams logs to/var/log/magic.log. - Validate on real hardware (or QEMU once GPU path is mocked) that the logo shows and backlight powers on.
- Configure networking basics, hostname, and ensure
- Add a simple smoke test stub (
test_alpine_magic.sh) describing how to exercise the image until automation exists. - Document how to invoke the new flow in
README.md(later) but keep it optional until feature-complete.
Phase 0 – Recon & Prep
- Pin an Alpine release (3.20 or edge) that ships
aarch64andarmv7repos; record mirror URLs. - List every Ubuntu package we install (
userspace/base_setup.sh,openpilot_dependencies.sh,install_extras.sh, etc.) and mark whether Alpine has an equivalentapk, needs a community repo, or must be built from source. - Inventory
.debblobs inuserspace/debs,userspace/qtwayland, compiler stages, and note which ones rely on glibc symbols. - Decide init strategy:
- Option A: keep systemd (build it from source on Alpine + run in PID1).
- Option B: switch to OpenRC (rewrite service units + helper scripts). Document the choice because it impacts almost every script.
- Confirm musl vs glibc requirements. If any binary must stay glibc-linked (e.g., Qualcomm blobs), plan to add the
gcompat/alpine-pkg-glibcshim or run those pieces inside a glibc sysroot.
Phase 1 – Alpine Builder Container (new Dockerfile.builder.alpine)
- Copy
Dockerfile.buildertoDockerfile.builder.alpine; base the new file onalpine:<version>while leaving the Ubuntu original intact. - Swap
apt-getforapk add --no-cacheand install Alpine equivalents (build-base,clang,openssl,ccache,android-tools,py3packages, etc.). - Ensure
python2requirement is gone or solved (Alpine only shipspython3; if python2 is still needed, vendor it from source). - Re-implement the user-mapping logic using BusyBox
addgroup/addusersyntax. - Verify
ccachesymlink setup still works and thatdocker buildx --loadsucceeds whenbuild_alpine.shreferences the new Dockerfile.
Phase 2 – Alpine Rootfs Source (build_alpine.sh)
- Copy
build_system.shtobuild_alpine.shso the Ubuntu path remains unchanged. - Replace Ubuntu download variables with Alpine ones (use
alpine-minirootfs-<ver>-aarch64.tar.gz) and update the SHA. - Wire
build_alpine.shto call the Alpine Dockerfile and emit outputs underbuild/alpine/+output/alpine/. - Drop
debconf,dpkg, andaptassumptions as soon as the Alpine tarball is extracted; Alpine already hasapk-tools. - Confirm
qemu-user-staticstill handles musl binaries when running on x86_64 hosts. - Validate that
img2simg+ ext4 creation stays the same (filesystem layer is independent of distro).
Phase 3 – Userspace & Package Installation (Alpine variants live beside Ubuntu)
- Agnos compiler stages
- Duplicate
Dockerfile.agnosor split it into named targets so the Alpine build stages live alongside the Ubuntu ones without altering them. - Change all Alpine stages to
FROM alpine. Install build deps viaapk(alpine-sdk,cmake,ninja, etc.). - Audit
compile-*.shscripts forapt-get,ldconfig,/usr/lib/aarch64-linux-gnuassumptions; rewrite paths for Alpine (/usr/lib,/lib). - Replace
checkinstall-generated.deboutputs with either plain tarballs or ad-hoc.apkpackages (useabuildorapkbuildtemplates).
- Duplicate
- Base setup (
userspace/base_setup.sh)- Add
userspace/base_setup_alpine.sh(leave the Ubuntu script alone). Use/etc/apk/repositoriesplusapk update && apk add. - Recreate required system users/groups using BusyBox tools.
- Re-map package names (e.g.,
build-essential→build-base,network-manager→NetworkManagerfrom the community repo,iptables-persistent→iptables+ manual save). - Handle 32-bit deps: Alpine’s
aarch64repo does not support mixingarmhfpackages. Decide between cross-compiling needed 32-bit libs from source or hosting a parallelarmv7sysroot mounted under/lib32. - Replace
locale-gen/update-localewith Alpine equivalents (/etc/profile.d/locale.sh,glibc-i18nif using glibc shim, ormusl-locales).
- Add
- Openpilot deps (
userspace/openpilot_dependencies.sh,openpilot_python_dependencies.sh)- Provide Alpine siblings of these scripts and translate each dependency to
apkpackages or source builds. For tools missing on Alpine, extend the compiler stages. - Ensure
uvinstall script runs on musl (needsbuild-base,curl,python3).
- Provide Alpine siblings of these scripts and translate each dependency to
- Hardware setup & proprietary debs
- For each
.debinuserspace/debs, extract it (dpkg-deb -x) and repackage the payload into the Alpine rootfs manually or via customapks. - Verify Qualcomm binaries only depend on glibc symbols that exist in your shim (or keep a glibc chroot mounted under
/usr/glibc). - Replace
apt install libjson-c2hack with either an Alpine package or a source build pinned to the required ABI.
- For each
- Service management
- If staying with systemd: build systemd against musl (supported as of v253) and ensure
pam,udev, andresolvedpieces still work. Double-checksystemctlinvocations inuserspace/services.sh. - If moving to OpenRC: convert every
.service,.timer,.pathfile into OpenRC services; rewriteuserspace/services.shto callrc-update. Confirm replacements forsystemd-tmpfiles,journald, andnetworkd(likely usebusybox-ntpd,rsyslog, andNetworkManager).
- If staying with systemd: build systemd against musl (supported as of v253) and ensure
- Filesystem layout differences
- Alpine does not use
/lib/systemd/systemor/usr/lib/aarch64-linux-gnu; adjust copy paths only in the Alpine Dockerfile variant. - Revisit
readonly_setup.sh: dropaptcache cleanup, ensure/etc/localtimelogic matches Alpine’s/etc/TZ//etc/timezoneexpectations.
- Alpine does not use
Phase 4 – Image Assembly & Flash Scripts
- Introduce
load_alpine*.shandflash_alpine*.shcompanions instead of rewriting the Ubuntu scripts; point them at the new image names. - Ensure the Alpine image writes its own
/VERSIONmetadata (maybeVERSION_ALPINEor embed inVERSION). - Re-run size optimization (Alpine is smaller; re-tune
ROOTFS_IMAGE_SIZEspecifically for the Alpine build). - Verify
readonly_setup.sh+mv /var /usr/defaultstill behave; Alpine may ship busyboxmvwithout-T, so test carefully.
Phase 5 – Validation
- First milestone: boot the Alpine image and verify
/usr/comma/magic.pyauto-runs and shows the logo. - Full milestone: boot the new image on comma 3/3X; confirm kernel + modem + UI stack.
- Run the existing
TESTING.mdchecklist plus:apkdatabase integrity (apk info -vv | head).- Services status via the new init system.
- Openpilot runtime smoke test.
- Exercise flashing (
flash_alpine*.sh) end-to-end on at least one device. - Document any remaining Ubuntu assumptions and either fix or log issues for follow-up.
Open Questions & Risks
- glibc-only blobs: If Qualcomm or Weston hacks require glibc, we need either a glibc compatibility layer or to keep those pieces in a Debian chroot.
- Systemd vs OpenRC: Porting dozens of custom services may dwarf other work; validate effort before committing.
- Multi-arch libraries: Alpine currently lacks an easy way to install
armhfpackages alongsideaarch64. Plan for source builds or rethink the need for 32-bit libs. - CI coverage: GitHub Actions runners may not have
apktooling; ensure CI images are updated before merging.
Next iteration: once we lock the init strategy and package availability, we can expand each checkbox into concrete scripts/commands.