#!/bin/bash # ============================================================================= # scripts/ci/setup-slackware-buildenv.sh # # Prepares a Slackware container (see .github/workflows/build-packages.yml) # to build all seven packages under packages/. Idempotent and safe to re-run. # # Strategy: vbatts/slackware:15.0 (the image build-packages.yml runs this # in) is a minimal rootfs — it ships none of the 'D' (development) series, # nor glib2/libcap/fuse3/curl. Step 0 below uses slackpkg (already present # and pre-configured with a mirror in that image) to install the toolchain # packages by name. Slackware packages carry no dependency metadata at all # (unlike apt/dnf), so slackpkg does NOT resolve dependencies — the list # below must name every package explicitly, including curl's HTTPS # runtime libs (nghttp2, brotli, cyrus-sasl), or you get a shared-library # error at the first invocation, not an install-time failure. What's left # after this (libseccomp, yajl, protoc — none ship in stock Slackware — # plus the Go/Rust toolchains and go-md2man, which no Slackware install # ships) is bootstrapped from source or official upstream releases, # further down. This makes the script tolerant # of small differences between Slackware base image variants (it skips # anything slackpkg reports as already installed) instead of assuming one # exact image layout, while still failing loudly if something we cannot # self-provision is missing. # # Exits non-zero with a clear message if a required tool cannot be found or # provisioned — this script is meant to run early in CI so failures surface # immediately, not halfway through a 20-minute podman build. # ============================================================================= set -eu REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # shellcheck source=/dev/null . "$REPO_ROOT/scripts/ci/buildenv-versions.env" WORK="/tmp/unraid-podman-buildenv" mkdir -p "$WORK" require_binary() { local bin="$1" hint="$2" if ! command -v "$bin" > /dev/null 2>&1; then echo "!! Required tool '$bin' not found in the build image." >&2 echo "!! $hint" >&2 exit 1 fi echo "==> found $bin: $(command -v "$bin")" } require_pkgconfig() { local module="$1" hint="$2" if ! pkg-config --exists "$module" 2>/dev/null; then echo "!! Required library '$module' not found via pkg-config." >&2 echo "!! $hint" >&2 return 1 fi echo "==> found pkg-config module: $module ($(pkg-config --modversion "$module"))" return 0 } # ----------------------------------------------------------------------------- # 0. Bootstrap the Slackware toolchain packages via slackpkg, if missing. # # CHECKGPG is turned off here: slackpkg's default GPG-key bootstrap fetches # Slackware's signing key from www.slackware.com, which is not reachable # from every CI network (observed to hang/fail on the self-hosted Gitea # Actions runner this project builds on). slackpkg's CHECKMD5 (on by # default) still verifies every package against the mirror's own # CHECKSUMS.md5 as a transit-integrity check. This is build-toolchain # provisioning, not the shipped artifacts — those are independently # checksummed by scripts/checksums.sh. # ----------------------------------------------------------------------------- if command -v slackpkg > /dev/null 2>&1; then echo "==> bootstrapping build toolchain via slackpkg" sed -i 's/^CHECKGPG=on/CHECKGPG=off/' /etc/slackpkg/slackpkg.conf slackpkg -batch=on -default_answer=y update slackpkg -batch=on -default_answer=y install \ gcc gcc-g++ binutils make m4 perl autoconf automake libtool pkg-config \ curl nghttp2 brotli cyrus-sasl ca-certificates glib2 libcap fuse3 \ cmake libarchive lz4 libxml2 guile gc kernel-headers flex elfutils \ python3 else echo "==> slackpkg not found, assuming toolchain is already provided by the base image" fi # ----------------------------------------------------------------------------- # 1. Baseline toolchain expected to already be present in the base image. # ----------------------------------------------------------------------------- require_binary gcc "Use a Slackware base image with the 'D' (development) series installed." require_binary make "Use a Slackware base image with the 'D' (development) series installed." require_binary autoconf "Needed by crun/fuse-overlayfs; part of Slackware's 'D' series." require_binary automake "Needed by crun/fuse-overlayfs; part of Slackware's 'D' series." require_binary libtool "Needed by crun/fuse-overlayfs; part of Slackware's 'D' series." require_binary pkg-config "Needed to locate C library dependencies." require_binary git "Needed to fetch crun's git submodules." require_binary curl "Needed to fetch pinned source tarballs." require_binary makepkg "Slackware's own packaging tool (pkgtools); should always be present." require_binary strip "Part of binutils; part of Slackware's 'D' series." require_binary python3 "Needed by crun's configure script (checks for Python >= 3)." # ----------------------------------------------------------------------------- # 2. C library dependencies expected to already be present. # ----------------------------------------------------------------------------- require_pkgconfig glib-2.0 "Install Slackware's glib2 package (needed by conmon)." require_pkgconfig libcap "Install Slackware's libcap package (needed by crun)." || true require_pkgconfig fuse3 "Install Slackware's fuse3 package (needed by fuse-overlayfs)." || true # ----------------------------------------------------------------------------- # 3. libseccomp — not part of stock Slackware, build from source if missing. # ----------------------------------------------------------------------------- if ! pkg-config --exists libseccomp 2>/dev/null; then echo "==> libseccomp not found, building v$LIBSECCOMP_VERSION from source" d="$WORK/libseccomp" mkdir -p "$d" curl -fL --retry 3 -o "$d/src.tar.gz" "$LIBSECCOMP_SRC_URL" actual=$(sha256sum "$d/src.tar.gz" | awk '{print $1}') [ "$actual" = "$LIBSECCOMP_SRC_SHA256" ] || { echo "!! libseccomp checksum mismatch (expected $LIBSECCOMP_SRC_SHA256, got $actual)" >&2 exit 1 } mkdir -p "$d/src" && tar -xf "$d/src.tar.gz" -C "$d/src" --strip-components=1 ( cd "$d/src" && ./autogen.sh && ./configure --prefix=/usr && make -j"$(nproc)" && make install ) else echo "==> libseccomp already present, skipping bootstrap build" fi # ----------------------------------------------------------------------------- # 4. yajl — not part of stock Slackware, build from source if missing. # ----------------------------------------------------------------------------- if ! pkg-config --exists yajl 2>/dev/null; then echo "==> yajl not found, building v$YAJL_VERSION from source" d="$WORK/yajl" mkdir -p "$d" curl -fL --retry 3 -o "$d/src.tar.gz" "$YAJL_SRC_URL" actual=$(sha256sum "$d/src.tar.gz" | awk '{print $1}') [ "$actual" = "$YAJL_SRC_SHA256" ] || { echo "!! yajl checksum mismatch (expected $YAJL_SRC_SHA256, got $actual)" >&2 exit 1 } mkdir -p "$d/src" && tar -xf "$d/src.tar.gz" -C "$d/src" --strip-components=1 # yajl's ./configure is a thin wrapper around CMake (not a cmake-free # autoconf script, despite its name) — cmake must already be on PATH, # see the slackpkg install list in step 0 above. ( cd "$d/src" && ./configure -p /usr && make -C build install ) ldconfig 2>/dev/null || true else echo "==> yajl already present, skipping bootstrap build" fi # ----------------------------------------------------------------------------- # 5. Go toolchain (podman) — official upstream tarball. # ----------------------------------------------------------------------------- # Always install our pinned Go, unconditionally — do NOT skip this just # because `command -v go` finds something. On this image, slackpkg's # batch-mode "install gcc" (step 0 above) pulls in every gcc- # sibling package built from the same Slackware gcc SlackBuild, including # gcc-go, which ships an ancient bundled Go (gccgo, go1.16.5) at # /usr/bin/go — old enough that its go.mod parser rejects the 3-component # "go 1.25.x" directive modern modules use, and would silently shadow our # intended $GO_VERSION if we only installed when `go` was missing. # Overwriting /usr/local/go and prepending it to PATH/GITHUB_PATH here # guarantees the pinned toolchain wins regardless of what else provides a # `go` binary. echo "==> installing Go $GO_VERSION (unconditionally, see comment above)" curl -fL --retry 3 -o "$WORK/go.tar.gz" "$GO_SRC_URL" actual=$(sha256sum "$WORK/go.tar.gz" | awk '{print $1}') [ "$actual" = "$GO_SRC_SHA256" ] || { echo "!! Go toolchain checksum mismatch (expected $GO_SRC_SHA256, got $actual)" >&2 exit 1 } rm -rf /usr/local/go tar -C /usr/local -xf "$WORK/go.tar.gz" export PATH="/usr/local/go/bin:$PATH" # Persist PATH for subsequent steps in the same GitHub Actions job. if [ -n "${GITHUB_PATH:-}" ]; then echo "/usr/local/go/bin" >> "$GITHUB_PATH" fi echo "==> using: $(go version)" # ----------------------------------------------------------------------------- # 6. go-md2man (conmon) — its `make install` shells out to this to render # docs/conmon.8.md into a man page; not packaged by Slackware, and no # prebuilt binary release exists upstream, so `go install` it (now that # our pinned Go from step 5 is on PATH). Pinned to a tagged release # rather than @latest to keep this build reproducible. # ----------------------------------------------------------------------------- if ! command -v go-md2man > /dev/null 2>&1; then echo "==> installing go-md2man v$GO_MD2MAN_VERSION" go install "github.com/cpuguy83/go-md2man/v2@v${GO_MD2MAN_VERSION}" gobin="$(go env GOPATH)/bin" export PATH="$gobin:$PATH" if [ -n "${GITHUB_PATH:-}" ]; then echo "$gobin" >> "$GITHUB_PATH" fi else echo "==> go-md2man already present: $(command -v go-md2man)" fi # ----------------------------------------------------------------------------- # 7. Rust toolchain (netavark, aardvark-dns) — via rustup. # ----------------------------------------------------------------------------- if ! command -v cargo > /dev/null 2>&1; then echo "==> Rust/cargo not found, installing via rustup ($RUST_CHANNEL channel)" curl -fL --retry 3 --proto '=https' --tlsv1.2 -sSf "$RUSTUP_INIT_URL" \ | sh -s -- -y --default-toolchain "$RUST_CHANNEL" --profile minimal # shellcheck source=/dev/null . "$HOME/.cargo/env" if [ -n "${GITHUB_PATH:-}" ]; then echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" fi else echo "==> Rust already present: $(cargo --version)" fi # ----------------------------------------------------------------------------- # 8. protoc (netavark) — its build.rs (via the prost-build crate) shells # out to a `protoc` binary to compile .proto files; Slackware packages no # protobuf/protoc at all (checked: not in any of the main/extra/pasture/ # testing repos). Official prebuilt release binary used instead of a # from-source C++ build. No `unzip` on this image either, so extract with # `python3 -m zipfile` (python3 is already installed, see step 0/1, for # crun's configure script) rather than adding yet another package. # ----------------------------------------------------------------------------- if ! command -v protoc > /dev/null 2>&1; then echo "==> installing protoc v$PROTOC_VERSION" curl -fL --retry 3 -o "$WORK/protoc.zip" "$PROTOC_SRC_URL" actual=$(sha256sum "$WORK/protoc.zip" | awk '{print $1}') [ "$actual" = "$PROTOC_SRC_SHA256" ] || { echo "!! protoc checksum mismatch (expected $PROTOC_SRC_SHA256, got $actual)" >&2 exit 1 } rm -rf /usr/local/protoc mkdir -p /usr/local/protoc python3 -m zipfile -e "$WORK/protoc.zip" /usr/local/protoc chmod +x /usr/local/protoc/bin/protoc export PATH="/usr/local/protoc/bin:$PATH" if [ -n "${GITHUB_PATH:-}" ]; then echo "/usr/local/protoc/bin" >> "$GITHUB_PATH" fi else echo "==> protoc already present: $(protoc --version)" fi echo echo "==> Build environment ready."