2026-08-08 15:42:45 +02:00
|
|
|
#!/bin/sh -eu
|
|
|
|
|
# fp6-img pipeline: build a flashable postmarketOS image for the Fairphone 6
|
2026-08-08 16:20:02 +02:00
|
|
|
# with the Catcrafts kernel (milos-linux combined-stable) and, once its tag
|
|
|
|
|
# is published, imsd (VoLTE).
|
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 15:42:45 +02:00
|
|
|
KERNEL_REPO=https://forgejo.catcrafts.net/Catcrafts/milos-linux.git
|
|
|
|
|
KERNEL_BRANCH=combined-stable
|
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-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)
|
|
|
|
|
apk add -q git sudo openssl python3 py3-pip multipath-tools util-linux
|
2026-08-08 16:40:11 +02:00
|
|
|
pip install -q --break-system-packages \
|
|
|
|
|
git+https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git@3.11.1
|
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 19:57:43 +02:00
|
|
|
# A privileged container's /dev is a snapshot from container start —
|
|
|
|
|
# loop/dm nodes the host creates later never appear. Create the static
|
|
|
|
|
# nodes here; opening them autoloads the drivers host-side (reserved
|
|
|
|
|
# major/minors: loop = block 7,N; device-mapper control = char 10,236).
|
|
|
|
|
i=0
|
|
|
|
|
while [ $i -le 15 ]; do
|
|
|
|
|
[ -b /dev/loop$i ] || mknod -m 660 /dev/loop$i b 7 $i
|
|
|
|
|
i=$((i + 1))
|
|
|
|
|
done
|
|
|
|
|
mkdir -p /dev/mapper
|
|
|
|
|
[ -c /dev/mapper/control ] || mknod -m 600 /dev/mapper/control c 10 236
|
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 16:30:19 +02:00
|
|
|
rm -rf dist
|
|
|
|
|
install -d -o build dist
|
|
|
|
|
exec su build -c "sh -eu '$PWD/build.sh'"
|
2026-08-08 16:20:02 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
WORK=${FP6IMG_WORK:-$HOME/fp6img-work}
|
|
|
|
|
mkdir -p "$WORK"
|
2026-08-08 15:42:45 +02:00
|
|
|
|
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.
|
|
|
|
|
rm -rf "$WORK/pmaports"
|
|
|
|
|
git clone -q --depth=1 "$PMAPORTS_REPO" "$WORK/pmaports"
|
|
|
|
|
rm -rf "$WORK/pmaports/device/testing/linux-postmarketos-qcom-milos" \
|
|
|
|
|
"$WORK/pmaports/main/postmarketos-config-nftables"
|
|
|
|
|
cp -r aports/device/linux-postmarketos-qcom-milos "$WORK/pmaports/device/testing/"
|
|
|
|
|
cp -r aports/main/postmarketos-config-nftables "$WORK/pmaports/main/"
|
|
|
|
|
cp -r aports/modem/imsd "$WORK/pmaports/modem/"
|
|
|
|
|
|
|
|
|
|
# --- 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.
|
|
|
|
|
rm -rf "$WORK/milos-src"
|
|
|
|
|
git clone -q --depth=1 -b "$KERNEL_BRANCH" "$KERNEL_REPO" "$WORK/milos-src"
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
# --- 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:20:02 +02:00
|
|
|
# Stage 1 builds WITHOUT imsd: its aport sources the v0.3.0 tag, which is not
|
2026-08-08 16:30:19 +02:00
|
|
|
# published yet. When it is: add "extra_packages = imsd" and enable the
|
|
|
|
|
# checksum line below.
|
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
|
|
|
|
|
EOF
|
2026-08-08 15:42:45 +02:00
|
|
|
|
|
|
|
|
pmbootstrap checksum linux-postmarketos-qcom-milos
|
2026-08-08 16:20:02 +02:00
|
|
|
#pmbootstrap checksum imsd
|
2026-08-08 15:42:45 +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"
|
|
|
|
|
cp -L "$EXPORT/boot.img" dist/boot.img
|
|
|
|
|
cp -L "$EXPORT/fairphone-fp6.img" dist/fairphone-fp6.img
|
2026-08-08 15:42:45 +02:00
|
|
|
|
|
|
|
|
{
|
2026-08-08 16:20:02 +02:00
|
|
|
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)"
|
|
|
|
|
echo "imsd: not included yet (waiting on the v0.3.0 tag)"
|
2026-08-08 15:42:45 +02:00
|
|
|
} > dist/build-info.txt
|
|
|
|
|
cp README.md dist/README.md
|
|
|
|
|
|
|
|
|
|
(cd dist && sha256sum -- * > sha256sums.txt)
|
|
|
|
|
ls -la dist/
|