summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-14 16:51:22 +0900
committerAdam Malczewski <[email protected]>2026-06-14 16:51:22 +0900
commitd337b8bf0e8f4b2e54ef76d093e76e5841fcf184 (patch)
treed028ae38227b40a7f6309bf6be7384e579f3540d
parent117ad49b2f9bfb6822e94df2706fd7cd0cf2121a (diff)
downloadunbox-d337b8bf0e8f4b2e54ef76d093e76e5841fcf184.tar.gz
unbox-d337b8bf0e8f4b2e54ef76d093e76e5841fcf184.zip
packaging: remote-build helpers (distcc offload + remote pkg build)
Transparent distributed compilation: setup-distcc.sh configures ccache (prefix_command=distcc) + ~/.distcc/hosts locally and a locked-down distccd on a remote builder, so a plain ninja/meson test offloads compiles automatically and falls back to local when the remote is down. build-remote.sh (renamed from the machine-specific build-remote.sh) builds + installs the pacman package on the fast box. start-unbox now takes its wallpaper from $HOME/.config/unbox/wallpaper (or $UNBOX_WALLPAPER) instead of a hardcoded filename. Machine/network specifics (hosts, private IPs) are NOT committed: the helpers read packaging/remote.local (gitignored); packaging/remote.local.example is the committed template with setup instructions for a new network.
-rw-r--r--.gitignore4
-rwxr-xr-xpackaging/build-remote.sh33
-rw-r--r--packaging/remote.local.example31
-rwxr-xr-xpackaging/setup-distcc.sh114
-rwxr-xr-xpackaging/start-unbox9
5 files changed, 177 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
index 72ded8c..a9d1f2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,10 @@ compile_commands.json
prompts/
reports/
+# machine/network specifics for the remote-build helpers (template is committed:
+# packaging/remote.local.example). Never commit real hosts/IPs.
+packaging/remote.local
+
# meson subprojects: keep wraps + our patch files, ignore downloaded sources
subprojects/*
!subprojects/*.wrap
diff --git a/packaging/build-remote.sh b/packaging/build-remote.sh
index b522284..3c55bcc 100755
--- a/packaging/build-remote.sh
+++ b/packaging/build-remote.sh
@@ -1,39 +1,44 @@
#!/usr/bin/env bash
-# build-on-builder.sh — build the unbox pacman package on the fast box (builder)
-# and install it here on the slow CF-AX3.
+# build-remote.sh — build the unbox pacman package on a fast remote builder and
+# install it here on the slow target.
#
-# 1. rsync the working tree to builder:~/unbox
-# 2. makepkg -s there (installs build deps via sudo ON builder — you'll be
+# 1. rsync the working tree to $REMOTE_HOST:~/$REMOTE_DIR
+# 2. makepkg -s there (installs build deps via sudo ON the builder — you'll be
# prompted for that machine's sudo password)
-# 3. scp the built .pkg.tar.zst back to /tmp here
+# 3. scp the built .pkg.tar.zst back to $PKGOUT here
# 4. pacman -U here (you'll be prompted for THIS machine's sudo password)
#
-# Re-runnable: edit code, run again. Safe to Ctrl-C.
+# Builder details come from the gitignored packaging/remote.local (template:
+# remote.local.example). Re-runnable; safe to Ctrl-C.
set -euo pipefail
-REMOTE="${REMOTE:-builder}"
-REMOTE_DIR="${REMOTE_DIR:-unbox}" # ~/unbox on builder
-LOCAL_REPO="${LOCAL_REPO:-/home/user/projects/unbox}"
+HERE="$(cd "$(dirname "$0")" && pwd)"
+LOCAL_REPO="$(cd "$HERE/.." && pwd)" # repo root (this script lives in packaging/)
+# shellcheck disable=SC1091
+[ -f "$HERE/remote.local" ] && . "$HERE/remote.local"
+: "${REMOTE_HOST:?set REMOTE_HOST (see packaging/remote.local.example)}"
+REMOTE_DIR="${REMOTE_DIR:-unbox}"
PKGOUT="${PKGOUT:-/tmp/unbox-pkg}"
-echo "==> [1/4] Sync source -> $REMOTE:$REMOTE_DIR"
+echo "==> [1/4] Sync source -> $REMOTE_HOST:$REMOTE_DIR"
rsync -az --info=progress2 \
--exclude 'build*/' \
--exclude '.git/' \
--exclude '.cache/' \
+ --exclude 'packaging/remote.local' \
--exclude 'packaging/src/' \
--exclude 'packaging/pkg/' \
--exclude 'packaging/*.pkg.tar.zst' \
- "$LOCAL_REPO/" "$REMOTE:$REMOTE_DIR/"
+ "$LOCAL_REPO/" "$REMOTE_HOST:$REMOTE_DIR/"
-echo "==> [2/4] Build package on $REMOTE (installs makedeps via its sudo)"
+echo "==> [2/4] Build package on $REMOTE_HOST (installs makedeps via its sudo)"
# -t: give makepkg a tty so the remote sudo password prompt works.
# -Cf: -C cleans $srcdir first (no stale build dir -> subproject options like
# toml++ default_library=static always apply), -f overwrites the package.
# Then verify the packaged binary links NO shared toml (extract to a real file;
# readelf cannot read a non-seekable pipe). Abort loudly if it still does.
-ssh -t "$REMOTE" "
+ssh -t "$REMOTE_HOST" "
set -e
cd '$REMOTE_DIR/packaging'
makepkg -sCf --noconfirm
@@ -49,7 +54,7 @@ ssh -t "$REMOTE" "
echo "==> [3/4] Ferry the built package back -> $PKGOUT"
mkdir -p "$PKGOUT"
-scp "$REMOTE:$REMOTE_DIR/packaging/unbox-*.pkg.tar.zst" "$PKGOUT/"
+scp "$REMOTE_HOST:$REMOTE_DIR/packaging/unbox-*.pkg.tar.zst" "$PKGOUT/"
PKG="$(ls -t "$PKGOUT"/unbox-*.pkg.tar.zst | head -1)"
if [ -z "$PKG" ]; then
diff --git a/packaging/remote.local.example b/packaging/remote.local.example
new file mode 100644
index 0000000..9505a55
--- /dev/null
+++ b/packaging/remote.local.example
@@ -0,0 +1,31 @@
+# remote.local.example — template for the remote-builder settings.
+#
+# These are machine/network specifics and are deliberately NOT committed. To use
+# the remote-build helpers (setup-distcc.sh, build-remote.sh), copy this file and
+# fill in your own values:
+#
+# cp packaging/remote.local.example packaging/remote.local
+# $EDITOR packaging/remote.local # packaging/remote.local is gitignored
+#
+# Prerequisites on a NEW set of systems:
+# * A fast "builder" box reachable over a private network (Tailscale/WireGuard/
+# LAN), with the SAME gcc version as this machine (distcc plain mode requires
+# matching compilers), passwordless SSH from here, and sudo on the builder.
+# * `distcc` (and `ccache`) installed on BOTH machines.
+# Then run: packaging/setup-distcc.sh
+
+# SSH host/alias of the fast remote builder.
+REMOTE_HOST=builder.example.ts.net
+
+# Private IP of the remote builder (Tailscale/WireGuard/LAN). distccd binds
+# (--listen) ONLY to this address, so it is never exposed on public interfaces.
+REMOTE_TS=100.64.0.2
+
+# Private IP of THIS machine. distccd will --allow ONLY this address.
+LOCAL_TS=100.64.0.1
+
+# Parallel compile slots the remote offers (roughly its CPU core count).
+REMOTE_JOBS=16
+
+# (Optional) working dir name created in the builder's home for build-remote.sh.
+REMOTE_DIR=unbox
diff --git a/packaging/setup-distcc.sh b/packaging/setup-distcc.sh
new file mode 100755
index 0000000..e22ed54
--- /dev/null
+++ b/packaging/setup-distcc.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# setup-distcc.sh — make C/C++ compilation transparently offload to a fast remote
+# builder, so a plain `ninja`/`meson test` (and any tool/agent that builds) farms
+# compile jobs out automatically, falling back to local when the remote is down.
+#
+# It works by:
+# * LOCAL : ccache prefix_command=distcc (~/.config/ccache/ccache.conf) +
+# distcc host list (~/.distcc/hosts). These are read by ccache/distcc
+# regardless of shell, so the offload needs NO env vars and is fully
+# transparent (no shell-init, no per-build flags).
+# * REMOTE: a locked-down distccd (systemd) bound to the private interface,
+# accepting ONLY this machine.
+# ccache hits stay local/instant; only real compiles go remote; preprocessing and
+# linking always run locally.
+#
+# ---- Setup on a NEW network / set of systems --------------------------------
+# 1. On BOTH machines: install distcc (+ ccache) and ensure the SAME gcc
+# version (distcc plain mode requires matching compilers).
+# 2. Ensure passwordless SSH from here to the builder, and sudo on the builder.
+# 3. cp packaging/remote.local.example packaging/remote.local # fill in IPs
+# 4. packaging/setup-distcc.sh
+# Re-runnable and idempotent. Output is tee'd to $LOG.
+# -----------------------------------------------------------------------------
+
+set -uo pipefail
+
+HERE="$(cd "$(dirname "$0")" && pwd)"
+# Machine/network specifics live in the gitignored packaging/remote.local
+# (template: remote.local.example). Env vars override the file.
+# shellcheck disable=SC1091
+[ -f "$HERE/remote.local" ] && . "$HERE/remote.local"
+: "${REMOTE_HOST:?set REMOTE_HOST (see packaging/remote.local.example)}"
+: "${REMOTE_TS:?set REMOTE_TS — remote builder private IP}"
+: "${LOCAL_TS:?set LOCAL_TS — this machine's private IP}"
+: "${REMOTE_JOBS:=16}"
+PORT="${PORT:-3632}"
+
+LOG=/tmp/distcc-setup.log
+: > "$LOG"
+exec > >(tee -a "$LOG") 2>&1
+
+echo "######## distcc setup $(date -Is) ########"
+echo "remote_host=$REMOTE_HOST remote_ip=$REMOTE_TS local_ip=$LOCAL_TS jobs=$REMOTE_JOBS port=$PORT"
+echo
+
+# ---- 1. local config: transparent for every build tool ----------------------
+echo "==> [1/4] local ccache + distcc config"
+mkdir -p "$HOME/.config/ccache" "$HOME/.distcc"
+cat > "$HOME/.config/ccache/ccache.conf" <<EOF
+# On a cache MISS, ccache compiles through distcc (hosts in ~/.distcc/hosts).
+# Read for every ccache invocation regardless of shell, so distributed builds
+# are transparent. Falls back to local if the remote is unreachable.
+prefix_command = distcc
+EOF
+cat > "$HOME/.distcc/hosts" <<EOF
+# Remote builder (private network), then local fallback/overflow. Read when the
+# DISTCC_HOSTS env var is unset (so non-login/automated builds use it too).
+${REMOTE_TS}/${REMOTE_JOBS},lzo
+localhost/4
+EOF
+echo " wrote ~/.config/ccache/ccache.conf and ~/.distcc/hosts"
+echo
+
+# ---- 2. push the locked-down daemon config to the builder --------------------
+echo "==> [2/4] copy distccd config to $REMOTE_HOST:/tmp"
+tmpconf="$(mktemp)"; tmpoverride="$(mktemp)"
+cat > "$tmpconf" <<EOF
+# Managed by packaging/setup-distcc.sh
+DISTCC_ARGS="--allow ${LOCAL_TS}/32 --listen ${REMOTE_TS} --port ${PORT} --jobs ${REMOTE_JOBS} --nice 5 --log-level warning"
+EOF
+cat > "$tmpoverride" <<'EOF'
+[Unit]
+After=tailscaled.service
+[Service]
+Restart=on-failure
+RestartSec=2
+EOF
+scp -q "$tmpconf" "$REMOTE_HOST:/tmp/distccd.conf" && echo " ok: distccd.conf" || { echo " SCP FAILED"; exit 1; }
+scp -q "$tmpoverride" "$REMOTE_HOST:/tmp/distccd-override.conf" && echo " ok: override.conf" || { echo " SCP FAILED"; exit 1; }
+rm -f "$tmpconf" "$tmpoverride"
+echo
+
+# ---- 3. install + enable distccd on the builder (sudo there) -----------------
+echo "==> [3/4] install + enable distccd on $REMOTE_HOST (sudo prompt on the builder)"
+ssh -t "$REMOTE_HOST" '
+ set -e
+ sudo install -m644 /tmp/distccd.conf /etc/conf.d/distccd
+ sudo mkdir -p /etc/systemd/system/distccd.service.d
+ sudo install -m644 /tmp/distccd-override.conf /etc/systemd/system/distccd.service.d/override.conf
+ rm -f /tmp/distccd.conf /tmp/distccd-override.conf
+ sudo systemctl daemon-reload
+ sudo systemctl enable --now distccd
+ sudo systemctl restart distccd
+ sleep 1
+ echo "----- systemctl status -----"; systemctl --no-pager --full status distccd | head -6
+ echo "----- listening socket -----"; sudo ss -tlnp | grep ":'"$PORT"'" || echo "NOT LISTENING (FAIL)"
+'
+echo
+
+# ---- 4. verify offload from this box ----------------------------------------
+echo "==> [4/4] verify offload"
+echo "--- ccache prefix_command ---"; ccache -p 2>/dev/null | grep -i prefix_command
+echo "--- distcc hosts (from file, env unset) ---"; env -u DISTCC_HOSTS distcc --show-hosts 2>&1
+td="$(mktemp -d)"
+printf '#include <vector>\nint main(){std::vector<int> v(8); return (int)v.size()-8;}\n' > "$td/probe.cpp"
+DISTCC_VERBOSE=1 distcc g++ -O2 -std=c++23 -c "$td/probe.cpp" -o "$td/probe.o" 2>"$td/v.txt"; rc=$?
+grep -iE "compiled on|exec on|locally|failed to distribute" "$td/v.txt" | head
+if [ $rc -eq 0 ] && [ -f "$td/probe.o" ] && ! grep -qiE "running locally|failed to distribute" "$td/v.txt"; then
+ echo "RESULT: OK — probe compiled on the remote builder."
+else
+ echo "RESULT: probe did NOT offload (built locally) — check the daemon/network."
+fi
+rm -rf "$td"
+echo "######## done — full log: $LOG ########"
diff --git a/packaging/start-unbox b/packaging/start-unbox
index d90a2f6..eb0c235 100755
--- a/packaging/start-unbox
+++ b/packaging/start-unbox
@@ -7,4 +7,13 @@
# makes PipeWire spam RTKit "ServiceUnknown" warnings.
#
# Usage: from a TTY, run start-unbox
+#
+# Wallpaper: unbox has no built-in wallpaper, so we draw one with swaybg (a
+# wlr-layer-shell client on the background layer) via unbox's -s startup hook.
+# It runs only if the image exists; put your image at the path below (a real file
+# or a symlink), or override with UNBOX_WALLPAPER=/path/to/image.
+WALLPAPER="${UNBOX_WALLPAPER:-$HOME/.config/unbox/wallpaper}"
+if [ -f "$WALLPAPER" ]; then
+ exec dbus-run-session -- unbox -s "swaybg -i '$WALLPAPER' -m fill"
+fi
exec dbus-run-session -- unbox