diff options
Diffstat (limited to 'packaging/build')
| -rwxr-xr-x | packaging/build/build-client.sh | 116 | ||||
| -rwxr-xr-x | packaging/build/build-common.sh | 75 | ||||
| -rwxr-xr-x | packaging/build/build-hub.sh | 73 | ||||
| -rwxr-xr-x | packaging/build/build-node.sh | 89 | ||||
| -rwxr-xr-x | packaging/build/build-packages.sh | 156 |
5 files changed, 509 insertions, 0 deletions
diff --git a/packaging/build/build-client.sh b/packaging/build/build-client.sh new file mode 100755 index 0000000..fbdf51e --- /dev/null +++ b/packaging/build/build-client.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# Build the meshbay-client package: Electron app + .desktop launcher. +# +# Usage: ./build-client.sh [staging-dir] [--arm64] +# +# Requires Node >= 22. If not in PATH, set NODEJS_DIR=/opt/nodejs or similar. +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +STAGING="${1:-/tmp/meshbay-build}" +ROOT="$STAGING/meshbay-client-root" +CLIENT="$REPO/packages/meshbay-client" + +ARCH_FLAG="" +ELECTRON_ARCH="x64" +DEB_ARCH="amd64" +for arg in "$@"; do + if [ "$arg" = "--arm64" ]; then + ARCH_FLAG="--arm64" + ELECTRON_ARCH="arm64" + DEB_ARCH="arm64" + fi +done + +# Node.js version check +if [ -n "${NODEJS_DIR:-}" ]; then + export PATH="$NODEJS_DIR/bin:$PATH" +fi +NODE_VER=$(node --version 2>/dev/null || echo "none") +echo "==> meshbay-client (Node $NODE_VER, arch=$ELECTRON_ARCH)" + +if ! node --version >/dev/null 2>&1; then + echo "!! Node.js not found. Set NODEJS_DIR or install Node >= 22." >&2 + exit 1 +fi + +NODE_MAJOR=$(node -e "process.stdout.write(String(process.versions.node.split('.')[0]))") +if [ "$NODE_MAJOR" -lt 22 ]; then + echo "!! Node $NODE_VER is too old — need >= 22 for Electron" >&2 + exit 1 +fi + +rm -rf "$ROOT" +mkdir -p "$ROOT" + +# --- Build the Electron app ----------------------------------------------- +cd "$CLIENT" + +echo " npm ci" +npm ci --ignore-scripts 2>&1 | tail -3 + +echo " approving + installing electron" +npm approve-scripts electron 2>/dev/null || true +node node_modules/electron/install.js 2>&1 | tail -1 + +echo " syncing UI from hub package" +npm run sync-ui 2>&1 | tail -1 + +echo " electron-builder --dir $ARCH_FLAG" +npx electron-builder --dir --linux $ARCH_FLAG 2>&1 | tail -5 + +# Determine the unpacked output directory +if [ "$ELECTRON_ARCH" = "arm64" ]; then + UNPACKED="$CLIENT/dist/linux-arm64-unpacked" +else + UNPACKED="$CLIENT/dist/linux-unpacked" +fi + +[ -d "$UNPACKED" ] || { echo "!! electron-builder output not found at $UNPACKED" >&2; exit 1; } + +# --- Assemble the package tree -------------------------------------------- +echo " assembling /opt/meshbay-client/" +mkdir -p "$ROOT/opt/meshbay-client" +cp -a "$UNPACKED"/* "$ROOT/opt/meshbay-client/" + +# Rename the electron binary +if [ -f "$ROOT/opt/meshbay-client/meshbay-client" ]; then + mv "$ROOT/opt/meshbay-client/meshbay-client" "$ROOT/opt/meshbay-client/meshbay" +elif [ -f "$ROOT/opt/meshbay-client/electron" ]; then + mv "$ROOT/opt/meshbay-client/electron" "$ROOT/opt/meshbay-client/meshbay" +fi + +# Symlink in PATH +mkdir -p "$ROOT/usr/bin" +ln -sf /opt/meshbay-client/meshbay "$ROOT/usr/bin/meshbay" + +# --- Desktop launcher ----------------------------------------------------- +mkdir -p "$ROOT/usr/share/applications" +cp "$REPO/packaging/desktop/meshbay.desktop" "$ROOT/usr/share/applications/" + +# --- Icons ---------------------------------------------------------------- +ICON_SRC="$CLIENT/build/icon.png" +if [ -f "$ICON_SRC" ]; then + for size in 128 256 512; do + ICON_DIR="$ROOT/usr/share/icons/hicolor/${size}x${size}/apps" + mkdir -p "$ICON_DIR" + if command -v convert >/dev/null 2>&1; then + convert "$ICON_SRC" -resize "${size}x${size}" "$ICON_DIR/meshbay.png" + elif command -v magick >/dev/null 2>&1; then + magick "$ICON_SRC" -resize "${size}x${size}" "$ICON_DIR/meshbay.png" + else + # Fallback: copy the original at all sizes (better than no icon) + cp "$ICON_SRC" "$ICON_DIR/meshbay.png" + fi + done + echo " icons installed" +else + echo " !! icon.png not found at $ICON_SRC — no icons" >&2 +fi + +# --- Firewall profile (Chromecast) ---------------------------------------- +mkdir -p "$ROOT/usr/lib/firewalld/services" +cp "$REPO/packaging/firewall/firewalld/meshbay-cast.xml" \ + "$ROOT/usr/lib/firewalld/services/" + +echo "==> meshbay-client staging ready at $ROOT ($DEB_ARCH)" diff --git a/packaging/build/build-common.sh b/packaging/build/build-common.sh new file mode 100755 index 0000000..3045689 --- /dev/null +++ b/packaging/build/build-common.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# Build the meshbay-common package: shared venv with ALL Python dependencies. +# +# Usage: ./build-common.sh [staging-dir] +# staging-dir defaults to /tmp/meshbay-build +# +# Output: $STAGING/meshbay-common-root/ (ready for dpkg-deb or rpmbuild) +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +STAGING="${1:-/tmp/meshbay-build}" +ROOT="$STAGING/meshbay-common-root" +VENV_FINAL="/opt/meshbay-common/venv" +VENV_BUILD="$ROOT$VENV_FINAL" +WHEEL_DIR="$STAGING/wheels" + +VERSION=$(python3 -c " +import tomllib, pathlib +p = pathlib.Path('$REPO/packages/meshbay-common/pyproject.toml') +print(tomllib.loads(p.read_text())['project']['version']) +") +echo "==> meshbay-common $VERSION" + +rm -rf "$ROOT" +mkdir -p "$ROOT/opt/meshbay-common" "$WHEEL_DIR" + +# --- Create venv ----------------------------------------------------------- +echo " creating venv at $VENV_BUILD" +python3 -m venv "$VENV_BUILD" +"$VENV_BUILD/bin/pip" install --upgrade pip wheel 2>&1 | tail -1 + +# --- Build wheels for all three packages ----------------------------------- +echo " building wheels" +for pkg in meshbay-common meshbay-hub meshbay-node; do + "$VENV_BUILD/bin/pip" wheel \ + --no-deps \ + --wheel-dir "$WHEEL_DIR" \ + "$REPO/packages/$pkg" 2>&1 | tail -1 +done + +# --- Install everything into the venv ------------------------------------- +echo " installing all packages + dependencies" +"$VENV_BUILD/bin/pip" install \ + --find-links "$WHEEL_DIR" \ + meshbay-common meshbay-hub meshbay-node 2>&1 | tail -3 + +# --- Strip build tools from the venv (not needed at runtime) --------------- +echo " stripping build tools" +"$VENV_BUILD/bin/pip" uninstall -y pip setuptools wheel 2>&1 | tail -1 +rm -rf "$VENV_BUILD/lib"/python*/site-packages/pip* +rm -rf "$VENV_BUILD/lib"/python*/site-packages/setuptools* +rm -rf "$VENV_BUILD/lib"/python*/site-packages/wheel* +rm -f "$VENV_BUILD/bin/pip"* + +# --- Fix shebangs and pyvenv.cfg ------------------------------------------ +# The venv was built under $ROOT but will be installed at /opt/meshbay-common/venv/ +echo " fixing paths ($ROOT -> '')" +for f in "$VENV_BUILD/bin"/*; do + [ -f "$f" ] || continue + [ -L "$f" ] && continue + head -1 "$f" | grep -q "^#!" || continue + sed -i "1s|$ROOT||" "$f" +done +sed -i "s|$ROOT||g" "$VENV_BUILD/pyvenv.cfg" + +# --- Remove __pycache__ (will be regenerated on first import) -------------- +find "$VENV_BUILD" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + +# --- Keep a copy of the wheels for reference ------------------------------- +mkdir -p "$ROOT/opt/meshbay-common/wheels" +cp "$WHEEL_DIR"/meshbay_common-*.whl "$ROOT/opt/meshbay-common/wheels/" + +echo "==> meshbay-common staging ready at $ROOT" +echo " venv: $VENV_BUILD" +echo " version: $VERSION" diff --git a/packaging/build/build-hub.sh b/packaging/build/build-hub.sh new file mode 100755 index 0000000..45d2ab9 --- /dev/null +++ b/packaging/build/build-hub.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Build the meshbay-hub package: hub-specific files on top of meshbay-common. +# +# Expects build-common.sh to have run first (shared venv exists in staging). +# +# Usage: ./build-hub.sh [staging-dir] +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +STAGING="${1:-/tmp/meshbay-build}" +COMMON_ROOT="$STAGING/meshbay-common-root" +ROOT="$STAGING/meshbay-hub-root" + +VENV="$COMMON_ROOT/opt/meshbay-common/venv" +PYVER=$(basename "$VENV"/lib/python3.*) +SITE="$VENV/lib/$PYVER/site-packages" + +VERSION=$(python3 -c " +import tomllib, pathlib +p = pathlib.Path('$REPO/packages/meshbay-hub/pyproject.toml') +print(tomllib.loads(p.read_text())['project']['version']) +") +echo "==> meshbay-hub $VERSION" + +[ -d "$VENV" ] || { echo "!! shared venv not found — run build-common.sh first" >&2; exit 1; } + +rm -rf "$ROOT" +mkdir -p "$ROOT" + +# --- Hub code in the shared venv (owned by meshbay-hub package) ----------- +# MOVE (not copy) from the common staging tree so dpkg/rpm knows these files +# belong to meshbay-hub, not meshbay-common. build-common.sh installed them +# so that shebangs and dependency resolution are correct. +HUB_ROOT="$ROOT/opt/meshbay-common/venv/lib/$PYVER/site-packages" +mkdir -p "$HUB_ROOT" +mv "$SITE/meshbay_hub" "$HUB_ROOT/" +mv "$SITE"/meshbay_hub-*.dist-info "$HUB_ROOT/" + +# Entry point +mkdir -p "$ROOT/opt/meshbay-common/venv/bin" +mv "$VENV/bin/meshbay-hub" "$ROOT/opt/meshbay-common/venv/bin/" + +# Symlink in PATH +mkdir -p "$ROOT/usr/bin" +ln -sf /opt/meshbay-common/venv/bin/meshbay-hub "$ROOT/usr/bin/meshbay-hub" + +# --- Hub-specific assets -------------------------------------------------- +mkdir -p "$ROOT/opt/meshbay-hub" + +# Alembic config (migrations are inside the installed package at meshbay_hub/db/migrations/) +if [ -f "$REPO/packages/meshbay-hub/alembic.ini" ]; then + mkdir -p "$ROOT/opt/meshbay-hub/migrations" + cp "$REPO/packages/meshbay-hub/alembic.ini" "$ROOT/opt/meshbay-hub/migrations/" +fi + +# Config example +mkdir -p "$ROOT/opt/meshbay-hub/share" +if [ -f "$REPO/packaging/conf/hub.toml.example" ]; then + cp "$REPO/packaging/conf/hub.toml.example" "$ROOT/opt/meshbay-hub/share/" +fi + +# Also install to /etc/meshbay/ for discoverability +mkdir -p "$ROOT/etc/meshbay" +if [ -f "$ROOT/opt/meshbay-hub/share/hub.toml.example" ]; then + cp "$ROOT/opt/meshbay-hub/share/hub.toml.example" "$ROOT/etc/meshbay/" +fi + +# --- Systemd unit --------------------------------------------------------- +mkdir -p "$ROOT/usr/lib/systemd/system" +cp "$REPO/packaging/systemd/meshbay-hub.service" \ + "$ROOT/usr/lib/systemd/system/meshbay-hub.service" + +echo "==> meshbay-hub staging ready at $ROOT" diff --git a/packaging/build/build-node.sh b/packaging/build/build-node.sh new file mode 100755 index 0000000..d8a5af4 --- /dev/null +++ b/packaging/build/build-node.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Build the meshbay-node package: node-specific files on top of meshbay-common. +# +# Expects build-common.sh to have run first (shared venv exists in staging). +# +# Usage: ./build-node.sh [staging-dir] +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +STAGING="${1:-/tmp/meshbay-build}" +COMMON_ROOT="$STAGING/meshbay-common-root" +ROOT="$STAGING/meshbay-node-root" + +VENV="$COMMON_ROOT/opt/meshbay-common/venv" +PYVER=$(basename "$VENV"/lib/python3.*) +SITE="$VENV/lib/$PYVER/site-packages" + +VERSION=$(python3 -c " +import tomllib, pathlib +p = pathlib.Path('$REPO/packages/meshbay-node/pyproject.toml') +print(tomllib.loads(p.read_text())['project']['version']) +") +echo "==> meshbay-node $VERSION" + +[ -d "$VENV" ] || { echo "!! shared venv not found — run build-common.sh first" >&2; exit 1; } + +rm -rf "$ROOT" +mkdir -p "$ROOT" + +# --- Node code in the shared venv (owned by meshbay-node package) --------- +# MOVE (not copy) from the common staging tree — same rationale as build-hub.sh. +NODE_ROOT="$ROOT/opt/meshbay-common/venv/lib/$PYVER/site-packages" +mkdir -p "$NODE_ROOT" +mv "$SITE/meshbay_node" "$NODE_ROOT/" +mv "$SITE"/meshbay_node-*.dist-info "$NODE_ROOT/" + +# Entry point +mkdir -p "$ROOT/opt/meshbay-common/venv/bin" +mv "$VENV/bin/meshbay-node" "$ROOT/opt/meshbay-common/venv/bin/" + +# Symlink in PATH +mkdir -p "$ROOT/usr/bin" +ln -sf /opt/meshbay-common/venv/bin/meshbay-node "$ROOT/usr/bin/meshbay-node" + +# --- Node-specific assets ------------------------------------------------- +mkdir -p "$ROOT/opt/meshbay-node/share" + +# Default env with TMDB token (read at build time). +# Override with MESHBAY_TMDB_TOKEN_FILE; falls back to QE/node.env (gitignored). +TMDB_TOKEN_FILE="${MESHBAY_TMDB_TOKEN_FILE:-$REPO/QE/node.env}" +TMDB_TOKEN="" +if [ -f "$TMDB_TOKEN_FILE" ]; then + TMDB_TOKEN=$(grep -oP 'MESHBAY_TMDB_DEFAULT_TOKEN=\K.*' "$TMDB_TOKEN_FILE" || true) +fi +if [ -n "$TMDB_TOKEN" ]; then + cat > "$ROOT/opt/meshbay-node/share/default.env" <<EOF +# Default environment for meshbay-node. +# Copied to ~/.config/meshbay/node.env by 'meshbay-node init' if it does not exist. +# The operator may override any value there or in the systemd EnvironmentFile. + +# TMDB API token for the Videos app (read-only, shared across installations) +MESHBAY_TMDB_DEFAULT_TOKEN=$TMDB_TOKEN +EOF + echo " TMDB token baked into default.env" +else + echo " !! TMDB token not found in QE/node.env — default.env will be empty" >&2 + touch "$ROOT/opt/meshbay-node/share/default.env" +fi + +# --- Systemd units -------------------------------------------------------- +mkdir -p "$ROOT/usr/lib/systemd/system" +mkdir -p "$ROOT/usr/lib/systemd/user" +cp "$REPO/packaging/systemd/meshbay-node.service" \ + "$ROOT/usr/lib/systemd/system/meshbay-node@.service" +cp "$REPO/packaging/systemd/meshbay-node-user.service" \ + "$ROOT/usr/lib/systemd/user/meshbay-node.service" + +# --- Firewall profiles ---------------------------------------------------- +# firewalld (Fedora) +mkdir -p "$ROOT/usr/lib/firewalld/services" +cp "$REPO/packaging/firewall/firewalld/meshbay-node.xml" \ + "$ROOT/usr/lib/firewalld/services/" + +# UFW (Ubuntu/Debian) +mkdir -p "$ROOT/etc/ufw/applications.d" +cp "$REPO/packaging/firewall/ufw/meshbay" \ + "$ROOT/etc/ufw/applications.d/" + +echo "==> meshbay-node staging ready at $ROOT" diff --git a/packaging/build/build-packages.sh b/packaging/build/build-packages.sh new file mode 100755 index 0000000..ccacb81 --- /dev/null +++ b/packaging/build/build-packages.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# Build all MeshBay packages for the current platform. +# +# Usage: +# ./build-packages.sh # auto-detect format (deb on Ubuntu, rpm on Fedora) +# ./build-packages.sh deb # force .deb +# ./build-packages.sh rpm # force .rpm +# ./build-packages.sh deb --arm64 # arm64 .deb (Pi) +# +# Expects to run on the target machine: +# - Ubuntu 26.04 (cbesson@meshbay.org) for .deb amd64 +# - Fedora 44 (local) for .rpm x86_64 +# - Raspberry Pi for .deb arm64 (deferred) +# +# Output: packages in $STAGING/out/ +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO="$(cd "$SCRIPT_DIR/../.." && pwd)" +STAGING="${MESHBAY_BUILD_DIR:-/tmp/meshbay-build}" +OUT="$STAGING/out" + +# --- Detect format -------------------------------------------------------- +FORMAT="${1:-auto}" +shift 2>/dev/null || true +ARM64_FLAG="" +for arg in "$@"; do + [ "$arg" = "--arm64" ] && ARM64_FLAG="--arm64" +done + +if [ "$FORMAT" = "auto" ]; then + if [ -f /etc/debian_version ]; then + FORMAT="deb" + elif [ -f /etc/fedora-release ] || [ -f /etc/redhat-release ]; then + FORMAT="rpm" + else + echo "!! Cannot detect OS — pass 'deb' or 'rpm' as argument" >&2 + exit 1 + fi +fi + +ARCH=$(dpkg --print-architecture 2>/dev/null || rpm --eval '%{_arch}' 2>/dev/null || uname -m) +if [ -n "$ARM64_FLAG" ]; then + ARCH="arm64" +fi + +VERSION=$(python3 -c " +import tomllib, pathlib +p = pathlib.Path('$REPO/packages/meshbay-common/pyproject.toml') +print(tomllib.loads(p.read_text())['project']['version']) +") + +echo "========================================" +echo " MeshBay $VERSION — $FORMAT $ARCH" +echo "========================================" +echo " Repo: $REPO" +echo " Staging: $STAGING" +echo " Output: $OUT" +echo "" + +rm -rf "$OUT" +mkdir -p "$OUT" + +# --- Step 1: Build staging trees ------------------------------------------ + +echo "--- [1/4] meshbay-common (shared venv + all deps) ---" +bash "$SCRIPT_DIR/build-common.sh" "$STAGING" + +echo "" +echo "--- [2/4] meshbay-hub ---" +bash "$SCRIPT_DIR/build-hub.sh" "$STAGING" + +echo "" +echo "--- [3/4] meshbay-node ---" +bash "$SCRIPT_DIR/build-node.sh" "$STAGING" + +echo "" +echo "--- [4/4] meshbay-client ---" +bash "$SCRIPT_DIR/build-client.sh" "$STAGING" $ARM64_FLAG + +# --- Step 2: Package ------------------------------------------------------ + +echo "" +echo "--- Packaging ($FORMAT) ---" + +if [ "$FORMAT" = "deb" ]; then + build_deb() { + local pkg="$1" + local root="$STAGING/${pkg}-root" + local deb_src="$REPO/packaging/deb/$pkg/DEBIAN" + local deb_dir="$root/DEBIAN" + + mkdir -p "$deb_dir" + for f in "$deb_src"/*; do + [ -f "$f" ] || continue + sed -e "s/__VERSION__/$VERSION/g" \ + -e "s/__ARCH__/$ARCH/g" \ + "$f" > "$deb_dir/$(basename "$f")" + chmod 755 "$deb_dir/$(basename "$f")" 2>/dev/null || true + done + # control files should be 644, scripts 755 + [ -f "$deb_dir/control" ] && chmod 644 "$deb_dir/control" + [ -f "$deb_dir/conffiles" ] && chmod 644 "$deb_dir/conffiles" + + dpkg-deb --build --root-owner-group "$root" "$OUT/${pkg}_${VERSION}_${ARCH}.deb" + echo " -> $OUT/${pkg}_${VERSION}_${ARCH}.deb" + } + + build_deb meshbay-common + build_deb meshbay-hub + build_deb meshbay-node + build_deb meshbay-client + +elif [ "$FORMAT" = "rpm" ]; then + RPMBUILD_DIR="$STAGING/rpmbuild" + rm -rf "$RPMBUILD_DIR" + mkdir -p "$RPMBUILD_DIR"/{SPECS,SOURCES,BUILD,RPMS,SRPMS} + + build_rpm() { + local pkg="$1" + local spec="$REPO/packaging/rpm/${pkg}.spec" + local root="$STAGING/${pkg}-root" + + # Substitute version and pass the staging root as a macro + sed "s/__VERSION__/$VERSION/g" "$spec" > "$RPMBUILD_DIR/SPECS/${pkg}.spec" + + rpmbuild \ + --define "_topdir $RPMBUILD_DIR" \ + --define "_staging_root $root" \ + -bb "$RPMBUILD_DIR/SPECS/${pkg}.spec" 2>&1 | tail -5 + + local rpm_file + rpm_file=$(find "$RPMBUILD_DIR/RPMS" -name "${pkg}-${VERSION}*.rpm" -print -quit) + if [ -n "$rpm_file" ]; then + cp "$rpm_file" "$OUT/" + echo " -> $OUT/$(basename "$rpm_file")" + fi + } + + build_rpm meshbay-common + build_rpm meshbay-hub + build_rpm meshbay-node + build_rpm meshbay-client +fi + +# --- Summary -------------------------------------------------------------- +echo "" +echo "========================================" +echo " Build complete" +echo "========================================" +ls -lh "$OUT"/ +echo "" +echo "Install order:" +echo " 1. meshbay-common" +echo " 2. meshbay-hub and/or meshbay-node" +echo " 3. meshbay-client" |