split the public gates from the internal packaging half
The three portable jobs stay; the archdev packaging and archive publish move to packages.yml, which the public branch does not carry. The handoff document is operator archaeology and does not travel.
This commit is contained in:
parent
b8e76c4e9e
commit
76846feb61
4 changed files with 457 additions and 579 deletions
|
|
@ -136,448 +136,3 @@ jobs:
|
|||
# archdev) where the aarch64 sysroot lives — no docker, no node, plain shell.
|
||||
# Publishes the cross-built binary to a rolling `edge` prerelease; the phone
|
||||
# updates itself from there instead of waiting on a laptop scp.
|
||||
aarch64-artifact:
|
||||
runs-on: archdev
|
||||
needs: [rust-test, no-ai-attribution, action-pins]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/primary'
|
||||
steps:
|
||||
- name: Clone repo + pinned tuie
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf "$GITHUB_WORKSPACE/src"
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
git -C "$GITHUB_WORKSPACE/src/Souveraine" checkout "$GITHUB_SHA"
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "${GITHUB_SERVER_URL}/Fimeg/tuie.git" "$GITHUB_WORKSPACE/src/tuie"
|
||||
git -C "$GITHUB_WORKSPACE/src/tuie" checkout aa43e335c8f5a3cd2c78e397a1a8dfe534a6bc35
|
||||
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# Persistent target dirs: fresh clones per run, warm compile cache.
|
||||
export CARGO_TARGET_DIR="$HOME/.cache/souveraine-ci-target"
|
||||
export SOUVERAINE_AARCH64_SYSROOT="$HOME/aarch64-sysroot"
|
||||
./scripts/build-cross.sh
|
||||
BIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine"
|
||||
file "$BIN" | grep -q aarch64 || { echo "built binary is not aarch64" >&2; exit 1; }
|
||||
cp "$BIN" souveraine-aarch64
|
||||
|
||||
# sessiond is a separate bin behind the sessiond feature. It was
|
||||
# hand-copied to the phone's /usr/local/bin and owned by no package,
|
||||
# so it never received updates; build and ship it with the rest.
|
||||
./scripts/build-cross.sh --features sessiond --bin souveraine-sessiond
|
||||
SBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-sessiond"
|
||||
file "$SBIN" | grep -q aarch64 || { echo "sessiond is not aarch64" >&2; exit 1; }
|
||||
cp "$SBIN" souveraine-sessiond-aarch64
|
||||
|
||||
# The sensor reporter. Same story as sessiond: it replaces shell
|
||||
# scripts that shipped in rootfs-overlay/ and drifted per-device.
|
||||
./scripts/build-cross.sh --bin souveraine-sensord
|
||||
NBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-sensord"
|
||||
file "$NBIN" | grep -q aarch64 || { echo "sensord is not aarch64" >&2; exit 1; }
|
||||
cp "$NBIN" souveraine-sensord-aarch64
|
||||
|
||||
# Full USB KVM receiver: GUD display-in and the HID return surface.
|
||||
./scripts/build-cross.sh --features usb-kvm --bin souveraine-usb-kvm
|
||||
UBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-usb-kvm"
|
||||
file "$UBIN" | grep -q aarch64 || { echo "usb-kvm is not aarch64" >&2; exit 1; }
|
||||
cp "$UBIN" souveraine-usb-kvm-aarch64
|
||||
|
||||
# The wry host: self-hosted sites as apps, and the rig she wears.
|
||||
# Needs webkit2gtk on the build sysroot; it is already installed on
|
||||
# the phone (TASK-59 phase 1 measured against it).
|
||||
# Best effort: it links against the system webview, so it needs
|
||||
# webkit2gtk in the aarch64 sysroot. Until that is there this is the
|
||||
# one binary that may legitimately not build, and it must not stop the
|
||||
# ones that decide whether the phone locks.
|
||||
if ./scripts/build-cross.sh --features web --bin souveraine-web; then
|
||||
WBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-web"
|
||||
if file "$WBIN" | grep -q aarch64; then
|
||||
cp "$WBIN" souveraine-web-aarch64
|
||||
else
|
||||
echo "::warning::web host built but is not aarch64; skipping it"
|
||||
fi
|
||||
else
|
||||
echo "::warning::the wry host did not cross-build (webkit2gtk sysroot?); skipping it"
|
||||
fi
|
||||
|
||||
# secrets rail + machined: also hand-copied on the phone until now.
|
||||
./scripts/build-cross.sh --features secrets --bin souveraine-secrets
|
||||
./scripts/build-cross.sh --bin souveraine-machined
|
||||
./scripts/build-cross.sh --bin souveraine-admit
|
||||
for b in souveraine-secrets souveraine-machined souveraine-admit; do
|
||||
B="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/$b"
|
||||
file "$B" | grep -q aarch64 || { echo "$b is not aarch64" >&2; exit 1; }
|
||||
cp "$B" "$b-aarch64"
|
||||
done
|
||||
|
||||
export CARGO_TARGET_DIR="$HOME/.cache/souveraine-ci-target-x86_64"
|
||||
cargo build --release
|
||||
BIN="$CARGO_TARGET_DIR/release/souveraine"
|
||||
file "$BIN" | grep -q 'x86-64' || { echo "built binary is not x86_64" >&2; exit 1; }
|
||||
cp "$BIN" souveraine-x86_64
|
||||
|
||||
cargo build --release --features secrets --bin souveraine-secrets
|
||||
cargo build --release --bin souveraine-machined
|
||||
cargo build --release --bin souveraine-admit
|
||||
for b in souveraine-secrets souveraine-machined souveraine-admit; do
|
||||
B="$CARGO_TARGET_DIR/release/$b"
|
||||
file "$B" | grep -q 'x86-64' || { echo "$b is not x86_64" >&2; exit 1; }
|
||||
cp "$B" "$b-x86_64"
|
||||
done
|
||||
|
||||
# sessiond for x86_64 as well. It was skipped here with a comment
|
||||
# calling it deliberate; it never was (Casey, 2026-08-02) — the
|
||||
# laptop hit lock-screen errors with it once and it stayed
|
||||
# phone-only by inertia. That inertia is now load-bearing in the
|
||||
# wrong direction: viewtop targets the laptop screen too
|
||||
# (VIEWTOP-AND-DENIAL.md), and panel power, the LockedHint report and
|
||||
# button reporting all assume the daemon is there — so no session
|
||||
# authority on x86 means no viewtop on the laptop.
|
||||
#
|
||||
# Building and packaging are not enabling. The package-owned user
|
||||
# unit ships on both architectures, but package installation neither
|
||||
# enables nor starts it; target activation remains separately tested.
|
||||
cargo build --release --features sessiond --bin souveraine-sessiond
|
||||
B="$CARGO_TARGET_DIR/release/souveraine-sessiond"
|
||||
file "$B" | grep -q 'x86-64' || { echo "sessiond is not x86_64" >&2; exit 1; }
|
||||
cp "$B" souveraine-sessiond-x86_64
|
||||
|
||||
# sensord for x86_64 too. It stopped being phone-only when charge
|
||||
# became a source: sessiond no longer probes the supplies itself, so
|
||||
# a machine without the reporter has no charge evidence at all — and
|
||||
# the laptop is a machine with a battery.
|
||||
cargo build --release --bin souveraine-sensord
|
||||
B="$CARGO_TARGET_DIR/release/souveraine-sensord"
|
||||
file "$B" | grep -q 'x86-64' || { echo "sensord is not x86_64" >&2; exit 1; }
|
||||
cp "$B" souveraine-sensord-x86_64
|
||||
|
||||
sha256sum souveraine-aarch64 souveraine-x86_64 \
|
||||
souveraine-sessiond-aarch64 souveraine-sensord-aarch64 \
|
||||
souveraine-usb-kvm-aarch64 \
|
||||
souveraine-secrets-aarch64 souveraine-secrets-x86_64 \
|
||||
souveraine-machined-aarch64 souveraine-machined-x86_64 \
|
||||
souveraine-admit-aarch64 souveraine-admit-x86_64 \
|
||||
souveraine-sessiond-x86_64 souveraine-sensord-x86_64 \
|
||||
> souveraine-binaries.sha256
|
||||
|
||||
- name: Build UPower fork (per-arch install trees)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# The fork is a git submodule of Souveraine. The runner checkout
|
||||
# above is not recursive; clone it explicitly with the same LAN
|
||||
# token used for tuie, pinned to the submodule's recorded commit.
|
||||
UPOWER_URL=$(git config --file .gitmodules submodule."packaging/upower-souveraine".url)
|
||||
UPOWER_SHA=$(git ls-tree HEAD packaging/upower-souveraine | awk '{print $3}')
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "$UPOWER_URL" "$GITHUB_WORKSPACE/src/upower"
|
||||
git -C "$GITHUB_WORKSPACE/src/upower" checkout "$UPOWER_SHA"
|
||||
|
||||
SYSROOT="$HOME/aarch64-sysroot"
|
||||
# .cargo/aarch64-pkg-config reads SOUVERAINE_AARCH64_SYSROOT and falls
|
||||
# back to /usr/aarch64-linux-gnu, which has no glib. Steps do not share
|
||||
# environment, so exporting it in the binaries step does not reach here.
|
||||
export SOUVERAINE_AARCH64_SYSROOT="$SYSROOT"
|
||||
|
||||
# --- x86_64: native build on the archdev host ---
|
||||
(
|
||||
cd "$GITHUB_WORKSPACE/src/upower"
|
||||
# gtkdoc-scan is not installed on the runner, so gtk-doc=true fails
|
||||
# here too; man needs the same toolchain. Introspection is left ON
|
||||
# for the native build — it works, and costs nothing.
|
||||
meson setup build-x86_64 \
|
||||
--prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
-Dsystemdsystemunitdir=/usr/lib/systemd/system \
|
||||
-Dgtk-doc=false -Dman=false
|
||||
meson compile -C build-x86_64
|
||||
DESTDIR="$GITHUB_WORKSPACE/src/upower/dest-x86_64" meson install -C build-x86_64
|
||||
)
|
||||
tar -C "$GITHUB_WORKSPACE/src/upower/dest-x86_64" \
|
||||
-cf "$GITHUB_WORKSPACE/src/Souveraine/upower-tree-x86_64.tar" .
|
||||
|
||||
# --- aarch64: cross build against the same sysroot souveraine uses ---
|
||||
CROSS="$GITHUB_WORKSPACE/src/upower/aarch64-cross.ini"
|
||||
cat > "$CROSS" <<EOF
|
||||
[binaries]
|
||||
c = 'aarch64-linux-gnu-gcc'
|
||||
cpp = 'aarch64-linux-gnu-g++'
|
||||
ar = 'aarch64-linux-gnu-ar'
|
||||
strip = 'aarch64-linux-gnu-strip'
|
||||
pkgconfig = '$PWD/.cargo/aarch64-pkg-config'
|
||||
# gobject-introspection is on by default and RUNS the aarch64 binary
|
||||
# it just built (g-ir-scanner). qemu needs -L to find the aarch64
|
||||
# loader; binfmt is registered on the Proxmox host so this works
|
||||
# inside the unprivileged archdev container.
|
||||
exe_wrapper = ['qemu-aarch64-static', '-L', '$SYSROOT']
|
||||
[built-in options]
|
||||
c_args = ['--sysroot=$SYSROOT']
|
||||
cpp_args = ['--sysroot=$SYSROOT']
|
||||
# -L as well as --sysroot: libc.so/libbsd.so in the sysroot are linker
|
||||
# SCRIPTS naming bare paths (libm, libmvec, -lmd), which ld resolves
|
||||
# against its own prefix and misses without an explicit search dir.
|
||||
c_link_args = ['--sysroot=$SYSROOT', '-L$SYSROOT/usr/lib']
|
||||
cpp_link_args = ['--sysroot=$SYSROOT', '-L$SYSROOT/usr/lib']
|
||||
[host_machine]
|
||||
system = 'linux'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
EOF
|
||||
(
|
||||
cd "$GITHUB_WORKSPACE/src/upower"
|
||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||
PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_SYSROOT_DIR="$SYSROOT" \
|
||||
# gtk-doc/man need host doc tooling, and introspection needs a
|
||||
# g-ir-scanner that can scan an aarch64 build (the host copy reads
|
||||
# its data files out of the sysroot and fails). None of the three
|
||||
# ship anything the phone uses — quickshell's UPower module is
|
||||
# native Qt, and the device carries no UPowerGlib typelib.
|
||||
meson setup build-aarch64 \
|
||||
--prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
-Dsystemdsystemunitdir=/usr/lib/systemd/system \
|
||||
-Dgtk-doc=false -Dman=false -Dintrospection=disabled \
|
||||
--cross-file "$CROSS"
|
||||
meson compile -C build-aarch64
|
||||
DESTDIR="$GITHUB_WORKSPACE/src/upower/dest-aarch64" meson install -C build-aarch64
|
||||
)
|
||||
# Sanity: the aarch64 upowerd must actually be aarch64.
|
||||
# It installs to libexec/, not lib/ — the old path made this check
|
||||
# fail even on a good build.
|
||||
file "$GITHUB_WORKSPACE/src/upower/dest-aarch64/usr/libexec/upowerd" \
|
||||
| grep -q aarch64 || { echo "aarch64 upowerd is not aarch64" >&2; exit 1; }
|
||||
# Cross-install leak. upower asks pkg-config for the udev dir, and
|
||||
# with a sysroot in play that answer comes back sysroot-prefixed, so
|
||||
# the rules and hwdb install under $DESTDIR/home/casey/aarch64-sysroot/
|
||||
# instead of /usr/lib/udev. systemdsystemunitdir is passed explicitly
|
||||
# above for exactly this reason; the udev dir was missed. Measured on
|
||||
# the phone 2026-07-27: all four of 60-upower-battery.{rules,hwdb},
|
||||
# 95-upower-wup.rules and 95-upower-hid.hwdb were absent from
|
||||
# /usr/lib/udev and present only under the junk path — so the fork's
|
||||
# udev quirks had never once been applied on the device.
|
||||
# Fold any leaked tree back before the tar, then refuse to ship a
|
||||
# tree containing anything but the three prefixes upower legitimately
|
||||
# installs into: usr/, etc/ (UPower.conf) and var/ (var/lib/upower).
|
||||
# Leaving var/ out of that list is what failed run 1234.
|
||||
DEST="$GITHUB_WORKSPACE/src/upower/dest-aarch64"
|
||||
LEAK="$DEST$HOME/aarch64-sysroot"
|
||||
if [ -d "$LEAK" ]; then
|
||||
echo "cross install leaked into $HOME/aarch64-sysroot — relocating"
|
||||
cp -a "$LEAK/." "$DEST/"
|
||||
rm -rf "$LEAK"
|
||||
# Removing the sysroot subtree leaves its empty parents behind
|
||||
# ($DEST/home/casey), and an empty directory still ships AND still
|
||||
# trips the stray check below — which is how runs 1234 and 1248
|
||||
# failed. The PKGBUILD copy of this had the rmdir; this one did not.
|
||||
rmdir -p --ignore-fail-on-non-empty "$(dirname "$LEAK")" 2>/dev/null || true
|
||||
fi
|
||||
stray=$(find "$DEST" -mindepth 1 -maxdepth 1 \
|
||||
! -name usr ! -name etc ! -name var -printf '%f\n')
|
||||
if [ -n "$stray" ]; then
|
||||
echo "upower tree would ship outside usr/, etc/ and var/: $stray" >&2
|
||||
exit 1
|
||||
fi
|
||||
# And the rules must have landed where udev actually reads them.
|
||||
for f in usr/lib/udev/rules.d/60-upower-battery.rules \
|
||||
usr/lib/udev/hwdb.d/60-upower-battery.hwdb; do
|
||||
[ -e "$DEST/$f" ] || { echo "upower tree is missing $f" >&2; exit 1; }
|
||||
done
|
||||
tar -C "$GITHUB_WORKSPACE/src/upower/dest-aarch64" \
|
||||
-cf "$GITHUB_WORKSPACE/src/Souveraine/upower-tree-aarch64.tar" .
|
||||
|
||||
- name: Stage the shell surface for packaging
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# deploy.sh composes ~/.config/quickshell/souveraine from repo files
|
||||
# (symlinks) plus ii dirs borrowed from the ii-base pin. Run it in a
|
||||
# scratch HOME and tar the result dereferenced, so the package is
|
||||
# self-contained real files and never ships dangling links.
|
||||
SHELL_STAGE="$GITHUB_WORKSPACE/shell-stage"
|
||||
SHELL_TAR="$GITHUB_WORKSPACE/shell-package-input"
|
||||
rm -rf "$SHELL_STAGE" "$SHELL_TAR"
|
||||
mkdir -p "$SHELL_STAGE" "$SHELL_TAR"
|
||||
HOME="$SHELL_STAGE" ./surfaces/quickshell/deploy.sh
|
||||
test -L "$SHELL_STAGE/.config/quickshell/souveraine/shell.qml"
|
||||
tar -h --zstd -C "$SHELL_STAGE/.config/quickshell" -cf "$SHELL_TAR/souveraine-shell-config.tar.zst" souveraine
|
||||
tar -tf "$SHELL_TAR/souveraine-shell-config.tar.zst" | grep -qx 'souveraine/shell.qml'
|
||||
|
||||
- name: Package and sign pacman repository
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
PKGVER="0.1.r$(git rev-list --count HEAD).g${GITHUB_SHA:0:12}"
|
||||
WORK="$GITHUB_WORKSPACE/pacman-package"
|
||||
REPO="$GITHUB_WORKSPACE/pacman-repo"
|
||||
ARCHIVE_KEY="3CD9E99E222C2A174986FC9AFF4949AA20C8E911"
|
||||
rm -rf "$WORK" "$REPO"
|
||||
mkdir -p "$WORK" "$REPO"
|
||||
|
||||
for ARCH in aarch64 x86_64; do
|
||||
PKG_WORK="$WORK/$ARCH"
|
||||
ARCH_REPO="$REPO/$ARCH"
|
||||
mkdir -p "$PKG_WORK" "$ARCH_REPO"
|
||||
cp "souveraine-$ARCH" "$PKG_WORK/souveraine-binary"
|
||||
cp "souveraine-secrets-$ARCH" "$PKG_WORK/souveraine-secrets-binary"
|
||||
cp "souveraine-machined-$ARCH" "$PKG_WORK/souveraine-machined-binary"
|
||||
cp "souveraine-admit-$ARCH" "$PKG_WORK/souveraine-admit-binary"
|
||||
cp "souveraine-sessiond-$ARCH" "$PKG_WORK/souveraine-sessiond-binary"
|
||||
cp "souveraine-sensord-$ARCH" "$PKG_WORK/souveraine-sensord-binary"
|
||||
cp packaging/souveraine.service packaging/souveraine-secrets.service \
|
||||
packaging/org.freedesktop.secrets.service \
|
||||
packaging/souveraine-machined.service \
|
||||
packaging/souveraine-sessiond.service \
|
||||
packaging/souveraine-sensord.service LICENSE "$PKG_WORK/"
|
||||
cp packaging/arch/souveraine.sysusers "$PKG_WORK/"
|
||||
# Phone-only reporters and surfaces remain aarch64-only.
|
||||
if [ "$ARCH" = aarch64 ]; then
|
||||
cp "souveraine-usb-kvm-$ARCH" "$PKG_WORK/souveraine-usb-kvm-binary"
|
||||
# The wry host. Guarded, and the PKGBUILD source line is removed
|
||||
# when it is absent: it needs webkit2gtk in the aarch64 sysroot,
|
||||
# and a missing avatar must not stop sessiond reaching the phone.
|
||||
# makepkg validates every entry in `source`, so leaving a name
|
||||
# there with no file fails the whole package.
|
||||
if [ -f "souveraine-web-$ARCH" ]; then
|
||||
cp "souveraine-web-$ARCH" "$PKG_WORK/souveraine-web-binary"
|
||||
fi
|
||||
fi
|
||||
cp packaging/souveraine-verify-trail "$PKG_WORK/"
|
||||
cp packaging/souveraine-button "$PKG_WORK/"
|
||||
cp packaging/souveraine-stepup.pam packaging/souveraine-sessiond.pam \
|
||||
packaging/org.souveraine.stepup.policy "$PKG_WORK/"
|
||||
cp packaging/arch/PKGBUILD.prebuilt "$PKG_WORK/PKGBUILD"
|
||||
(
|
||||
cd "$PKG_WORK"
|
||||
export SOUVERAINE_PKGVER="$PKGVER"
|
||||
export SOUVERAINE_PKGARCH="$ARCH"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
PKG=$(find "$PKG_WORK" -maxdepth 1 -name 'souveraine-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$PKG"
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-sessiond'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/lib/systemd/user/souveraine-sessiond.service'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-sensord'
|
||||
bsdtar -tf "$PKG" | grep -qx 'etc/pam.d/souveraine-sessiond'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/lib/sysusers.d/souveraine.conf'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-admit'
|
||||
if [ "$ARCH" = aarch64 ]; then
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-usb-kvm'
|
||||
fi
|
||||
cp "$PKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$PKG")"
|
||||
|
||||
# UPower fork package — built from packaging/upower-souveraine
|
||||
# (git submodule of the Souveraine fork of upstream upower). Built
|
||||
# natively per-arch by the "Build UPower fork" step above into
|
||||
# upower-tree-$ARCH.tar, then folded into the SAME per-arch pacman
|
||||
# database so the phone installs upower-souveraine from the same
|
||||
# edge archive it already uses for souveraine.
|
||||
# Version from UPSTREAM upower + the fork commit, NOT souveraine's
|
||||
# r-count. The hand-built package on the phone was 1.91.3+<sha>;
|
||||
# reusing souveraine's 0.1.rN here sorts LOWER, so pacman treats
|
||||
# the repo copy as a downgrade and -Syu silently skips it.
|
||||
UPOWER_VER=$(sed -nE "s/^ *version *: *'([^']+)'.*/\1/p" \
|
||||
"$GITHUB_WORKSPACE/src/upower/meson.build" | head -1)
|
||||
# '.r' not '+': a literal + in a release asset filename cannot be
|
||||
# fetched from Gitea at all — it is stored decoded as a space, and
|
||||
# both the raw + and %2B forms 404. Still sorts above the old
|
||||
# hand-built 1.91.3+<sha> because vercmp reads + as a separator too.
|
||||
UPOWER_PKGVER="${UPOWER_VER}.r$(git -C "$GITHUB_WORKSPACE/src/upower" rev-parse --short HEAD)"
|
||||
|
||||
UPOWER_WORK="$WORK/upower-$ARCH"
|
||||
mkdir -p "$UPOWER_WORK"
|
||||
cp "upower-tree-$ARCH.tar" "$UPOWER_WORK/upower-tree.tar"
|
||||
cp packaging/upower-souveraine/dbus/org.freedesktop.UPower.Device.xml \
|
||||
"$UPOWER_WORK/" 2>/dev/null \
|
||||
|| cp "$GITHUB_WORKSPACE/src/upower/dbus/org.freedesktop.UPower.Device.xml" \
|
||||
"$UPOWER_WORK/"
|
||||
cp packaging/arch/PKGBUILD.upower.prebuilt "$UPOWER_WORK/PKGBUILD"
|
||||
(
|
||||
cd "$UPOWER_WORK"
|
||||
export SOUVERAINE_PKGVER="$UPOWER_PKGVER"
|
||||
export SOUVERAINE_PKGARCH="$ARCH"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
UPKG=$(find "$UPOWER_WORK" -maxdepth 1 -name 'upower-souveraine-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$UPKG"
|
||||
# libexec/, not lib/ — same path correction as the sanity check above.
|
||||
bsdtar -tf "$UPKG" | grep -qx 'usr/libexec/upowerd'
|
||||
cp "$UPKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$UPKG")"
|
||||
|
||||
# Shell package — the composed quickshell config, arch=any, from
|
||||
# the staged tar. Same per-arch database as the binaries above.
|
||||
SHELL_WORK="$WORK/shell-$ARCH"
|
||||
mkdir -p "$SHELL_WORK"
|
||||
cp "$GITHUB_WORKSPACE/shell-package-input/souveraine-shell-config.tar.zst" "$SHELL_WORK/"
|
||||
cp packaging/arch/PKGBUILD.shell.prebuilt "$SHELL_WORK/PKGBUILD"
|
||||
cp LICENSE "$SHELL_WORK/"
|
||||
(
|
||||
cd "$SHELL_WORK"
|
||||
export SOUVERAINE_PKGVER="$PKGVER"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
SPKG=$(find "$SHELL_WORK" -maxdepth 1 -name 'souveraine-shell-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$SPKG"
|
||||
bsdtar -tf "$SPKG" | grep -qx 'etc/xdg/quickshell/souveraine/shell.qml'
|
||||
bsdtar -tf "$SPKG" | grep -qx 'usr/share/licenses/souveraine-shell/LICENSE'
|
||||
cp "$SPKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$SPKG")"
|
||||
|
||||
# The per-arch database is NOT built here. `edge` is a shared archive
|
||||
# with more than one producer in it, so the database has to be
|
||||
# read-modify-written against the live copy rather than generated
|
||||
# fresh — packaging/arch/publish-edge.sh owns that, under a lock.
|
||||
done
|
||||
cp packaging/arch/souveraine-archive-key.asc "$REPO/"
|
||||
|
||||
- name: Publish rolling edge prerelease
|
||||
env:
|
||||
# A PAT rather than the job's own GITHUB_TOKEN, so the identical
|
||||
# invocation works from a producer repo other than this one.
|
||||
EDGE_TOKEN: ${{ secrets.LOCAL_GITEA_TOKEN }}
|
||||
ARCHIVE_KEY: 3CD9E99E222C2A174986FC9AFF4949AA20C8E911
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# Additive: the release and tag are never deleted, the live databases
|
||||
# are merged into, and only this producer's own superseded packages
|
||||
# are removed. Before 2026-07-25 this step deleted and recreated the
|
||||
# release, which erased every other producer's packages on each push.
|
||||
export EDGE_TARGET_SHA="$GITHUB_SHA"
|
||||
export PRODUCER_VERSION="0.1.r$(git rev-list --count HEAD).g${GITHUB_SHA:0:12}"
|
||||
./packaging/arch/publish-edge.sh souveraine "$GITHUB_WORKSPACE/pacman-repo" \
|
||||
souveraine-aarch64 souveraine-x86_64 souveraine-binaries.sha256 \
|
||||
"$GITHUB_WORKSPACE/pacman-repo/souveraine-archive-key.asc"
|
||||
|
||||
- name: Mirror into the Gitea Arch registry (alongside edge)
|
||||
# Best-effort, never fails the publish: the registry sits alongside
|
||||
# release-assets until proven as the phone's source. A 409 means
|
||||
# (name, version) is already present immutably -- bump to republish.
|
||||
# Device-side auth is handled by souveraine-pacman-fetch; this only
|
||||
# uploads. Covers souveraine + upower-souveraine (both arches).
|
||||
env:
|
||||
EDGE_TOKEN: ${{ secrets.LOCAL_GITEA_TOKEN }}
|
||||
run: |
|
||||
set +e
|
||||
REG="$GITHUB_SERVER_URL/api/packages/${GITHUB_REPOSITORY%/*}/arch/edge"
|
||||
ok=0; skip=0; bad=0
|
||||
shopt -s nullglob
|
||||
for pkg in "$GITHUB_WORKSPACE"/pacman-repo/*/*.pkg.tar.zst; do
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" -X PUT \
|
||||
-H "Authorization: token $EDGE_TOKEN" --upload-file "$pkg" "$REG")
|
||||
case "$code" in
|
||||
200|201) echo "registry: published $(basename "$pkg")"; ok=$((ok+1));;
|
||||
409) echo "registry: present $(basename "$pkg") (bump to republish)"; skip=$((skip+1));;
|
||||
*) echo "::warning::registry $(basename "$pkg") -> HTTP $code"; bad=$((bad+1));;
|
||||
esac
|
||||
done
|
||||
echo "registry mirror: $ok new, $skip present, $bad failed"
|
||||
exit 0
|
||||
|
|
|
|||
456
.gitea/workflows/packages.yml
Normal file
456
.gitea/workflows/packages.yml
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
name: packages
|
||||
|
||||
# Internal only. Packaging, signing and archive publication run on the
|
||||
# build host where the aarch64 sysroot and the signing key live. This file
|
||||
# is not carried on the public branch; the gates in ci.yml are.
|
||||
on:
|
||||
push:
|
||||
branches: [primary, main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
aarch64-artifact:
|
||||
runs-on: archdev
|
||||
needs: [rust-test, no-ai-attribution, action-pins]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/primary'
|
||||
steps:
|
||||
- name: Clone repo + pinned tuie
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf "$GITHUB_WORKSPACE/src"
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
git -C "$GITHUB_WORKSPACE/src/Souveraine" checkout "$GITHUB_SHA"
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "${GITHUB_SERVER_URL}/Fimeg/tuie.git" "$GITHUB_WORKSPACE/src/tuie"
|
||||
git -C "$GITHUB_WORKSPACE/src/tuie" checkout aa43e335c8f5a3cd2c78e397a1a8dfe534a6bc35
|
||||
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# Persistent target dirs: fresh clones per run, warm compile cache.
|
||||
export CARGO_TARGET_DIR="$HOME/.cache/souveraine-ci-target"
|
||||
export SOUVERAINE_AARCH64_SYSROOT="$HOME/aarch64-sysroot"
|
||||
./scripts/build-cross.sh
|
||||
BIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine"
|
||||
file "$BIN" | grep -q aarch64 || { echo "built binary is not aarch64" >&2; exit 1; }
|
||||
cp "$BIN" souveraine-aarch64
|
||||
|
||||
# sessiond is a separate bin behind the sessiond feature. It was
|
||||
# hand-copied to the phone's /usr/local/bin and owned by no package,
|
||||
# so it never received updates; build and ship it with the rest.
|
||||
./scripts/build-cross.sh --features sessiond --bin souveraine-sessiond
|
||||
SBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-sessiond"
|
||||
file "$SBIN" | grep -q aarch64 || { echo "sessiond is not aarch64" >&2; exit 1; }
|
||||
cp "$SBIN" souveraine-sessiond-aarch64
|
||||
|
||||
# The sensor reporter. Same story as sessiond: it replaces shell
|
||||
# scripts that shipped in rootfs-overlay/ and drifted per-device.
|
||||
./scripts/build-cross.sh --bin souveraine-sensord
|
||||
NBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-sensord"
|
||||
file "$NBIN" | grep -q aarch64 || { echo "sensord is not aarch64" >&2; exit 1; }
|
||||
cp "$NBIN" souveraine-sensord-aarch64
|
||||
|
||||
# Full USB KVM receiver: GUD display-in and the HID return surface.
|
||||
./scripts/build-cross.sh --features usb-kvm --bin souveraine-usb-kvm
|
||||
UBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-usb-kvm"
|
||||
file "$UBIN" | grep -q aarch64 || { echo "usb-kvm is not aarch64" >&2; exit 1; }
|
||||
cp "$UBIN" souveraine-usb-kvm-aarch64
|
||||
|
||||
# The wry host: self-hosted sites as apps, and the rig she wears.
|
||||
# Needs webkit2gtk on the build sysroot; it is already installed on
|
||||
# the phone (TASK-59 phase 1 measured against it).
|
||||
# Best effort: it links against the system webview, so it needs
|
||||
# webkit2gtk in the aarch64 sysroot. Until that is there this is the
|
||||
# one binary that may legitimately not build, and it must not stop the
|
||||
# ones that decide whether the phone locks.
|
||||
if ./scripts/build-cross.sh --features web --bin souveraine-web; then
|
||||
WBIN="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/souveraine-web"
|
||||
if file "$WBIN" | grep -q aarch64; then
|
||||
cp "$WBIN" souveraine-web-aarch64
|
||||
else
|
||||
echo "::warning::web host built but is not aarch64; skipping it"
|
||||
fi
|
||||
else
|
||||
echo "::warning::the wry host did not cross-build (webkit2gtk sysroot?); skipping it"
|
||||
fi
|
||||
|
||||
# secrets rail + machined: also hand-copied on the phone until now.
|
||||
./scripts/build-cross.sh --features secrets --bin souveraine-secrets
|
||||
./scripts/build-cross.sh --bin souveraine-machined
|
||||
./scripts/build-cross.sh --bin souveraine-admit
|
||||
for b in souveraine-secrets souveraine-machined souveraine-admit; do
|
||||
B="$CARGO_TARGET_DIR/aarch64-unknown-linux-gnu/release/$b"
|
||||
file "$B" | grep -q aarch64 || { echo "$b is not aarch64" >&2; exit 1; }
|
||||
cp "$B" "$b-aarch64"
|
||||
done
|
||||
|
||||
export CARGO_TARGET_DIR="$HOME/.cache/souveraine-ci-target-x86_64"
|
||||
cargo build --release
|
||||
BIN="$CARGO_TARGET_DIR/release/souveraine"
|
||||
file "$BIN" | grep -q 'x86-64' || { echo "built binary is not x86_64" >&2; exit 1; }
|
||||
cp "$BIN" souveraine-x86_64
|
||||
|
||||
cargo build --release --features secrets --bin souveraine-secrets
|
||||
cargo build --release --bin souveraine-machined
|
||||
cargo build --release --bin souveraine-admit
|
||||
for b in souveraine-secrets souveraine-machined souveraine-admit; do
|
||||
B="$CARGO_TARGET_DIR/release/$b"
|
||||
file "$B" | grep -q 'x86-64' || { echo "$b is not x86_64" >&2; exit 1; }
|
||||
cp "$B" "$b-x86_64"
|
||||
done
|
||||
|
||||
# sessiond for x86_64 as well. It was skipped here with a comment
|
||||
# calling it deliberate; it never was (Casey, 2026-08-02) — the
|
||||
# laptop hit lock-screen errors with it once and it stayed
|
||||
# phone-only by inertia. That inertia is now load-bearing in the
|
||||
# wrong direction: viewtop targets the laptop screen too
|
||||
# (VIEWTOP-AND-DENIAL.md), and panel power, the LockedHint report and
|
||||
# button reporting all assume the daemon is there — so no session
|
||||
# authority on x86 means no viewtop on the laptop.
|
||||
#
|
||||
# Building and packaging are not enabling. The package-owned user
|
||||
# unit ships on both architectures, but package installation neither
|
||||
# enables nor starts it; target activation remains separately tested.
|
||||
cargo build --release --features sessiond --bin souveraine-sessiond
|
||||
B="$CARGO_TARGET_DIR/release/souveraine-sessiond"
|
||||
file "$B" | grep -q 'x86-64' || { echo "sessiond is not x86_64" >&2; exit 1; }
|
||||
cp "$B" souveraine-sessiond-x86_64
|
||||
|
||||
# sensord for x86_64 too. It stopped being phone-only when charge
|
||||
# became a source: sessiond no longer probes the supplies itself, so
|
||||
# a machine without the reporter has no charge evidence at all — and
|
||||
# the laptop is a machine with a battery.
|
||||
cargo build --release --bin souveraine-sensord
|
||||
B="$CARGO_TARGET_DIR/release/souveraine-sensord"
|
||||
file "$B" | grep -q 'x86-64' || { echo "sensord is not x86_64" >&2; exit 1; }
|
||||
cp "$B" souveraine-sensord-x86_64
|
||||
|
||||
sha256sum souveraine-aarch64 souveraine-x86_64 \
|
||||
souveraine-sessiond-aarch64 souveraine-sensord-aarch64 \
|
||||
souveraine-usb-kvm-aarch64 \
|
||||
souveraine-secrets-aarch64 souveraine-secrets-x86_64 \
|
||||
souveraine-machined-aarch64 souveraine-machined-x86_64 \
|
||||
souveraine-admit-aarch64 souveraine-admit-x86_64 \
|
||||
souveraine-sessiond-x86_64 souveraine-sensord-x86_64 \
|
||||
> souveraine-binaries.sha256
|
||||
|
||||
- name: Build UPower fork (per-arch install trees)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# The fork is a git submodule of Souveraine. The runner checkout
|
||||
# above is not recursive; clone it explicitly with the same LAN
|
||||
# token used for tuie, pinned to the submodule's recorded commit.
|
||||
UPOWER_URL=$(git config --file .gitmodules submodule."packaging/upower-souveraine".url)
|
||||
UPOWER_SHA=$(git ls-tree HEAD packaging/upower-souveraine | awk '{print $3}')
|
||||
git -c http.extraheader="Authorization: token ${{ secrets.LOCAL_GITEA_TOKEN }}" \
|
||||
clone "$UPOWER_URL" "$GITHUB_WORKSPACE/src/upower"
|
||||
git -C "$GITHUB_WORKSPACE/src/upower" checkout "$UPOWER_SHA"
|
||||
|
||||
SYSROOT="$HOME/aarch64-sysroot"
|
||||
# .cargo/aarch64-pkg-config reads SOUVERAINE_AARCH64_SYSROOT and falls
|
||||
# back to /usr/aarch64-linux-gnu, which has no glib. Steps do not share
|
||||
# environment, so exporting it in the binaries step does not reach here.
|
||||
export SOUVERAINE_AARCH64_SYSROOT="$SYSROOT"
|
||||
|
||||
# --- x86_64: native build on the archdev host ---
|
||||
(
|
||||
cd "$GITHUB_WORKSPACE/src/upower"
|
||||
# gtkdoc-scan is not installed on the runner, so gtk-doc=true fails
|
||||
# here too; man needs the same toolchain. Introspection is left ON
|
||||
# for the native build — it works, and costs nothing.
|
||||
meson setup build-x86_64 \
|
||||
--prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
-Dsystemdsystemunitdir=/usr/lib/systemd/system \
|
||||
-Dgtk-doc=false -Dman=false
|
||||
meson compile -C build-x86_64
|
||||
DESTDIR="$GITHUB_WORKSPACE/src/upower/dest-x86_64" meson install -C build-x86_64
|
||||
)
|
||||
tar -C "$GITHUB_WORKSPACE/src/upower/dest-x86_64" \
|
||||
-cf "$GITHUB_WORKSPACE/src/Souveraine/upower-tree-x86_64.tar" .
|
||||
|
||||
# --- aarch64: cross build against the same sysroot souveraine uses ---
|
||||
CROSS="$GITHUB_WORKSPACE/src/upower/aarch64-cross.ini"
|
||||
cat > "$CROSS" <<EOF
|
||||
[binaries]
|
||||
c = 'aarch64-linux-gnu-gcc'
|
||||
cpp = 'aarch64-linux-gnu-g++'
|
||||
ar = 'aarch64-linux-gnu-ar'
|
||||
strip = 'aarch64-linux-gnu-strip'
|
||||
pkgconfig = '$PWD/.cargo/aarch64-pkg-config'
|
||||
# gobject-introspection is on by default and RUNS the aarch64 binary
|
||||
# it just built (g-ir-scanner). qemu needs -L to find the aarch64
|
||||
# loader; binfmt is registered on the Proxmox host so this works
|
||||
# inside the unprivileged archdev container.
|
||||
exe_wrapper = ['qemu-aarch64-static', '-L', '$SYSROOT']
|
||||
[built-in options]
|
||||
c_args = ['--sysroot=$SYSROOT']
|
||||
cpp_args = ['--sysroot=$SYSROOT']
|
||||
# -L as well as --sysroot: libc.so/libbsd.so in the sysroot are linker
|
||||
# SCRIPTS naming bare paths (libm, libmvec, -lmd), which ld resolves
|
||||
# against its own prefix and misses without an explicit search dir.
|
||||
c_link_args = ['--sysroot=$SYSROOT', '-L$SYSROOT/usr/lib']
|
||||
cpp_link_args = ['--sysroot=$SYSROOT', '-L$SYSROOT/usr/lib']
|
||||
[host_machine]
|
||||
system = 'linux'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
EOF
|
||||
(
|
||||
cd "$GITHUB_WORKSPACE/src/upower"
|
||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||
PKG_CONFIG_LIBDIR="$SYSROOT/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_SYSROOT_DIR="$SYSROOT" \
|
||||
# gtk-doc/man need host doc tooling, and introspection needs a
|
||||
# g-ir-scanner that can scan an aarch64 build (the host copy reads
|
||||
# its data files out of the sysroot and fails). None of the three
|
||||
# ship anything the phone uses — quickshell's UPower module is
|
||||
# native Qt, and the device carries no UPowerGlib typelib.
|
||||
meson setup build-aarch64 \
|
||||
--prefix=/usr --sysconfdir=/etc --localstatedir=/var \
|
||||
-Dsystemdsystemunitdir=/usr/lib/systemd/system \
|
||||
-Dgtk-doc=false -Dman=false -Dintrospection=disabled \
|
||||
--cross-file "$CROSS"
|
||||
meson compile -C build-aarch64
|
||||
DESTDIR="$GITHUB_WORKSPACE/src/upower/dest-aarch64" meson install -C build-aarch64
|
||||
)
|
||||
# Sanity: the aarch64 upowerd must actually be aarch64.
|
||||
# It installs to libexec/, not lib/ — the old path made this check
|
||||
# fail even on a good build.
|
||||
file "$GITHUB_WORKSPACE/src/upower/dest-aarch64/usr/libexec/upowerd" \
|
||||
| grep -q aarch64 || { echo "aarch64 upowerd is not aarch64" >&2; exit 1; }
|
||||
# Cross-install leak. upower asks pkg-config for the udev dir, and
|
||||
# with a sysroot in play that answer comes back sysroot-prefixed, so
|
||||
# the rules and hwdb install under $DESTDIR/home/casey/aarch64-sysroot/
|
||||
# instead of /usr/lib/udev. systemdsystemunitdir is passed explicitly
|
||||
# above for exactly this reason; the udev dir was missed. Measured on
|
||||
# the phone 2026-07-27: all four of 60-upower-battery.{rules,hwdb},
|
||||
# 95-upower-wup.rules and 95-upower-hid.hwdb were absent from
|
||||
# /usr/lib/udev and present only under the junk path — so the fork's
|
||||
# udev quirks had never once been applied on the device.
|
||||
# Fold any leaked tree back before the tar, then refuse to ship a
|
||||
# tree containing anything but the three prefixes upower legitimately
|
||||
# installs into: usr/, etc/ (UPower.conf) and var/ (var/lib/upower).
|
||||
# Leaving var/ out of that list is what failed run 1234.
|
||||
DEST="$GITHUB_WORKSPACE/src/upower/dest-aarch64"
|
||||
LEAK="$DEST$HOME/aarch64-sysroot"
|
||||
if [ -d "$LEAK" ]; then
|
||||
echo "cross install leaked into $HOME/aarch64-sysroot — relocating"
|
||||
cp -a "$LEAK/." "$DEST/"
|
||||
rm -rf "$LEAK"
|
||||
# Removing the sysroot subtree leaves its empty parents behind
|
||||
# ($DEST/home/casey), and an empty directory still ships AND still
|
||||
# trips the stray check below — which is how runs 1234 and 1248
|
||||
# failed. The PKGBUILD copy of this had the rmdir; this one did not.
|
||||
rmdir -p --ignore-fail-on-non-empty "$(dirname "$LEAK")" 2>/dev/null || true
|
||||
fi
|
||||
stray=$(find "$DEST" -mindepth 1 -maxdepth 1 \
|
||||
! -name usr ! -name etc ! -name var -printf '%f\n')
|
||||
if [ -n "$stray" ]; then
|
||||
echo "upower tree would ship outside usr/, etc/ and var/: $stray" >&2
|
||||
exit 1
|
||||
fi
|
||||
# And the rules must have landed where udev actually reads them.
|
||||
for f in usr/lib/udev/rules.d/60-upower-battery.rules \
|
||||
usr/lib/udev/hwdb.d/60-upower-battery.hwdb; do
|
||||
[ -e "$DEST/$f" ] || { echo "upower tree is missing $f" >&2; exit 1; }
|
||||
done
|
||||
tar -C "$GITHUB_WORKSPACE/src/upower/dest-aarch64" \
|
||||
-cf "$GITHUB_WORKSPACE/src/Souveraine/upower-tree-aarch64.tar" .
|
||||
|
||||
- name: Stage the shell surface for packaging
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# deploy.sh composes ~/.config/quickshell/souveraine from repo files
|
||||
# (symlinks) plus ii dirs borrowed from the ii-base pin. Run it in a
|
||||
# scratch HOME and tar the result dereferenced, so the package is
|
||||
# self-contained real files and never ships dangling links.
|
||||
SHELL_STAGE="$GITHUB_WORKSPACE/shell-stage"
|
||||
SHELL_TAR="$GITHUB_WORKSPACE/shell-package-input"
|
||||
rm -rf "$SHELL_STAGE" "$SHELL_TAR"
|
||||
mkdir -p "$SHELL_STAGE" "$SHELL_TAR"
|
||||
HOME="$SHELL_STAGE" ./surfaces/quickshell/deploy.sh
|
||||
test -L "$SHELL_STAGE/.config/quickshell/souveraine/shell.qml"
|
||||
tar -h --zstd -C "$SHELL_STAGE/.config/quickshell" -cf "$SHELL_TAR/souveraine-shell-config.tar.zst" souveraine
|
||||
tar -tf "$SHELL_TAR/souveraine-shell-config.tar.zst" | grep -qx 'souveraine/shell.qml'
|
||||
|
||||
- name: Package and sign pacman repository
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
PKGVER="0.1.r$(git rev-list --count HEAD).g${GITHUB_SHA:0:12}"
|
||||
WORK="$GITHUB_WORKSPACE/pacman-package"
|
||||
REPO="$GITHUB_WORKSPACE/pacman-repo"
|
||||
ARCHIVE_KEY="3CD9E99E222C2A174986FC9AFF4949AA20C8E911"
|
||||
rm -rf "$WORK" "$REPO"
|
||||
mkdir -p "$WORK" "$REPO"
|
||||
|
||||
for ARCH in aarch64 x86_64; do
|
||||
PKG_WORK="$WORK/$ARCH"
|
||||
ARCH_REPO="$REPO/$ARCH"
|
||||
mkdir -p "$PKG_WORK" "$ARCH_REPO"
|
||||
cp "souveraine-$ARCH" "$PKG_WORK/souveraine-binary"
|
||||
cp "souveraine-secrets-$ARCH" "$PKG_WORK/souveraine-secrets-binary"
|
||||
cp "souveraine-machined-$ARCH" "$PKG_WORK/souveraine-machined-binary"
|
||||
cp "souveraine-admit-$ARCH" "$PKG_WORK/souveraine-admit-binary"
|
||||
cp "souveraine-sessiond-$ARCH" "$PKG_WORK/souveraine-sessiond-binary"
|
||||
cp "souveraine-sensord-$ARCH" "$PKG_WORK/souveraine-sensord-binary"
|
||||
cp packaging/souveraine.service packaging/souveraine-secrets.service \
|
||||
packaging/org.freedesktop.secrets.service \
|
||||
packaging/souveraine-machined.service \
|
||||
packaging/souveraine-sessiond.service \
|
||||
packaging/souveraine-sensord.service LICENSE "$PKG_WORK/"
|
||||
cp packaging/arch/souveraine.sysusers "$PKG_WORK/"
|
||||
# Phone-only reporters and surfaces remain aarch64-only.
|
||||
if [ "$ARCH" = aarch64 ]; then
|
||||
cp "souveraine-usb-kvm-$ARCH" "$PKG_WORK/souveraine-usb-kvm-binary"
|
||||
# The wry host. Guarded, and the PKGBUILD source line is removed
|
||||
# when it is absent: it needs webkit2gtk in the aarch64 sysroot,
|
||||
# and a missing avatar must not stop sessiond reaching the phone.
|
||||
# makepkg validates every entry in `source`, so leaving a name
|
||||
# there with no file fails the whole package.
|
||||
if [ -f "souveraine-web-$ARCH" ]; then
|
||||
cp "souveraine-web-$ARCH" "$PKG_WORK/souveraine-web-binary"
|
||||
fi
|
||||
fi
|
||||
cp packaging/souveraine-verify-trail "$PKG_WORK/"
|
||||
cp packaging/souveraine-button "$PKG_WORK/"
|
||||
cp packaging/souveraine-stepup.pam packaging/souveraine-sessiond.pam \
|
||||
packaging/org.souveraine.stepup.policy "$PKG_WORK/"
|
||||
cp packaging/arch/PKGBUILD.prebuilt "$PKG_WORK/PKGBUILD"
|
||||
(
|
||||
cd "$PKG_WORK"
|
||||
export SOUVERAINE_PKGVER="$PKGVER"
|
||||
export SOUVERAINE_PKGARCH="$ARCH"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
PKG=$(find "$PKG_WORK" -maxdepth 1 -name 'souveraine-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$PKG"
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-sessiond'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/lib/systemd/user/souveraine-sessiond.service'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-sensord'
|
||||
bsdtar -tf "$PKG" | grep -qx 'etc/pam.d/souveraine-sessiond'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/lib/sysusers.d/souveraine.conf'
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-admit'
|
||||
if [ "$ARCH" = aarch64 ]; then
|
||||
bsdtar -tf "$PKG" | grep -qx 'usr/bin/souveraine-usb-kvm'
|
||||
fi
|
||||
cp "$PKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$PKG")"
|
||||
|
||||
# UPower fork package — built from packaging/upower-souveraine
|
||||
# (git submodule of the Souveraine fork of upstream upower). Built
|
||||
# natively per-arch by the "Build UPower fork" step above into
|
||||
# upower-tree-$ARCH.tar, then folded into the SAME per-arch pacman
|
||||
# database so the phone installs upower-souveraine from the same
|
||||
# edge archive it already uses for souveraine.
|
||||
# Version from UPSTREAM upower + the fork commit, NOT souveraine's
|
||||
# r-count. The hand-built package on the phone was 1.91.3+<sha>;
|
||||
# reusing souveraine's 0.1.rN here sorts LOWER, so pacman treats
|
||||
# the repo copy as a downgrade and -Syu silently skips it.
|
||||
UPOWER_VER=$(sed -nE "s/^ *version *: *'([^']+)'.*/\1/p" \
|
||||
"$GITHUB_WORKSPACE/src/upower/meson.build" | head -1)
|
||||
# '.r' not '+': a literal + in a release asset filename cannot be
|
||||
# fetched from Gitea at all — it is stored decoded as a space, and
|
||||
# both the raw + and %2B forms 404. Still sorts above the old
|
||||
# hand-built 1.91.3+<sha> because vercmp reads + as a separator too.
|
||||
UPOWER_PKGVER="${UPOWER_VER}.r$(git -C "$GITHUB_WORKSPACE/src/upower" rev-parse --short HEAD)"
|
||||
|
||||
UPOWER_WORK="$WORK/upower-$ARCH"
|
||||
mkdir -p "$UPOWER_WORK"
|
||||
cp "upower-tree-$ARCH.tar" "$UPOWER_WORK/upower-tree.tar"
|
||||
cp packaging/upower-souveraine/dbus/org.freedesktop.UPower.Device.xml \
|
||||
"$UPOWER_WORK/" 2>/dev/null \
|
||||
|| cp "$GITHUB_WORKSPACE/src/upower/dbus/org.freedesktop.UPower.Device.xml" \
|
||||
"$UPOWER_WORK/"
|
||||
cp packaging/arch/PKGBUILD.upower.prebuilt "$UPOWER_WORK/PKGBUILD"
|
||||
(
|
||||
cd "$UPOWER_WORK"
|
||||
export SOUVERAINE_PKGVER="$UPOWER_PKGVER"
|
||||
export SOUVERAINE_PKGARCH="$ARCH"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
UPKG=$(find "$UPOWER_WORK" -maxdepth 1 -name 'upower-souveraine-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$UPKG"
|
||||
# libexec/, not lib/ — same path correction as the sanity check above.
|
||||
bsdtar -tf "$UPKG" | grep -qx 'usr/libexec/upowerd'
|
||||
cp "$UPKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$UPKG")"
|
||||
|
||||
# Shell package — the composed quickshell config, arch=any, from
|
||||
# the staged tar. Same per-arch database as the binaries above.
|
||||
SHELL_WORK="$WORK/shell-$ARCH"
|
||||
mkdir -p "$SHELL_WORK"
|
||||
cp "$GITHUB_WORKSPACE/shell-package-input/souveraine-shell-config.tar.zst" "$SHELL_WORK/"
|
||||
cp packaging/arch/PKGBUILD.shell.prebuilt "$SHELL_WORK/PKGBUILD"
|
||||
cp LICENSE "$SHELL_WORK/"
|
||||
(
|
||||
cd "$SHELL_WORK"
|
||||
export SOUVERAINE_PKGVER="$PKGVER"
|
||||
CARCH="$ARCH" makepkg --nodeps --noconfirm --cleanbuild
|
||||
)
|
||||
SPKG=$(find "$SHELL_WORK" -maxdepth 1 -name 'souveraine-shell-*.pkg.tar.zst' -print -quit)
|
||||
test -n "$SPKG"
|
||||
bsdtar -tf "$SPKG" | grep -qx 'etc/xdg/quickshell/souveraine/shell.qml'
|
||||
bsdtar -tf "$SPKG" | grep -qx 'usr/share/licenses/souveraine-shell/LICENSE'
|
||||
cp "$SPKG" "$ARCH_REPO/"
|
||||
gpg --batch --yes --local-user "$ARCHIVE_KEY" \
|
||||
--detach-sign "$ARCH_REPO/$(basename "$SPKG")"
|
||||
|
||||
# The per-arch database is NOT built here. `edge` is a shared archive
|
||||
# with more than one producer in it, so the database has to be
|
||||
# read-modify-written against the live copy rather than generated
|
||||
# fresh — packaging/arch/publish-edge.sh owns that, under a lock.
|
||||
done
|
||||
cp packaging/arch/souveraine-archive-key.asc "$REPO/"
|
||||
|
||||
- name: Publish rolling edge prerelease
|
||||
env:
|
||||
# A PAT rather than the job's own GITHUB_TOKEN, so the identical
|
||||
# invocation works from a producer repo other than this one.
|
||||
EDGE_TOKEN: ${{ secrets.LOCAL_GITEA_TOKEN }}
|
||||
ARCHIVE_KEY: 3CD9E99E222C2A174986FC9AFF4949AA20C8E911
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd "$GITHUB_WORKSPACE/src/Souveraine"
|
||||
# Additive: the release and tag are never deleted, the live databases
|
||||
# are merged into, and only this producer's own superseded packages
|
||||
# are removed. Before 2026-07-25 this step deleted and recreated the
|
||||
# release, which erased every other producer's packages on each push.
|
||||
export EDGE_TARGET_SHA="$GITHUB_SHA"
|
||||
export PRODUCER_VERSION="0.1.r$(git rev-list --count HEAD).g${GITHUB_SHA:0:12}"
|
||||
./packaging/arch/publish-edge.sh souveraine "$GITHUB_WORKSPACE/pacman-repo" \
|
||||
souveraine-aarch64 souveraine-x86_64 souveraine-binaries.sha256 \
|
||||
"$GITHUB_WORKSPACE/pacman-repo/souveraine-archive-key.asc"
|
||||
|
||||
- name: Mirror into the Gitea Arch registry (alongside edge)
|
||||
# Best-effort, never fails the publish: the registry sits alongside
|
||||
# release-assets until proven as the phone's source. A 409 means
|
||||
# (name, version) is already present immutably -- bump to republish.
|
||||
# Device-side auth is handled by souveraine-pacman-fetch; this only
|
||||
# uploads. Covers souveraine + upower-souveraine (both arches).
|
||||
env:
|
||||
EDGE_TOKEN: ${{ secrets.LOCAL_GITEA_TOKEN }}
|
||||
run: |
|
||||
set +e
|
||||
REG="$GITHUB_SERVER_URL/api/packages/${GITHUB_REPOSITORY%/*}/arch/edge"
|
||||
ok=0; skip=0; bad=0
|
||||
shopt -s nullglob
|
||||
for pkg in "$GITHUB_WORKSPACE"/pacman-repo/*/*.pkg.tar.zst; do
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" -X PUT \
|
||||
-H "Authorization: token $EDGE_TOKEN" --upload-file "$pkg" "$REG")
|
||||
case "$code" in
|
||||
200|201) echo "registry: published $(basename "$pkg")"; ok=$((ok+1));;
|
||||
409) echo "registry: present $(basename "$pkg") (bump to republish)"; skip=$((skip+1));;
|
||||
*) echo "::warning::registry $(basename "$pkg") -> HTTP $code"; bad=$((bad+1));;
|
||||
esac
|
||||
done
|
||||
echo "registry mirror: $ok new, $skip present, $bad failed"
|
||||
exit 0
|
||||
Loading…
Reference in a new issue