2026-07-18 15:14:54 -07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Build every narOS package into dist/pool/ with plain dpkg-deb (NAROS.md §3.1).
|
|
|
|
|
#
|
|
|
|
|
# Deliberately not debhelper: these are binary payloads, scripts, and meta-packages —
|
|
|
|
|
# a uniform stage-and-pack loop keeps the whole layer readable and buildable anywhere
|
|
|
|
|
# dpkg-deb exists (this container, CI). Graduating a package to dpkg-buildpackage later
|
|
|
|
|
# is a per-package decision, not a build-system change.
|
|
|
|
|
#
|
|
|
|
|
# build-all.sh [--arch arm64,amd64] [--channel edge|stable] [--only pkg1,pkg2]
|
|
|
|
|
#
|
|
|
|
|
# Per package dir: control (template: @VERSION@ @ARCH@), optional files/ (copied
|
|
|
|
|
# verbatim), optional stage.sh (sourced; must define stage <destdir> <arch>), optional
|
|
|
|
|
# postinst/prerm/preinst/postrm. Arch-any packages build once per requested arch and
|
|
|
|
|
# typically stage a prebuilt binary from dist/bin/<name>-<arch>; missing binaries skip
|
|
|
|
|
# the package with a warning so metadata-only iteration needs no Rust toolchain.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
PKG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
OS_DIR="$(cd "$PKG_DIR/.." && pwd)"
|
2026-07-21 19:14:46 -07:00
|
|
|
ARCHES="arm64,amd64" CHANNEL="${NAROS_CHANNEL:-edge}" ONLY="" REQUIRE=""
|
2026-07-18 15:14:54 -07:00
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
--arch) ARCHES="$2"; shift 2 ;;
|
|
|
|
|
--channel) CHANNEL="$2"; shift 2 ;;
|
|
|
|
|
--only) ONLY="$2"; shift 2 ;;
|
2026-07-21 19:14:46 -07:00
|
|
|
--require) REQUIRE="$2"; shift 2 ;;
|
2026-07-18 15:14:54 -07:00
|
|
|
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
OS_VERSION="$(cat "$OS_DIR/VERSION")"
|
|
|
|
|
POOL="$OS_DIR/dist/pool"
|
|
|
|
|
mkdir -p "$POOL"
|
|
|
|
|
|
|
|
|
|
pkg_version() { # package semver + channel tag, e.g. 0.1.0+edge26.07
|
|
|
|
|
local d="$1" base
|
|
|
|
|
base="$( [ -f "$d/VERSION" ] && cat "$d/VERSION" || echo 0.1.0 )"
|
|
|
|
|
echo "${base}+${CHANNEL}${OS_VERSION}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build_one() { # <pkgdir> <arch>
|
|
|
|
|
local d="$1" arch="$2" name ver stage out
|
|
|
|
|
name="$(basename "$d")"
|
|
|
|
|
ver="$(pkg_version "$d")"
|
|
|
|
|
stage="$(mktemp -d)"
|
|
|
|
|
trap 'rm -rf "$stage"' RETURN
|
|
|
|
|
|
|
|
|
|
[ -d "$d/files" ] && cp -a "$d/files/." "$stage/"
|
2026-07-21 02:31:12 -07:00
|
|
|
# The runtime apt source's suite must mirror the channel it was published under
|
|
|
|
|
# (dists/<channel>; NAROS.md §3.2 "Suites mirror channels"), so @CHANNEL@ is templated
|
|
|
|
|
# here the same way @VERSION@/@ARCH@ are templated into control. Targeted rather than a
|
|
|
|
|
# blanket sed so binary payloads in files/ (e.g. the keyring .gpg) are never rewritten.
|
|
|
|
|
[ -f "$stage/etc/apt/sources.list.d/naros.sources" ] && \
|
|
|
|
|
sed -i "s/@CHANNEL@/$CHANNEL/g" "$stage/etc/apt/sources.list.d/naros.sources"
|
2026-07-18 15:14:54 -07:00
|
|
|
if [ -f "$d/stage.sh" ]; then
|
|
|
|
|
# shellcheck source=/dev/null
|
|
|
|
|
( set -euo pipefail; OS_DIR="$OS_DIR" . "$d/stage.sh"; stage "$stage" "$arch" ) || {
|
|
|
|
|
echo "SKIP $name/$arch: $(cat "$stage/.skip-reason" 2>/dev/null || echo staging failed)" >&2
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mkdir -p "$stage/DEBIAN"
|
|
|
|
|
sed -e "s/@VERSION@/$ver/" -e "s/@ARCH@/$arch/" "$d/control" > "$stage/DEBIAN/control"
|
|
|
|
|
local s
|
|
|
|
|
for s in preinst postinst prerm postrm; do
|
|
|
|
|
[ -f "$d/$s" ] && install -m 0755 "$d/$s" "$stage/DEBIAN/$s"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
out="$POOL/${name}_${ver}_$(grep -Po '^Architecture: \K.*' "$stage/DEBIAN/control").deb"
|
|
|
|
|
dpkg-deb --root-owner-group -Zxz --build "$stage" "$out" > /dev/null
|
|
|
|
|
echo "built ${out#"$OS_DIR/"}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for d in "$PKG_DIR"/*/; do
|
|
|
|
|
name="$(basename "$d")"
|
|
|
|
|
[ -f "$d/control" ] || continue
|
|
|
|
|
if [ -n "$ONLY" ] && ! echo ",$ONLY," | grep -q ",$name,"; then continue; fi
|
|
|
|
|
if grep -q '^Architecture: @ARCH@' "$d/control"; then
|
|
|
|
|
IFS=, read -ra AA <<< "$ARCHES"
|
|
|
|
|
for a in "${AA[@]}"; do build_one "$d" "$a"; done
|
|
|
|
|
else
|
|
|
|
|
build_one "$d" all
|
|
|
|
|
fi
|
|
|
|
|
done
|
2026-07-21 19:14:46 -07:00
|
|
|
|
|
|
|
|
# A missing prebuilt binary only WARNS above, so that metadata-only iteration works without a Rust
|
|
|
|
|
# toolchain. That leniency is wrong for the packages a guest cannot boot without: nucleic-a11y-agent
|
|
|
|
|
# was cross-built for arm64 only, so its amd64 deb skipped silently and simply never appeared in the
|
|
|
|
|
# published repo — a gap nothing failed on. --require turns the skip into an error for a named set.
|
|
|
|
|
if [ -n "$REQUIRE" ]; then
|
|
|
|
|
missing=""
|
|
|
|
|
IFS=, read -ra RR <<< "$REQUIRE"
|
|
|
|
|
IFS=, read -ra AA <<< "$ARCHES"
|
|
|
|
|
for name in "${RR[@]}"; do
|
|
|
|
|
ver="$(pkg_version "$PKG_DIR/$name")"
|
|
|
|
|
for a in "${AA[@]}"; do
|
|
|
|
|
[ -f "$POOL/${name}_${ver}_$a.deb" ] || missing="$missing ${name}_${ver}_$a.deb"
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
if [ -n "$missing" ]; then
|
|
|
|
|
echo "FATAL: required package(s) did not build (see the SKIP lines above):$missing" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|