2026-08-08 15:42:45 +02:00
|
|
|
#!/bin/sh -eu
|
|
|
|
|
# fp6-img pipeline: build a flashable postmarketOS image for the Fairphone 6
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
# with the Catcrafts kernel (milos-linux combined-stable), imsd (VoLTE) and
|
|
|
|
|
# fingerprintd (fingerprint unlock)
|
2026-09-02 16:11:50 +02:00
|
|
|
# installed from the Catcrafts apk registry.
|
2026-08-08 15:42:45 +02:00
|
|
|
#
|
2026-08-08 16:20:02 +02:00
|
|
|
# Runs in CI inside an Alpine container on the privileged "pmos" runner
|
|
|
|
|
# (pmbootstrap needs loop devices; the aarch64 chroots need the qemu-user
|
|
|
|
|
# binfmt registered on the host). Also runnable in any Alpine environment
|
|
|
|
|
# with the same privileges.
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-08-08 16:30:19 +02:00
|
|
|
set -eu
|
|
|
|
|
|
2026-08-08 20:11:03 +02:00
|
|
|
# HOST REQUIREMENT: the loop driver must be loaded on the host kernel
|
|
|
|
|
# (modprobe loop + modules-load.d entry). Containers cannot load host
|
|
|
|
|
# modules, and on some kernels opening /dev/loop-control from a container
|
|
|
|
|
# does not autoload the driver either.
|
|
|
|
|
|
2026-08-08 15:42:45 +02:00
|
|
|
KERNEL_REPO=https://forgejo.catcrafts.net/Catcrafts/milos-linux.git
|
|
|
|
|
KERNEL_BRANCH=combined-stable
|
build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.
registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.
Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
|
|
|
# imsd and fingerprintd are not built here: each repo's package CI publishes
|
|
|
|
|
# its apk to the registry, and the image installs the NEWEST published
|
|
|
|
|
# version (section 3b), so a fresh install carries the same binary every
|
|
|
|
|
# installed phone gets from 'apk upgrade', and a release of either needs no
|
|
|
|
|
# commit here. Integrity comes from the same place as on the phones: the
|
|
|
|
|
# registry index is verified against the key catcrafts-fp6-repo ships
|
|
|
|
|
# (aports/device/, the phones' /etc/apk/keys) and each apk against that
|
|
|
|
|
# index, by registry-fetch.py. The resolved versions and sha256s are in the
|
|
|
|
|
# build summary. Until 2026-09-11 both were pinned here by version and
|
|
|
|
|
# sha256; that gated fresh installs only, never upgrades, and cost a commit
|
|
|
|
|
# and an image run per release.
|
|
|
|
|
REGISTRY=https://forgejo.catcrafts.net/api/packages/Catcrafts/alpine/edge/fp6
|
2026-08-08 16:20:02 +02:00
|
|
|
PMAPORTS_REPO=https://gitlab.postmarketos.org/postmarketOS/pmaports.git
|
2026-08-08 15:42:45 +02:00
|
|
|
|
|
|
|
|
cd "$(dirname "$0")"
|
2026-09-05 22:26:06 +02:00
|
|
|
# Fail in seconds, not at minute 57: a stale sha512sum in one of our own
|
|
|
|
|
# aports (run 52, fp6-vendor-blobs 1-r2) only surfaces when abuild reaches that
|
|
|
|
|
# aport, an hour into the run. This checks every aport's local source files
|
|
|
|
|
# against the committed sums before pmbootstrap does anything.
|
|
|
|
|
./check-aports.sh
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
# pmbootstrap refuses to run as root: install deps, then re-exec as a build
|
|
|
|
|
# user with passwordless sudo (pmbootstrap escalates itself where needed).
|
2026-08-08 16:30:19 +02:00
|
|
|
# The checkout stays root-owned (later workflow steps need its .git); the
|
|
|
|
|
# build user only gets dist/.
|
2026-08-08 16:20:02 +02:00
|
|
|
if [ "$(id -u)" = 0 ]; then
|
2026-08-08 16:40:11 +02:00
|
|
|
# pmbootstrap pinned from git: Alpine's package is older and e.g. still
|
|
|
|
|
# reads channels.cfg from origin/master (upstream pmaports moved to main).
|
2026-08-08 16:43:26 +02:00
|
|
|
# multipath-tools: kpartx; util-linux: losetup with --json support
|
|
|
|
|
# (pmbootstrap's host-tool checks + image mounting need both)
|
2026-08-10 10:40:50 +02:00
|
|
|
apk add -q git sudo openssl python3 py3-pip multipath-tools util-linux \
|
2026-09-02 16:11:50 +02:00
|
|
|
tar xz curl
|
2026-08-24 04:51:06 +02:00
|
|
|
# the pmOS gitlab hiccups under crawler load and truncates clones
|
|
|
|
|
# ("early EOF"); that should cost a retry, not the run — same reasoning
|
|
|
|
|
# as clone_retry below, which isn't defined yet in this root branch
|
|
|
|
|
for _i in 1 2 3; do
|
|
|
|
|
pip install -q --break-system-packages \
|
|
|
|
|
git+https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git@3.11.1 \
|
|
|
|
|
&& break
|
|
|
|
|
if [ "$_i" = 3 ]; then
|
|
|
|
|
echo "pmbootstrap pip install failed after 3 attempts" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo "pmbootstrap pip install failed (attempt $_i/3), retrying in 15s..." >&2
|
|
|
|
|
sleep 15
|
|
|
|
|
done
|
2026-08-08 19:36:55 +02:00
|
|
|
# The container cannot modprobe (no /lib/modules in here), but it doesn't
|
|
|
|
|
# need to: the host kernel autoloads the loop driver when losetup opens
|
|
|
|
|
# the static /dev/loop-control node. Make pmbootstrap's explicit
|
|
|
|
|
# 'sudo modprobe loop' a no-op (/usr/local/sbin precedes /sbin in sudo's
|
|
|
|
|
# secure_path).
|
2026-08-08 19:39:45 +02:00
|
|
|
mkdir -p /usr/local/sbin
|
2026-08-08 19:36:55 +02:00
|
|
|
printf '#!/bin/sh\nexit 0\n' > /usr/local/sbin/modprobe
|
|
|
|
|
chmod +x /usr/local/sbin/modprobe
|
2026-08-08 20:28:46 +02:00
|
|
|
# The container must run with the host's /dev bind-mounted
|
|
|
|
|
# (--volume /dev:/dev): partition nodes (loopNpM) appear via devtmpfs
|
|
|
|
|
# only there — a privileged container's own /dev is a stale snapshot
|
|
|
|
|
# from container start and never gains them.
|
|
|
|
|
if ! grep -q ' /dev devtmpfs ' /proc/mounts; then
|
|
|
|
|
echo "WARNING: /dev is not the host devtmpfs (run the container" \
|
|
|
|
|
"with --volume /dev:/dev) - partition nodes will not appear" >&2
|
|
|
|
|
fi
|
2026-08-08 16:20:02 +02:00
|
|
|
id build >/dev/null 2>&1 || adduser -D build
|
|
|
|
|
echo 'build ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/build
|
2026-08-08 22:26:36 +02:00
|
|
|
# empty dist in place (never delete the dir itself: a shell cd'd into it
|
|
|
|
|
# would poison the next podman invocation's cwd)
|
2026-08-08 16:30:19 +02:00
|
|
|
install -d -o build dist
|
2026-08-08 22:26:36 +02:00
|
|
|
find dist -mindepth 1 -delete
|
2026-08-24 05:39:11 +02:00
|
|
|
# su scrubs the environment — carry the knobs that matter across it
|
|
|
|
|
exec su build -c "FP6IMG_NO_CROSSDIRECT='${FP6IMG_NO_CROSSDIRECT:-}' sh -eu '$PWD/build.sh'"
|
2026-08-08 16:20:02 +02:00
|
|
|
fi
|
|
|
|
|
|
2026-08-08 22:26:36 +02:00
|
|
|
# git hosts occasionally hiccup (our forgejo lives on a small VM that also
|
|
|
|
|
# serves crawlers); a clone failure should cost a retry, not the whole run
|
|
|
|
|
clone_retry() { # clone_retry <dest> <git clone args...>
|
|
|
|
|
_dest=$1; shift
|
|
|
|
|
for _i in 1 2 3; do
|
|
|
|
|
rm -rf "$_dest"
|
|
|
|
|
git clone "$@" "$_dest" && return 0
|
|
|
|
|
echo "git clone $_dest failed (attempt $_i/3), retrying in 10s..." >&2
|
|
|
|
|
sleep 10
|
|
|
|
|
done
|
|
|
|
|
echo "git clone $_dest failed after 3 attempts" >&2
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-17 10:36:05 +02:00
|
|
|
# Build steps still install their makedepends over the network; same
|
|
|
|
|
# reasoning as clone_retry: a transient mirror hiccup should cost a retry,
|
|
|
|
|
# not the run. (The gitlab.freedesktop.org archive fetches that killed runs
|
|
|
|
|
# #25/#27/#30/#31 are gone — section 2 generates those tarballs locally.)
|
2026-08-16 18:08:17 +02:00
|
|
|
retry() { # retry <description> <cmd...>
|
|
|
|
|
_desc=$1; shift
|
|
|
|
|
for _i in 1 2 3; do
|
|
|
|
|
"$@" && return 0
|
|
|
|
|
echo "$_desc failed (attempt $_i/3), retrying in 30s..." >&2
|
|
|
|
|
sleep 30
|
|
|
|
|
done
|
|
|
|
|
echo "$_desc failed after 3 attempts" >&2
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
WORK=${FP6IMG_WORK:-$HOME/fp6img-work}
|
|
|
|
|
mkdir -p "$WORK"
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-08-08 20:11:03 +02:00
|
|
|
# pmbootstrap swallows its subcommands' stderr into its own log; surface it
|
|
|
|
|
# whenever this script dies so failures are diagnosable from the CI/console
|
|
|
|
|
# output alone.
|
|
|
|
|
trap 'rc=$?; if [ $rc -ne 0 ]; then
|
|
|
|
|
echo "=== build.sh failed (exit $rc); pmbootstrap log tail ==="
|
|
|
|
|
tail -60 "$HOME/.local/var/pmbootstrap/log.txt" 2>/dev/null || true
|
|
|
|
|
fi' EXIT
|
|
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
# --- 1. pmaports with our aports copied over ---------------------------------
|
|
|
|
|
# pmbootstrap hard-errors when a pkgname exists in more than one aports dir,
|
|
|
|
|
# so "overlay" means: clone upstream, delete the upstream aport, drop ours in.
|
2026-08-08 22:26:36 +02:00
|
|
|
|
|
|
|
|
clone_retry "$WORK/pmaports" -q --depth=1 "$PMAPORTS_REPO"
|
2026-08-08 16:20:02 +02:00
|
|
|
rm -rf "$WORK/pmaports/device/testing/linux-postmarketos-qcom-milos" \
|
2026-08-08 21:27:48 +02:00
|
|
|
"$WORK/pmaports/main/postmarketos-config-nftables" \
|
2026-08-16 16:56:04 +02:00
|
|
|
"$WORK/pmaports/temp/libcamera" \
|
2026-08-08 21:27:48 +02:00
|
|
|
"$WORK/pmaports/temp/libqmi" \
|
|
|
|
|
"$WORK/pmaports/temp/modemmanager"
|
|
|
|
|
mkdir -p "$WORK/pmaports/temp"
|
2026-08-08 16:20:02 +02:00
|
|
|
cp -r aports/device/linux-postmarketos-qcom-milos "$WORK/pmaports/device/testing/"
|
2026-08-08 20:48:28 +02:00
|
|
|
cp -r aports/device/soc-fairphone-fp6-audio "$WORK/pmaports/device/"
|
aports: extract aw88261_acf.bin on-device instead of shipping it
New package fp6-vendor-blobs: a manifest-driven systemd oneshot that
copies proprietary blobs out of the stock Android partitions on first
boot. pmOS installs flash only boot+userdata, so every installed unit
still carries the stock vendor/dsp partitions - the device duplicates a
file it already contains, for its own operation, and nothing proprietary
is distributed by us. Partitions are mounted read-only (ext4 additionally
with noload - not even a journal replay touches the stock data), every
copy is sha256-pinned and a miss fails loudly, and the consuming devices
are unbound and re-probed afterwards so the feature works the same boot.
The rebind is unconditional by design: aw88261 binds on a bare i2c
chip-id probe and requests the ACF only at ASoC card init (~21 s, after
SoundWire enumeration), so a bound device can still be one that lost the
firmware race - and that failed request is never retried.
soc-fairphone-fp6-audio (pkgver 4) stops shipping the blob, installs the
manifest fragment instead, and re-runs the extractor from
post-install/post-upgrade so upgrades restore the file immediately (the
old package version removed it on upgrade). License drops back to plain
BSD-3-Clause - nothing proprietary left in the package.
Verified on the dev phone (fp6 repo journal/blobs/captures/
2026-08-24-first-boot-extractor-verification.txt): first-boot extraction
+ same-boot audio, mid-session post-upgrade recovery, and the everyday
fast-path no-op; committed files byte-identical to the tested deployment.
Assisted-by: Claude:claude-fable-5
2026-08-24 21:00:34 +02:00
|
|
|
cp -r aports/device/fp6-vendor-blobs "$WORK/pmaports/device/"
|
2026-08-08 20:54:29 +02:00
|
|
|
cp -r aports/device/callaudioshim "$WORK/pmaports/device/"
|
2026-08-09 00:03:09 +02:00
|
|
|
cp -r aports/device/fp6-device-tweaks "$WORK/pmaports/device/"
|
aports: add fp6-charging-mode (offline charging, charger-boot detection)
Charger-insertion boots (androidboot.mode=charger, ABL-appended) divert to
a minimal charging.target instead of the full UI, ending the dead-battery
bootloop: ADSP charging/USB-PD runs, splash+panel killed ~6-10s in via an
initramfs hook, modem+cdsp stopped, radios/sensors suppressed, CPU capped.
Power key reboots to a normal boot, volume-up shows battery status,
volume-down enables USB ssh, unplug powers off. Measured >= breakeven on a
100mA-classified SDP port, strongly net-positive on real chargers.
Suspend duty-cycling (~13mA floor) exists behind an off-by-default flag:
resume from deep suspend intermittently kills the UFS link (hibern8 exit
failed ret=5) - do not enable until that kernel bug is fixed.
Byte-identical to the deployment verified on the dev phone 2026-08-24
(journal/power: r5 armed tests, crash autopsies, measurements).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 04:03:06 +02:00
|
|
|
cp -r aports/device/fp6-charging-mode "$WORK/pmaports/device/"
|
2026-08-15 00:04:55 +02:00
|
|
|
cp -r aports/device/catcrafts-fp6-repo "$WORK/pmaports/device/"
|
2026-08-08 16:20:02 +02:00
|
|
|
cp -r aports/main/postmarketos-config-nftables "$WORK/pmaports/main/"
|
2026-09-02 16:11:50 +02:00
|
|
|
# imsd has no aport here at all: section 3b installs the published apk.
|
2026-08-08 21:27:48 +02:00
|
|
|
# Alpine forks carrying the GNSS patches (libqmi !470 unreleased; MM !1463
|
|
|
|
|
# draft) - deps of modemmanager/imsd, built from aports because r100 > repo.
|
|
|
|
|
cp -r aports/temp/libqmi "$WORK/pmaports/temp/"
|
|
|
|
|
cp -r aports/temp/modemmanager "$WORK/pmaports/temp/"
|
2026-08-16 16:56:04 +02:00
|
|
|
# pmOS libcamera fork + the FP6 OV13B10 sensor-helper/properties patches
|
|
|
|
|
# (r2 > pmOS r1)
|
|
|
|
|
cp -r aports/temp/libcamera "$WORK/pmaports/temp/"
|
2026-08-08 16:20:02 +02:00
|
|
|
|
|
|
|
|
# --- 2. pin the kernel source -------------------------------------------------
|
2026-08-08 15:42:45 +02:00
|
|
|
# Source archives are disabled on the Forgejo instance, so generate the
|
2026-08-08 16:20:02 +02:00
|
|
|
# tarball ourselves; abuild treats it as a local source file.
|
2026-08-08 22:26:36 +02:00
|
|
|
|
|
|
|
|
clone_retry "$WORK/milos-src" -q --depth=1 -b "$KERNEL_BRANCH" "$KERNEL_REPO"
|
2026-08-08 16:20:02 +02:00
|
|
|
COMMIT=$(git -C "$WORK/milos-src" rev-parse HEAD)
|
|
|
|
|
KAPORT="$WORK/pmaports/device/testing/linux-postmarketos-qcom-milos"
|
|
|
|
|
git -C "$WORK/milos-src" archive --prefix=milos-linux/ \
|
|
|
|
|
-o "$KAPORT/milos-linux-$COMMIT.tar.gz" HEAD
|
|
|
|
|
sed -i "s/^_commit=.*/_commit=\"$COMMIT\"/" "$KAPORT/APKBUILD"
|
2026-08-08 22:39:14 +02:00
|
|
|
# Stamp the kernel pkgver with the source commit date (7.1.2 ->
|
|
|
|
|
# 7.1.2_git20260808): a rebuilt combined-stable must produce a HIGHER
|
|
|
|
|
# package version, or phones subscribed to the package registry would
|
|
|
|
|
# never see kernel updates.
|
|
|
|
|
KDATE=$(git -C "$WORK/milos-src" log -1 --format=%cd --date=format:%Y%m%d)
|
|
|
|
|
sed -i "s/^pkgver=\([0-9.]*\)\$/pkgver=\1_git$KDATE/" "$KAPORT/APKBUILD"
|
2026-08-08 16:20:02 +02:00
|
|
|
|
2026-09-02 16:11:50 +02:00
|
|
|
# Same dance for the gitlab.freedesktop.org packages (libqmi, modemmanager,
|
2026-08-17 10:36:05 +02:00
|
|
|
# libcamera): their pinned tarballs came from fd.o's on-demand archive
|
|
|
|
|
# endpoint, which 503/504s for hours at a stretch — runs #25, #27, #30 and
|
|
|
|
|
# #31 all died there, outlasting any in-run retry. git clone is served from
|
|
|
|
|
# a different code path and holds up; generate the tarballs ourselves. The
|
|
|
|
|
# pins stay single-sourced in the APKBUILDs, read back out here.
|
|
|
|
|
|
|
|
|
|
apkbuild_var() { # apkbuild_var <aport dir> <variable>
|
|
|
|
|
sed -n "s/^$2=//p" "$1/APKBUILD" | tr -d '"'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fdo_tarball() { # fdo_tarball <repo url> <aport dir> <tarball dirname> <ref>
|
|
|
|
|
clone_retry "$WORK/fdo-src" -q --bare "$1"
|
|
|
|
|
git -C "$WORK/fdo-src" archive --prefix="$3/" -o "$2/$3.tar.gz" "$4"
|
|
|
|
|
rm -rf "$WORK/fdo-src"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FDO=https://gitlab.freedesktop.org
|
|
|
|
|
QMI_COMMIT=$(apkbuild_var "$WORK/pmaports/temp/libqmi" _commit)
|
|
|
|
|
fdo_tarball "$FDO/mobile-broadband/libqmi.git" \
|
|
|
|
|
"$WORK/pmaports/temp/libqmi" "libqmi-$QMI_COMMIT" "$QMI_COMMIT"
|
|
|
|
|
MM_COMMIT=$(apkbuild_var "$WORK/pmaports/temp/modemmanager" _commit)
|
|
|
|
|
fdo_tarball "$FDO/mobile-broadband/ModemManager.git" \
|
|
|
|
|
"$WORK/pmaports/temp/modemmanager" "ModemManager-$MM_COMMIT" "$MM_COMMIT"
|
|
|
|
|
CAM_VER=$(apkbuild_var "$WORK/pmaports/temp/libcamera" _pkgver)
|
|
|
|
|
fdo_tarball "$FDO/camera/libcamera.git" \
|
|
|
|
|
"$WORK/pmaports/temp/libcamera" "libcamera-v$CAM_VER" "v$CAM_VER"
|
|
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
# --- 3. configure pmbootstrap -------------------------------------------------
|
2026-08-08 16:30:19 +02:00
|
|
|
# 'pmbootstrap config' refuses to run before a config exists ("run init
|
|
|
|
|
# first"), so write the config file directly (INI, [pmbootstrap] section,
|
|
|
|
|
# keys = pmb.core.Config attributes).
|
2026-08-08 16:35:01 +02:00
|
|
|
# 'init' would also create the work dir and stamp its migration version;
|
|
|
|
|
# do both ourselves (version derived from the installed pmb, not hardcoded).
|
|
|
|
|
WORKDIR="$HOME/.local/var/pmbootstrap"
|
|
|
|
|
mkdir -p "$WORKDIR/cache_git"
|
|
|
|
|
python3 -c "import pmb.config; print(pmb.config.work_version)" > "$WORKDIR/version"
|
|
|
|
|
|
2026-08-08 16:30:19 +02:00
|
|
|
mkdir -p "$HOME/.config"
|
|
|
|
|
cat > "$HOME/.config/pmbootstrap_v3.cfg" <<EOF
|
|
|
|
|
[pmbootstrap]
|
|
|
|
|
aports = $WORK/pmaports
|
|
|
|
|
device = fairphone-fp6
|
|
|
|
|
ui = plasma-mobile
|
|
|
|
|
systemd = always
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fingerprintd,fingerprintd-systemd,fingerprintd-agent,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel
|
2026-08-08 16:30:19 +02:00
|
|
|
EOF
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-09-02 16:11:50 +02:00
|
|
|
# All four source tarballs are generated locally above, so every checksum
|
2026-08-17 10:36:05 +02:00
|
|
|
# step is offline. libcamera needs a checksum step now too: its committed
|
|
|
|
|
# sums were for the fd.o-served tarball, and git-archive output is not
|
|
|
|
|
# byte-identical to it (verified: sha512 differs).
|
2026-08-08 15:42:45 +02:00
|
|
|
pmbootstrap checksum linux-postmarketos-qcom-milos
|
2026-08-17 10:36:05 +02:00
|
|
|
pmbootstrap checksum libqmi
|
|
|
|
|
pmbootstrap checksum modemmanager
|
|
|
|
|
pmbootstrap checksum libcamera
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-08-24 05:15:53 +02:00
|
|
|
# FP6IMG_NO_CROSSDIRECT=1: for local (podman) environments where
|
|
|
|
|
# crossdirect's /native bridge breaks ("cc: cannot execute cc1:
|
|
|
|
|
# posix_spawnp: No such file or directory" during meson setup); the
|
|
|
|
|
# affected packages then build qemu-only — slower, identical output. The
|
|
|
|
|
# kernel is unaffected either way (cross-native, no crossdirect).
|
|
|
|
|
NOCROSS=${FP6IMG_NO_CROSSDIRECT:+--no-cross}
|
|
|
|
|
|
2026-08-08 22:47:55 +02:00
|
|
|
# pmbootstrap's install-time build plan is not dependency-ordered (it tried
|
|
|
|
|
# building modemmanager before the libqmi its makedepends require); build
|
|
|
|
|
# the GNSS stack bottom-up explicitly. 'pmbootstrap build' is a no-op when
|
|
|
|
|
# the package is already current.
|
2026-08-24 05:15:53 +02:00
|
|
|
retry "build libqmi" pmbootstrap $NOCROSS build --arch aarch64 libqmi
|
|
|
|
|
retry "build modemmanager" pmbootstrap $NOCROSS build --arch aarch64 modemmanager
|
2026-08-16 16:56:04 +02:00
|
|
|
# libcamera is no makedepend of anything here - build it explicitly so the
|
|
|
|
|
# patched -r2 exists for the publish step even if the install set resolves
|
|
|
|
|
# it before the overlay is considered.
|
2026-08-24 05:15:53 +02:00
|
|
|
retry "build libcamera" pmbootstrap $NOCROSS build --arch aarch64 libcamera
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
# --- 3b. imsd + fingerprintd: the published apks, not local builds -----------
|
|
|
|
|
# Each repo's package CI is the only producer of its apk; the image installs
|
build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.
registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.
Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
|
|
|
# the newest registry package, the one users get via 'apk upgrade'.
|
2026-09-02 16:11:50 +02:00
|
|
|
# pmbootstrap has no knob for a third-party repository, and after the main
|
|
|
|
|
# 'apk add' it re-adds every package found in its local packages dir BY FILE
|
|
|
|
|
# PATH — which makes apk verify the package's own signature, and registry
|
|
|
|
|
# packages are signed with per-run keys nobody keeps (phones trust the
|
build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.
registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.
Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
|
|
|
# registry-signed index instead). So: fetch the newest of each, verified the
|
|
|
|
|
# way a phone verifies them (registry-fetch.py: index signature against the
|
|
|
|
|
# shipped key, control checksum and data hash against the index), re-sign
|
|
|
|
|
# the envelope with this run's abuild key (control and data streams stay
|
|
|
|
|
# byte-identical, so the identity checksum equals the registry's), drop into
|
|
|
|
|
# the local packages dir, re-index. The abuild key exists because the builds
|
|
|
|
|
# above initialized the buildroot. Each group's first name decides the
|
|
|
|
|
# version; its subpackages must exist at that same version.
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
REG_DL="$WORK/registry-apks"
|
|
|
|
|
rm -rf "$REG_DL"
|
|
|
|
|
mkdir -p "$REG_DL"
|
build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.
registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.
Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
|
|
|
REGISTRY_KEY=$(echo aports/device/catcrafts-fp6-repo/*.rsa.pub)
|
|
|
|
|
if [ ! -f "$REGISTRY_KEY" ]; then
|
|
|
|
|
echo "expected exactly one registry key in aports/device/catcrafts-fp6-repo" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
retry "fetch registry packages" python3 ./registry-fetch.py "$REGISTRY" "$REGISTRY_KEY" "$REG_DL" \
|
|
|
|
|
imsd,imsd-systemd fingerprintd,fingerprintd-systemd,fingerprintd-agent
|
2026-09-02 16:11:50 +02:00
|
|
|
ABUILD_KEY=$(echo "$WORKDIR"/config_abuild/*.rsa)
|
|
|
|
|
if [ ! -f "$ABUILD_KEY" ]; then
|
|
|
|
|
echo "expected exactly one abuild key in $WORKDIR/config_abuild" >&2
|
|
|
|
|
exit 1
|
2026-08-24 05:15:53 +02:00
|
|
|
fi
|
2026-09-02 17:21:30 +02:00
|
|
|
# abuild-keygen ran inside the chroot as pmbootstrap's user (uid 12345), so
|
|
|
|
|
# the key is 0600 to that uid and unreadable here (run 49 died on exactly
|
|
|
|
|
# this); sign from a private copy taken via sudo, then drop it.
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
KEYCOPY="$REG_DL/abuild-key.rsa"
|
2026-09-02 17:21:30 +02:00
|
|
|
sudo install -m 0600 -o "$(id -un)" "$ABUILD_KEY" "$KEYCOPY"
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
for _f in "$REG_DL"/*.apk; do
|
2026-09-02 17:21:30 +02:00
|
|
|
python3 ./apk-resign.py "$_f" "$KEYCOPY" "$(basename "$ABUILD_KEY").pub"
|
2026-09-02 16:11:50 +02:00
|
|
|
done
|
2026-09-02 17:21:30 +02:00
|
|
|
rm -f "$KEYCOPY"
|
2026-09-02 19:35:20 +02:00
|
|
|
# The packages dir belongs to pmbootstrap's chroot user as well (abuild wrote
|
|
|
|
|
# it from inside the chroot), so the build user cannot write there either
|
|
|
|
|
# (run 50): install the files with the directory's own owner, as abuild would
|
|
|
|
|
# have left them. The dir exists because the builds above populated it.
|
|
|
|
|
PKGDIR="$WORKDIR/packages/edge/aarch64"
|
|
|
|
|
if [ ! -d "$PKGDIR" ]; then
|
|
|
|
|
echo "$PKGDIR missing - the package builds above should have created it" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
for _f in "$REG_DL"/*.apk; do
|
2026-09-02 19:35:20 +02:00
|
|
|
sudo install -m 0644 -o "$(stat -c %u "$PKGDIR")" -g "$(stat -c %g "$PKGDIR")" \
|
|
|
|
|
"$_f" "$PKGDIR/$(basename "$_f")"
|
|
|
|
|
done
|
Install fingerprintd from the registry, so the image unlocks with a finger
The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.
Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.
The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.
Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
2026-09-05 20:31:37 +02:00
|
|
|
rm -f "$REG_DL"/*.apk
|
2026-09-02 16:11:50 +02:00
|
|
|
pmbootstrap index
|
2026-08-08 22:47:55 +02:00
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
# --- 4. build the image -------------------------------------------------------
|
2026-08-08 15:42:45 +02:00
|
|
|
# Same default credentials as the official postmarketOS images.
|
2026-08-08 16:20:02 +02:00
|
|
|
pmbootstrap -y zap >/dev/null 2>&1 || true
|
2026-08-08 15:42:45 +02:00
|
|
|
pmbootstrap install --password 147147
|
|
|
|
|
|
2026-08-08 16:20:02 +02:00
|
|
|
# --- 5. collect artifacts -----------------------------------------------------
|
|
|
|
|
EXPORT=/tmp/postmarketOS-export
|
2026-08-08 16:30:19 +02:00
|
|
|
rm -rf "$EXPORT"
|
2026-08-08 16:20:02 +02:00
|
|
|
pmbootstrap export "$EXPORT"
|
2026-08-08 15:42:45 +02:00
|
|
|
|
2026-08-10 16:22:20 +02:00
|
|
|
# Everything ships as ONE archive, and dist/ holds nothing else: the rootfs is
|
|
|
|
|
# 3.0 GiB raw and 812 MiB under xz -6 (gzip -6 stops at 1144 MiB), and a bundle
|
|
|
|
|
# makes it impossible to pair a boot.img with a rootfs from a different build.
|
|
|
|
|
# README/install.sh are not attached loose as well — they are readable in the
|
|
|
|
|
# repository, and a second copy in the release only invites reading a stale one
|
|
|
|
|
# (and a loose sha256sums.txt next to the archive's own is pure confusion).
|
2026-08-10 10:40:50 +02:00
|
|
|
#
|
|
|
|
|
# pmbootstrap export writes symlinks into its work dir; tar -h resolves them,
|
|
|
|
|
# so the archive is built without a second 3 GiB copy on disk.
|
|
|
|
|
STAGE="$WORK/stage"
|
|
|
|
|
rm -rf "$STAGE"
|
|
|
|
|
mkdir -p "$STAGE/fp6-img"
|
|
|
|
|
ln -s "$EXPORT/boot.img" "$STAGE/fp6-img/boot.img"
|
|
|
|
|
ln -s "$EXPORT/fairphone-fp6.img" "$STAGE/fp6-img/fairphone-fp6.img"
|
2026-08-10 16:22:20 +02:00
|
|
|
cp README.md install.sh "$STAGE/fp6-img/"
|
|
|
|
|
{
|
|
|
|
|
echo "kernel: $KERNEL_REPO $KERNEL_BRANCH @ $COMMIT"
|
|
|
|
|
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
|
|
|
echo "default login: user / 147147 (same as official postmarketOS images)"
|
build.sh: install the newest registry imsd and fingerprintd, verified the way a phone does
Until now both were pinned here by version and sha256, so every release
of either needed a commit and an image run, in the right order
(fingerprintd 0.2.4 would have taken: push, wait for the registry, pin,
push). The pin gated fresh installs only: every installed phone already
takes the newest registry package on 'apk upgrade'.
registry-fetch.py resolves the newest version of each group (imsd + its
systemd unit; fingerprintd + systemd + agent, the subpackages at the
anchor's version, or it fails) and verifies the way apk does on the
phone: the index signature against the key catcrafts-fp6-repo ships --
the signer's name must be that key's too -- each apk's control checksum
against the index, and its data segment against the control's datahash.
Anything that fails is not written. The resolved versions and sha256s go
into the release's build-info.txt, so an image still names its exact
packages.
Tested against the live registry: it resolves imsd 0.3.3-r0 and
fingerprintd 0.2.3-r0 with sha256s identical to the five pins this
removes; a wrong key, a key of another name, a tampered control segment,
a corrupt or swapped data segment, a truncated file and a missing
subpackage are each refused with a reason.
2026-09-11 13:21:18 +02:00
|
|
|
while read -r _n _v _s; do
|
|
|
|
|
printf '%-14s %s %s-%s (newest registry package, sha256 %s)\n' "$_n:" "$REGISTRY" "$_n" "$_v" "$_s"
|
|
|
|
|
done < "$REG_DL/manifest"
|
2026-08-10 16:22:20 +02:00
|
|
|
} > "$STAGE/fp6-img/build-info.txt"
|
|
|
|
|
# sums of the extracted contents
|
2026-08-10 10:40:50 +02:00
|
|
|
(cd "$STAGE/fp6-img" && sha256sum -- * > sha256sums.txt)
|
|
|
|
|
tar -C "$STAGE" -chf - fp6-img | xz -T0 -6 > dist/fp6-img.tar.xz
|
|
|
|
|
rm -rf "$STAGE"
|
|
|
|
|
|
2026-08-08 15:42:45 +02:00
|
|
|
ls -la dist/
|