Files
unraid-podman/scripts/ci/setup-slackware-buildenv.sh
maggesandClaude Sonnet 5 5b47b4cc0a Add catatonit/nftables/docker-compose packages, fix CSRF/streaming/storage bugs found by live testing
- Package #9-11: catatonit (pod infra init), nftables (netavark firewall
  backend), docker-compose (external compose provider for `podman compose`)
  — all vendored prebuilt binaries, versions.env pinned, propagated through
  build-packages.sh/release.sh/podman.plg/verify+update-packages.sh.
- Fix WebUI: every POST action was silently failing (empty response body)
  because Unraid's own CSRF protection was never satisfied — app.js now
  sends the page's csrf_token as X-CSRF-Token.
- Fix WebUI: PodmanClient::pullImage() assumed a single JSON response, but
  /images/pull actually streams newline-delimited JSON — every successful
  pull was throwing "Expected a JSON object/array response".
- Fix WebUI: compose.php's up/down status detection had the same
  single-JSON-vs-NDJSON bug for `podman compose ps`, plus stderr was
  corrupting the parse.
- Add cache-busting (?v=<mtime>) to Podman.page's script/style tags so a
  redeployed JS/CSS fix isn't served stale from browser cache.
- Add a reusable modal dialog (app.js openFormModal) replacing
  prompt()/alert() for New Volume/Network/Pull Image.
- Add host-path (bind-mount) support when creating a named volume.
- Add Create Container (image, name, network mode incl. custom networks,
  ports, volumes, env, restart policy, privileged, start-after-create),
  auto-pulling the image on first use since /containers/create doesn't.

All fixes verified live against a real podman system service and, where
reachable, via the actual WebUI over the real socket — not just unit-level.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 11:51:17 +00:00

247 lines
12 KiB
Bash
Executable File

#!/bin/bash
# =============================================================================
# scripts/ci/setup-slackware-buildenv.sh
#
# Prepares a Slackware container (see .github/workflows/build-packages.yml)
# to build all eleven 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 json-c
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
require_pkgconfig json-c "Install Slackware's json-c package (needed by crun, >= 0.14)." || 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-<lang>
# 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."