xbps packages are zstd-compressed. The clean install image had no zstd, so tar could not list the payload and the smoke failed on a package that was fine. Install zstd in the install phase.
181 lines
8.1 KiB
Bash
Executable File
181 lines
8.1 KiB
Bash
Executable File
#!/bin/sh
|
|
# Container half of packaging/void/build-release.sh (tickets #28 and #29).
|
|
# Runs inside the pinned Void glibc image, once per phase. Each phase gets only
|
|
# the privileges it needs:
|
|
#
|
|
# build root, network on, NO signing key. Clones void-packages at the
|
|
# pinned commit, renders the voculinux build tree from the tag, and
|
|
# builds every template in smoke-gate order as the unprivileged
|
|
# builder user (xbps-src refuses root). Build scripts run here, so
|
|
# the key is never mounted in this phase.
|
|
# sign root, NO network, the RSA4096 key mounted read-only. Runs no
|
|
# build code: it indexes and signs the packages the build produced.
|
|
# install root, network on (the official repository supplies dependencies),
|
|
# NO key. In a clean image, installs voculinux from the staged signed
|
|
# repository and checks what a user sees: the version, the payload,
|
|
# the imports, and that the repository is refused until its key is
|
|
# trusted.
|
|
#
|
|
# Not executed directly: build-release.sh mounts this file and runs it with one
|
|
# phase argument. Inputs arrive as environment variables set there.
|
|
set -eu
|
|
|
|
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
KEY=/run/xbps-sign-key.pem
|
|
PKG_STAGE=/mnt/packages
|
|
XBPS_STAGE=/mnt/xbps
|
|
OFFICIAL=https://repo-default.voidlinux.org/current
|
|
|
|
fail() {
|
|
echo "container-release.sh: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
phase_build() {
|
|
: "${VOID_COMMIT:?}" "${PKGS:?}" "${XBPS_VERSION:?}" "${SDIST_NAME:?}"
|
|
export VOID_COMMIT PKGS XBPS_VERSION SDIST_NAME
|
|
|
|
xbps-install -Suy xbps >/dev/null
|
|
xbps-install -y git base-devel xtools binutils zstd openssl >/dev/null
|
|
git clone --quiet --depth 1 https://github.com/void-linux/void-packages /void-packages
|
|
git -C /void-packages fetch --quiet --depth 1 origin "$VOID_COMMIT"
|
|
git -C /void-packages checkout --quiet "$VOID_COMMIT"
|
|
cp -r /overlay/. /void-packages/srcpkgs/
|
|
id builder >/dev/null 2>&1 || useradd -m builder
|
|
|
|
# Build tree only: the committed template in the repository is never changed.
|
|
for PKG in $PKGS; do
|
|
if [ "$PKG" = voculinux ]; then
|
|
VOID_PACKAGES=/void-packages SDIST_DIR=/mnt/inputs sh /mnt/render-voculinux.sh
|
|
fi
|
|
done
|
|
|
|
chown -R builder:builder /void-packages
|
|
su -s /bin/sh builder -c 'cd /void-packages && ./xbps-src binary-bootstrap'
|
|
for PKG in $PKGS; do
|
|
echo "== xlint $PKG"
|
|
su -s /bin/sh builder -c "cd /void-packages && xlint 'srcpkgs/$PKG/template'"
|
|
echo "== xbps-src pkg $PKG"
|
|
su -s /bin/sh builder -c "cd /void-packages && ./xbps-src pkg '$PKG'"
|
|
done
|
|
|
|
# The distfile the template names must be the sdist staged for it.
|
|
su -s /bin/sh builder -c 'cd /void-packages && ./xbps-src show voculinux' >/tmp/show.txt 2>&1 ||
|
|
fail "xbps-src show voculinux failed"
|
|
grep -qF "/$SDIST_NAME" /tmp/show.txt || fail "xbps-src does not resolve distfile $SDIST_NAME"
|
|
|
|
mkdir -p "$PKG_STAGE"
|
|
for PKG in $PKGS; do
|
|
PKGFILE=$(ls /void-packages/hostdir/binpkgs/"$PKG"-[0-9]*.x86_64.xbps 2>/dev/null | head -n1)
|
|
[ -n "$PKGFILE" ] || fail "built package missing from hostdir/binpkgs: $PKG"
|
|
echo "collected: $(basename "$PKGFILE")"
|
|
cp "$PKGFILE" "$PKG_STAGE/"
|
|
done
|
|
}
|
|
|
|
phase_sign() {
|
|
: "${SIGNEDBY:?}"
|
|
[ -r "$KEY" ] && [ -s "$KEY" ] || fail "signing key is not mounted at $KEY"
|
|
mkdir -p "$XBPS_STAGE/repo"
|
|
cp "$PKG_STAGE"/*.x86_64.xbps "$XBPS_STAGE/repo/"
|
|
|
|
echo "== index the full repository"
|
|
xbps-rindex -a "$XBPS_STAGE"/repo/*.x86_64.xbps
|
|
echo "== sign every binary package"
|
|
xbps-rindex -S --privkey "$KEY" --signedby "$SIGNEDBY" "$XBPS_STAGE"/repo/*.x86_64.xbps
|
|
echo "== sign the repository metadata"
|
|
xbps-rindex -s --privkey "$KEY" --signedby "$SIGNEDBY" "$XBPS_STAGE/repo"
|
|
echo "signed repository staged: $(ls "$XBPS_STAGE/repo" | wc -l) files"
|
|
}
|
|
|
|
# The payload checks the package must pass before anyone installs it.
|
|
payload_voculinux() {
|
|
PKGFILE=$1
|
|
tar -tf "$PKGFILE" | grep -q "usr/share/applications/voculinux.desktop" ||
|
|
fail "renamed desktop entry missing from the package"
|
|
tar -tf "$PKGFILE" | grep -q "usr/share/icons/hicolor/scalable/apps/voculinux.svg" ||
|
|
fail "renamed icon missing from the package"
|
|
tar -tf "$PKGFILE" | grep -q "usr/share/voculinux/packaged" ||
|
|
fail "packaged-install marker missing from the package"
|
|
if tar -tf "$PKGFILE" | grep -E "models/|\.ggml$|\.bin$|\.pt$|\.whl$"; then
|
|
fail "package payload carries models or wheels"
|
|
fi
|
|
if tar -tf "$PKGFILE" | grep -E "usr/share/applications/vocalinux|usr/share/icons/.*/vocalinux"; then
|
|
fail "pre-rename file names in the OS-visible payload"
|
|
fi
|
|
echo "payload ok: desktop entry, icon and marker present; no models, wheels or pre-rename names"
|
|
}
|
|
|
|
# whisper.cpp must ship its Vulkan backend and no host-native CPU tuning.
|
|
payload_pywhispercpp() {
|
|
PKGFILE=$1
|
|
tar -tf "$PKGFILE" | grep -q "libggml-vulkan" || fail "the Vulkan backend is missing from $PKGFILE"
|
|
LIBMEMBER=$(tar -tf "$PKGFILE" | grep -E "libggml-cpu\.so" | head -n1)
|
|
[ -n "$LIBMEMBER" ] || fail "libggml-cpu is missing from $PKGFILE"
|
|
mkdir -p /flagscan
|
|
tar -xf "$PKGFILE" -C /flagscan "$LIBMEMBER"
|
|
LIBSCAN="/flagscan/${LIBMEMBER#./}"
|
|
if readelf -p .GCC.command.line "$LIBSCAN" 2>/dev/null | grep -E "march=native|mcpu=native|mtune=native"; then
|
|
fail "host-native CPU tuning in the shipped libggml-cpu"
|
|
fi
|
|
echo "payload ok: Vulkan backend present; no host-native tuning in libggml-cpu"
|
|
}
|
|
|
|
phase_install() {
|
|
: "${PKGS:?}" "${XBPS_VERSION:?}" "${VERSION:?}"
|
|
[ -d "$XBPS_STAGE/repo" ] && [ -d "$XBPS_STAGE/keys" ] || fail "staged repository or keys are missing"
|
|
xbps-install -Sy xbps binutils zstd >/dev/null
|
|
|
|
echo "== the signed repository is refused while its key is not trusted"
|
|
# No -y: the documented first sync must not accept an unknown key.
|
|
set +e
|
|
REFUSAL=$(xbps-install -S --repository="$XBPS_STAGE/repo" voculinux </dev/null 2>&1)
|
|
REFUSAL_RC=$?
|
|
set -e
|
|
printf '%s\n' "$REFUSAL" | grep -i "fingerprint" || true
|
|
[ "$REFUSAL_RC" -ne 0 ] || fail "the repository installed without its key being trusted"
|
|
xbps-query voculinux >/dev/null 2>&1 && fail "voculinux was installed by the untrusted attempt"
|
|
echo "refused without the key, as required (rc=$REFUSAL_RC)"
|
|
|
|
echo "== trust the staged key, then install voculinux from the signed repository"
|
|
cp "$XBPS_STAGE"/keys/*.plist /var/db/xbps/keys/
|
|
xbps-install -y --repository="$XBPS_STAGE/repo" --repository="$OFFICIAL" -S voculinux >/dev/null
|
|
xbps-query voculinux | grep -qx "pkgver: voculinux-${XBPS_VERSION}_1" ||
|
|
fail "installed package is not voculinux-${XBPS_VERSION}_1"
|
|
echo "installed: voculinux-${XBPS_VERSION}_1"
|
|
|
|
echo "== the renamed binary reports its version"
|
|
VERSION_OUT=$(voculinux --version 2>&1) || fail "voculinux --version exited non-zero"
|
|
printf '%s\n' "$VERSION_OUT"
|
|
printf '%s' "$VERSION_OUT" | grep -qF "$VERSION" || fail "voculinux --version does not show $VERSION"
|
|
|
|
echo "== payload of the built packages; every package needs a smoke recipe"
|
|
for PKG in $PKGS; do
|
|
PKGFILE=$(ls "$PKG_STAGE"/"$PKG"-[0-9]*.x86_64.xbps | head -n1)
|
|
case "$PKG" in
|
|
voculinux) payload_voculinux "$PKGFILE" ;;
|
|
python3-pywhispercpp) payload_pywhispercpp "$PKGFILE" ;;
|
|
python3-pynput | python3-PyAudio) ;; # checked by import below
|
|
*) fail "no smoke recipe for package $PKG; add one alongside its template" ;;
|
|
esac
|
|
done
|
|
|
|
echo "== imports, without a display or hardware"
|
|
# pynput: the dummy backend proves the package and its loader. The X backend
|
|
# needs an X server and is covered by the manual hardware checklist.
|
|
PYNPUT_BACKEND=dummy python3 -c "import pynput.keyboard, pynput.mouse; print('pynput dummy backend ok')"
|
|
python3 -c "import evdev; print('evdev ok')"
|
|
python3 -c "import gi; gi.require_version('Gtk', '3.0'); from gi.repository import Gtk; print('gi ok: Gtk', Gtk.get_major_version(), Gtk.get_minor_version())"
|
|
python3 -c "from pywhispercpp.model import Model; print('pywhispercpp.model ok')"
|
|
python3 -c "import pyaudio; print('pyaudio ok')"
|
|
python3 -c "import vocalinux.main, vocalinux.speech_recognition.recognition_manager; print('vocalinux imports ok')"
|
|
echo "INSTALL SMOKE OK: voculinux $VERSION installed from the signed repository"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
build) phase_build ;;
|
|
sign) phase_sign ;;
|
|
install) phase_install ;;
|
|
*) fail "usage: container-release.sh build|sign|install" ;;
|
|
esac
|