feat(niri): niri live config
This commit is contained in:
228
iso/_inner-build-niri-live.sh
Executable file
228
iso/_inner-build-niri-live.sh
Executable file
@@ -0,0 +1,228 @@
|
||||
#!/bin/bash
|
||||
# Runs INSIDE the docker container (as root). Invoked by iso/build-niri-live-iso.sh.
|
||||
# Niri/Wayland variant: nix prebake (shared cache with Cinnamon), adds noctalia repo.
|
||||
# Expects the project bind-mounted at /work and the cache at /cache.
|
||||
#
|
||||
# Required env (set by build-niri-live-iso.sh):
|
||||
# ARCH, REPO_URL, KEYMAP, LOCALE, ISO_PKGS, ISO_TITLE, OUT_ISO_REL,
|
||||
# INCLUDE_DIR_REL, NIX_PACKAGES_PREBAKE (optional)
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${ARCH:?}"; : "${REPO_URL:?}"; : "${KEYMAP:?}"; : "${LOCALE:?}"
|
||||
: "${ISO_PKGS:?}"; : "${ISO_TITLE:?}"; : "${OUT_ISO_REL:?}"
|
||||
: "${INCLUDE_DIR_REL:?}"
|
||||
|
||||
CACHE_DIR=/cache
|
||||
PROJECT_DIR=/work
|
||||
MKLIVE_DIR="$CACHE_DIR/void-mklive-niri" # separate clone — avoids race with Cinnamon parallel build
|
||||
INCLUDE_DIR="$PROJECT_DIR/$INCLUDE_DIR_REL"
|
||||
OUT_ISO="$PROJECT_DIR/$OUT_ISO_REL"
|
||||
|
||||
# Third-party Void repo that ships noctalia-shell + noctalia-qs.
|
||||
NOCTALIA_REPO="${NOCTALIA_REPO:-https://universalrepo.r1xelelo.workers.dev/void}"
|
||||
|
||||
export PATH="$CACHE_DIR/xbps-static/usr/bin:$PATH"
|
||||
|
||||
[[ -d "$MKLIVE_DIR" ]] || { echo "ERROR: $MKLIVE_DIR missing"; exit 1; }
|
||||
[[ -d "$INCLUDE_DIR" ]] || { echo "ERROR: $INCLUDE_DIR missing"; exit 1; }
|
||||
command -v xbps-install.static >/dev/null \
|
||||
|| { echo "ERROR: xbps-install.static not on PATH"; exit 1; }
|
||||
|
||||
mkdir -p "$(dirname "$OUT_ISO")"
|
||||
|
||||
cd "$MKLIVE_DIR"
|
||||
|
||||
# ── Pre-bake nix packages ────────────────────────────────────────────────
|
||||
# Shared with the Cinnamon build — same cache dir, same store.
|
||||
if [[ -n "${NIX_PACKAGES_PREBAKE:-}" ]]; then
|
||||
echo ">>> pre-baking nix packages"
|
||||
read -r -a _NIX_PKGS <<< "$NIX_PACKAGES_PREBAKE"
|
||||
|
||||
_NIX_CACHE="$CACHE_DIR/nix-prebake"
|
||||
_CACHE_KEY="$_NIX_CACHE/.done.$(printf '%s\n' "${_NIX_PKGS[@]}" | sort | md5sum | cut -c1-8)"
|
||||
|
||||
mkdir -p "$_NIX_CACHE"
|
||||
|
||||
if [[ -f "$_CACHE_KEY" ]] && [[ -d "$_NIX_CACHE/store" ]] && [[ -f "$_NIX_CACHE/.profile-path" ]]; then
|
||||
echo " restoring cached nix store ($(du -sh "$_NIX_CACHE/store" 2>/dev/null | cut -f1))"
|
||||
mkdir -p /nix
|
||||
rsync -a "$_NIX_CACHE/" /nix/ 2>&1 | tail -1
|
||||
else
|
||||
echo " installing nix (single-user, no-daemon)..."
|
||||
rm -rf /nix ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
|
||||
mkdir -m 0755 -p /nix
|
||||
export NIX_CONFIG="build-users-group = "
|
||||
curl -fsSL https://nixos.org/nix/install | \
|
||||
NIX_INSTALLER_TRUST_INSTALLER=1 sh -s -- --no-daemon --no-channel-add
|
||||
# shellcheck disable=SC1091
|
||||
. /root/.nix-profile/etc/profile.d/nix.sh 2>/dev/null || true
|
||||
export PATH="/root/.nix-profile/bin:/nix/var/nix/profiles/default/bin:$PATH"
|
||||
|
||||
export NIXPKGS_ALLOW_UNFREE=1
|
||||
echo " nix profile install: ${_NIX_PKGS[*]}"
|
||||
nix profile add --extra-experimental-features "nix-command flakes" \
|
||||
--impure "${_NIX_PKGS[@]}" 2>&1
|
||||
|
||||
readlink -f /root/.nix-profile > "$_NIX_CACHE/.profile-path"
|
||||
rsync -a /nix/ "$_NIX_CACHE/" 2>&1 | tail -1
|
||||
touch "$_CACHE_KEY"
|
||||
echo " cached nix store: $(du -sh "$_NIX_CACHE/store" 2>/dev/null | cut -f1)"
|
||||
fi
|
||||
|
||||
echo " staging /nix into overlay ($(du -sh /nix/store 2>/dev/null | cut -f1))"
|
||||
mkdir -p "$INCLUDE_DIR/nix"
|
||||
rsync -a /nix/ "$INCLUDE_DIR/nix/" 2>&1 | tail -1
|
||||
|
||||
_STORE_PROFILE=$(cat "$_NIX_CACHE/.profile-path" 2>/dev/null \
|
||||
|| readlink -f /root/.nix-profile 2>/dev/null || echo "")
|
||||
if [[ -n "$_STORE_PROFILE" && -d "$_STORE_PROFILE" ]]; then
|
||||
mkdir -p "$INCLUDE_DIR/etc/skel"
|
||||
ln -sf "$_STORE_PROFILE" "$INCLUDE_DIR/etc/skel/.nix-profile"
|
||||
echo " skel/.nix-profile → $_STORE_PROFILE"
|
||||
fi
|
||||
fi
|
||||
# ── end nix prebake ──────────────────────────────────────────────────────
|
||||
|
||||
# ── Noctalia: build a locally SIGNED XBPS repo ───────────────────────────
|
||||
# noctalia-qs has a broken .sig2 on the CDN (RSA signature not valid).
|
||||
# noctalia-shell's CDN key import also fails intermittently (EAGAIN).
|
||||
# Solution: download both .xbps archives directly (no sig check), create a
|
||||
# LOCAL SIGNED repo with a fresh keypair, register our public key in
|
||||
# mklive/keys/ so copy_void_keys pre-trusts it in the rootfs.
|
||||
# Our local signed repo gets HIGHEST priority (-r last = prepended first),
|
||||
# so xbps resolves and verifies both packages against our trusted key.
|
||||
echo ">>> building local signed noctalia XBPS repo (CDN .sig2 workaround)"
|
||||
_NOC_LOCAL="/tmp/noctalia-local"
|
||||
_NOC_HOME="/tmp/noc-sign-home"
|
||||
_ARCH="${ARCH:-x86_64}"
|
||||
mkdir -p "$_NOC_LOCAL" "$_NOC_HOME"
|
||||
export HOME="$_NOC_HOME"
|
||||
|
||||
# Discover exact package versions from CDN repodata (try multiple User-Agents).
|
||||
# Falls back to versions confirmed by previous build errors.
|
||||
_NOC_VERS=$(python3 - <<'PYEOF' 2>/dev/null
|
||||
import urllib.request, plistlib, tarfile, io, sys, os
|
||||
repo = os.environ.get("NOCTALIA_REPO", "https://universalrepo.r1xelelo.workers.dev/void")
|
||||
arch = os.environ.get("ARCH", "x86_64")
|
||||
want = {"noctalia-qs", "noctalia-shell"}
|
||||
found = {}
|
||||
for ua in ("curl/8.0", "xbps/0.59.2", "Mozilla/5.0 (X11; Linux x86_64)"):
|
||||
try:
|
||||
req = urllib.request.Request(f"{repo}/{arch}-repodata", headers={"User-Agent": ua})
|
||||
data = urllib.request.urlopen(req, timeout=15).read()
|
||||
tf = tarfile.open(fileobj=io.BytesIO(data))
|
||||
idx = plistlib.loads(tf.extractfile("index.plist").read())
|
||||
for pkgver, meta in idx.items():
|
||||
if isinstance(meta, dict) and meta.get("pkgname") in want:
|
||||
found[meta["pkgname"]] = pkgver
|
||||
if len(found) >= len(want):
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
# Fallback to versions confirmed by last build errors
|
||||
defaults = {"noctalia-qs": "noctalia-qs-0.0.12_0", "noctalia-shell": "noctalia-shell-4.7.6_1"}
|
||||
for pkg, ver in defaults.items():
|
||||
if pkg not in found:
|
||||
found[pkg] = ver
|
||||
for ver in found.values():
|
||||
print(ver)
|
||||
PYEOF
|
||||
)
|
||||
|
||||
# Download both .xbps archives directly (curl never checks .sig2)
|
||||
for _ver in $_NOC_VERS; do
|
||||
_fname="${_ver}.${_ARCH}.xbps"
|
||||
# Clear any cached bad sig2 from a previous failed build
|
||||
rm -f "$CACHE_DIR/xbps-niri-pkgs/${_ver}"* 2>/dev/null || true
|
||||
if [[ ! -f "$_NOC_LOCAL/$_fname" ]]; then
|
||||
echo " downloading $_fname ..."
|
||||
curl -fsSL "$NOCTALIA_REPO/$_fname" -o "$_NOC_LOCAL/$_fname" \
|
||||
|| { echo " WARNING: failed to download $_fname"; rm -f "$_NOC_LOCAL/$_fname"; }
|
||||
else
|
||||
echo " cached: $_fname"
|
||||
fi
|
||||
done
|
||||
|
||||
# Build index
|
||||
xbps-rindex.static -a "$_NOC_LOCAL"/*.xbps
|
||||
|
||||
# Generate RSA keypair. xbps-rindex requires --privkey passed explicitly.
|
||||
mkdir -p "$_NOC_HOME/.xbps-sign"
|
||||
openssl genrsa -out "$_NOC_HOME/.xbps-sign/privkey.pem" 4096 2>/dev/null
|
||||
[[ -s "$_NOC_HOME/.xbps-sign/privkey.pem" ]] \
|
||||
|| { echo "ERROR: openssl genrsa failed"; exit 1; }
|
||||
|
||||
_NOC_PRIVKEY="$_NOC_HOME/.xbps-sign/privkey.pem"
|
||||
|
||||
# Sign repodata then each package (separate calls — xbps-rindex restriction).
|
||||
xbps-rindex.static --sign --privkey "$_NOC_PRIVKEY" \
|
||||
--signedby "noctalia-local" "$_NOC_LOCAL"
|
||||
for _pkg in "$_NOC_LOCAL"/*.xbps; do
|
||||
xbps-rindex.static --sign-pkg --privkey "$_NOC_PRIVKEY" \
|
||||
--signedby "noctalia-local" "$_pkg"
|
||||
done
|
||||
|
||||
# Register public key in mklive/keys/ so copy_void_keys installs it in the rootfs.
|
||||
# xbps fingerprint = MD5 of DER-encoded public key, formatted as aa:bb:cc:...
|
||||
openssl rsa -in "$_NOC_PRIVKEY" \
|
||||
-pubout -outform DER -out "$_NOC_HOME/pubkey.der" 2>/dev/null
|
||||
_FINGERPRINT=$(md5sum "$_NOC_HOME/pubkey.der" \
|
||||
| cut -d' ' -f1 | sed 's/../&:/g; s/:$//')
|
||||
# The plist <data> field = base64 of the PEM public key (headers included)
|
||||
_PUBKEY_B64=$(openssl rsa -in "$_NOC_PRIVKEY" \
|
||||
-pubout 2>/dev/null | base64 -w 0)
|
||||
cat > "$MKLIVE_DIR/keys/$_FINGERPRINT.plist" <<KEOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>public-key</key>
|
||||
<data>$_PUBKEY_B64</data>
|
||||
<key>public-key-size</key>
|
||||
<integer>4096</integer>
|
||||
<key>signature-by</key>
|
||||
<string>noctalia-local</string>
|
||||
</dict>
|
||||
</plist>
|
||||
KEOF
|
||||
echo " local signed noctalia repo ready — key: $_FINGERPRINT"
|
||||
unset HOME
|
||||
# ── end noctalia local repo ───────────────────────────────────────────────
|
||||
|
||||
_cleanup_mklive_builds() {
|
||||
local d sub
|
||||
for d in "$MKLIVE_DIR"/mklive-build.*/; do
|
||||
[[ -d "$d" ]] || continue
|
||||
for sub in tmp-rootfs/sys tmp-rootfs/proc tmp-rootfs/dev tmp-rootfs/run \
|
||||
image/rootfs/sys image/rootfs/proc image/rootfs/dev image/rootfs/run; do
|
||||
[[ -d "$d$sub" ]] && umount -R --lazy "$d$sub" 2>/dev/null || true
|
||||
done
|
||||
rm -rf "$d" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
trap _cleanup_mklive_builds EXIT
|
||||
|
||||
# mklive prepends -r args: LAST -r = HIGHEST priority.
|
||||
# Our local signed repo is last so xbps resolves noctalia-* from it.
|
||||
./mklive.sh \
|
||||
-a "$ARCH" \
|
||||
-r "$REPO_URL" \
|
||||
-r "${REPO_URL%/current}/current/nonfree" \
|
||||
-r "$NOCTALIA_REPO" \
|
||||
-r "$_NOC_LOCAL" \
|
||||
-c "$CACHE_DIR/xbps-niri-pkgs" \
|
||||
-H "$CACHE_DIR/xbps-host-pkgs" \
|
||||
-k "$KEYMAP" \
|
||||
-l "$LOCALE" \
|
||||
-T "$ISO_TITLE" \
|
||||
-p "$ISO_PKGS" \
|
||||
-I "$INCLUDE_DIR" \
|
||||
-C "${BOOT_CMDLINE:-}" \
|
||||
-o "$OUT_ISO"
|
||||
|
||||
# Fix ownership so the host user can clean up without sudo.
|
||||
# u+rwX: sets read+write on all, execute only on directories (capital X).
|
||||
chmod -R u+rwX "$INCLUDE_DIR" 2>/dev/null || true
|
||||
chown -R "${HOST_UID:-1000}:${HOST_GID:-1000}" "$INCLUDE_DIR" 2>/dev/null || true
|
||||
chown -R "${HOST_UID:-1000}:${HOST_GID:-1000}" "$OUT_ISO" "${OUT_ISO}".* 2>/dev/null || true
|
||||
Reference in New Issue
Block a user