#!/usr/bin/env bash # ============================================================================= # builder-chroot.sh – powtarzalne środowisko budowania PaganOS # # Tworzy Debian-based chroot dla pagbuild. Nie zastępuje rootfs PaganOS: # jest osobnym, pełnym środowiskiem BUILDROOT z narzędziami developerskimi. # # Użycie: # sudo ./builder-chroot.sh --rootfs /mnt/pagan-builder --rebuild # sudo ./builder-chroot.sh --rootfs /mnt/pagan-builder # ============================================================================= set -Eeuo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOTFS="${BUILDER_ROOTFS:-/mnt/pagan-builder-rootfs}" SUITE="${BUILDER_SUITE:-trixie}" MIRROR="${BUILDER_MIRROR:-https://deb.debian.org/debian}" REBUILD=0 ACTIVATE=0 KEEP_MOUNTS=0 ACTIVE_ROOTFS="${BUILDER_ACTIVE_ROOTFS:-/var/lib/pagan-build/rootfs}" usage() { sed -n '1,22p' "$0" exit "${1:-0}" } while [[ $# -gt 0 ]]; do case "$1" in --rootfs) [[ $# -ge 2 ]] || usage 2; ROOTFS="$2"; shift 2 ;; --suite) [[ $# -ge 2 ]] || usage 2; SUITE="$2"; shift 2 ;; --mirror) [[ $# -ge 2 ]] || usage 2; MIRROR="$2"; shift 2 ;; --rebuild|--clean) REBUILD=1; shift ;; --activate) ACTIVATE=1; REBUILD=1; ROOTFS="${BUILDER_STAGING_ROOTFS:-${ACTIVE_ROOTFS}.new}"; shift ;; --active-rootfs) [[ $# -ge 2 ]] || usage 2; ACTIVE_ROOTFS="$2"; shift 2 ;; --keep-mounts) KEEP_MOUNTS=1; shift ;; -h|--help) usage 0 ;; *) echo "Nieznany argument: $1" >&2; usage 2 ;; esac done [[ $EUID -eq 0 ]] || { echo "Uruchom jako root: sudo $0 ..." >&2; exit 1; } [[ "$ROOTFS" != "/" && -n "$ROOTFS" ]] || { echo "Niebezpieczny ROOTFS=$ROOTFS" >&2; exit 1; } log() { printf '[builder-chroot] %s\n' "$*"; } die() { echo "[builder-chroot] BŁĄD: $*" >&2; exit 1; } for cmd in mount umount chroot rsync tar sha256sum; do command -v "$cmd" >/dev/null || die "Brak programu hosta: $cmd" done if [[ $REBUILD -eq 1 ]]; then log "Czyszczenie $ROOTFS" rm -rf --one-file-system "$ROOTFS" fi mkdir -p "$ROOTFS" # Debian bootstrap jest celowo osobny od LFS/PaganOS rootfs. Dzięki temu # builder ma spójne ABI i działający menedżer pakietów. if [[ ! -x "$ROOTFS/usr/bin/apt-get" ]]; then command -v debootstrap >/dev/null || die "Brak debootstrap. Zainstaluj na hoście: apt install debootstrap" log "Bootstrap Debian $SUITE" debootstrap --variant=minbase "$SUITE" "$ROOTFS" "$MIRROR" fi MOUNTS=() unmount_all() { for m in "${MOUNTS[@]:-}"; do umount -l "$m" 2>/dev/null || true; done MOUNTS=() } cleanup() { local rc=$? if [[ $KEEP_MOUNTS -eq 0 ]]; then unmount_all; fi exit "$rc" } trap cleanup EXIT INT TERM mount_one() { local options="$1" source="$2" target="$3" mkdir -p "$target" mount $options "$source" "$target" MOUNTS=("$target" "${MOUNTS[@]}") } # Montowania są potrzebne dla apt, testów kompilatora oraz programów używających # /proc, /sys i /dev. --rbind zachowuje /dev/pts i /dev/shm. mount_one "-t proc" proc "$ROOTFS/proc" mount --rbind /sys "$ROOTFS/sys"; mount --make-rslave "$ROOTFS/sys"; MOUNTS=("$ROOTFS/sys" "${MOUNTS[@]}") mount --rbind /dev "$ROOTFS/dev"; mount --make-rslave "$ROOTFS/dev"; MOUNTS=("$ROOTFS/dev" "${MOUNTS[@]}") mount --rbind /run "$ROOTFS/run"; mount --make-rslave "$ROOTFS/run"; MOUNTS=("$ROOTFS/run" "${MOUNTS[@]}") rm -f "$ROOTFS/etc/resolv.conf" cp -L /etc/resolv.conf "$ROOTFS/etc/resolv.conf" cat > "$ROOTFS/etc/apt/apt.conf.d/99builder" <<'EOF' Acquire::Retries "5"; APT::Install-Recommends "false"; APT::Install-Suggests "false"; EOF # Kompletna baza do budowania core/libs/desktop. Nazwy są Debianowe i służą # tylko builderowi; zależności PaganOS nadal pochodzą z PAGBUILD.yaml. BUILD_PACKAGES=( build-essential gcc g++ gcc-multilib libc6-dev-i386 make ninja-build meson cmake autoconf automake libtool pkg-config gettext autopoint bison flex gawk m4 texinfo git curl wget ca-certificates rsync patch file diffutils findutils sed grep tar gzip bzip2 xz-utils zstd unzip cpio gperf xsltproc docbook-xsl docbook-xml libxml2-utils libxml-parser-perl perl python3 python3-dev python3-pip python3-yaml python3-venv sqlite3 libsqlite3-dev gpg gpg-agent fakeroot libssl-dev libffi-dev libreadline-dev libncurses-dev zlib1g-dev liblzma-dev libbz2-dev libzstd-dev libexpat1-dev libpcre2-dev libxml2-dev libxslt1-dev libglib2.0-dev libdbus-1-dev libsystemd-dev libudev-dev libmount-dev libacl1-dev libattr1-dev libpam0g-dev libcap-dev libmnl-dev libevdev-dev libgirepository1.0-dev gobject-introspection valac shared-mime-info libgtk-3-dev libpango1.0-dev libcairo2-dev libgdk-pixbuf-2.0-dev libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxft-dev libxrender-dev libxrandr-dev libxi-dev libxinerama-dev libxkbcommon-dev libwayland-dev wayland-protocols libdrm-dev qtbase5-dev qtdeclarative5-dev qttools5-dev libqt5svg5-dev libpolkit-qt5-1-dev intltool ) log "Instalowanie ${#BUILD_PACKAGES[@]} pakietów developerskich" chroot "$ROOTFS" /usr/bin/env DEBIAN_FRONTEND=noninteractive \ apt-get update chroot "$ROOTFS" /usr/bin/env DEBIAN_FRONTEND=noninteractive \ apt-get install -y "${BUILD_PACKAGES[@]}" # Stałe środowisko dla każdego przepisu. cat > "$ROOTFS/etc/profile.d/pagan-builder.sh" <<'EOF' export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin export HOME=/root export LANG=C.UTF-8 export LC_ALL=C.UTF-8 export FORCE_UNSAFE_CONFIGURE=1 export CC=/usr/bin/gcc export CXX=/usr/bin/g++ export AR=/usr/bin/ar export RANLIB=/usr/bin/ranlib export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig export PKG_CONFIG_LIBDIR=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig export CMAKE_PREFIX_PATH=/usr:/ export MAKEFLAGS="-j$(nproc)" EOF mkdir -p "$ROOTFS/var/cache/pagbuild" "$ROOTFS/var/lib/pagan-sync" "$ROOTFS/opt/pagan" # Aktualne narzędzia z tego samego checkoutu, bez starego heredoc/stuba. for tool in pag pagbuild pagsync; do [[ -f "$SCRIPT_DIR/$tool" ]] || die "Brak pliku projektu: $SCRIPT_DIR/$tool" install -Dm755 "$SCRIPT_DIR/$tool" "$ROOTFS/usr/local/bin/$tool" done install -Dm644 "$SCRIPT_DIR/pagbuild" "$ROOTFS/opt/pagan/pagbuild" install -Dm644 "$SCRIPT_DIR/pagsync" "$ROOTFS/opt/pagan/pagsync" cat > "$ROOTFS/etc/pagan-builder-release" </dev/null || { echo "missing:$x" >&2; exit 1; } done python3 -c "import yaml,sys; print(\"python-yaml:ok\")" test "$(pkg-config --modversion glib-2.0)" test "$(pkg-config --modversion dbus-1)" test "$(pkg-config --modversion gtk+-3.0)" test -f /usr/lib/x86_64-linux-gnu/cmake/Qt5/Qt5Config.cmake pagbuild --help >/dev/null 2>&1 || true printf "builder-chroot:ok\\n" ' # Archiwum i checksum są tworzone obok rootfs, gotowe do scp. ARCHIVE="${ROOTFS%/}.tar.xz" log "Pakowanie $ARCHIVE" tar -cJf "$ARCHIVE" -C "$ROOTFS" \ --exclude='./proc' --exclude='./sys' --exclude='./dev' --exclude='./run' --exclude='./tmp' . sha256sum "$ARCHIVE" > "$ARCHIVE.sha256" log "Gotowe: $ARCHIVE" log "Checksum: $ARCHIVE.sha256" if [[ $ACTIVATE -eq 1 ]]; then [[ "$ACTIVE_ROOTFS" != "/var/lib/pagan-build/rootfs" ]] || die "Odmowa: Debianowy builder-chroot nie może zastąpić PaganOS rootfs. Użyj go jako osobnego środowiska BUILDROOT." [[ "$ROOTFS" == "${ACTIVE_ROOTFS}.new" ]] || die "Tryb --activate wymaga stagingu ${ACTIVE_ROOTFS}.new" # Nie przełączaj środowiska, gdy kolejka nadal używa starego rootfs. if ps -eo args= | grep -E '[p]agsync|[p]agbuild /var/lib/pagan-sync' | grep -v 'builder-chroot.sh' >/dev/null; then die "Aktywna kolejka pagsync/pagbuild. Zatrzymaj ją i uruchom --activate ponownie." fi unmount_all BACKUP="${ACTIVE_ROOTFS}.previous-$(date -u +%Y%m%dT%H%M%SZ)" log "Backup obecnego rootfs: $BACKUP" [[ -d "$ACTIVE_ROOTFS" ]] && mv "$ACTIVE_ROOTFS" "$BACKUP" mv "$ROOTFS" "$ACTIVE_ROOTFS" # Zachowaj artefakty do weryfikacji po przełączeniu. cp -f "$ARCHIVE" "$ACTIVE_ROOTFS.tar.xz" cp -f "$ARCHIVE.sha256" "$ACTIVE_ROOTFS.tar.xz.sha256" log "Aktywowano nowy rootfs: $ACTIVE_ROOTFS" fi