Files
maggesandClaude Sonnet 5 7f6fcb9166
Build Packages / Build .txz packages (push) Successful in 9m17s
Lint / ShellCheck (push) Successful in 12s
Lint / Validate .plg XML (push) Successful in 13s
Lint / EditorConfig (push) Successful in 6s
Fix real STORAGE_PATH bug; add Start Podman + format-disk from the WebUI
Root cause of a fresh-install "cannot reach the Podman API socket" report
(a friend's Unraid box, cache pool present and mounted): unlike
Docker-for-Unraid's docker.img path, this plugin never auto-created
STORAGE_PATH itself — only podman.img inside it. A perfectly normal,
already-mounted cache pool still failed preflight/storage-create with
"does not exist", just because its own .../system/podman subdirectory
had never been created. Fixed by walking up to the nearest existing
ancestor and checking whether it's on a different device than / (real
mount vs. nothing mounted at all) — see podman-common.sh's new
podman_path_has_real_mount_ancestor(), used by both podman-preflight.sh
and podman-storage.sh.

Settings gets a "Podman Service" card (status chip + Start/Restart,
backed by new ajax/settings.php service_status/start/restart actions
that just shell out to rc.podman) so a fresh install that failed to start
can be diagnosed and retried without SSH/terminal access at all — exactly
what was missing when this was first needed live.

Also adds "Format a Disk for Podman Storage" (new ajax/disks.php) for a
single-disk system with no cache pool at all. Only ever lists disks with
literally no existing partition/filesystem/RAID-or-ZFS-membership
signature and that aren't Unraid's boot flash — found live, twice, during
development: the boot USB (FAT, labeled "UNRAID") passed the initial
mounted-only check because this host's /boot is backed by a ZFS dataset
rather than a direct partition mount, and active RAID-member cache disks
passed a data-vs-blank *warning* rather than a hard exclusion. Both are
now excluded outright, not just flagged — see disks.php's
device_or_children_labeled_unraid() and the hasData exclusion in
list_candidate_disks(). A disk formatted this way is remounted by UUID on
every boot via a new plugin/sbin/podman-mount-managed-disk.sh, called
from plugin/event/disks_mounted before rc.podman start.

Unrelated fix bundled in: scripts/lib/slackbuild-common.sh now sets
SOURCE_DATE_EPOCH (derived from the repo's last commit) before calling
makepkg, so two separate builds of the same commit produce byte-identical
.txz files — makepkg already supports this (`--clamp-mtime` when
$SOURCE_DATE_EPOCH is set, confirmed by reading a real host's
/sbin/makepkg) but nothing was setting the variable, so release.yml's
"rebuild in CI and verify it matches the committed checksums" step was
guaranteed to fail on the first package it checked alphabetically
(observed live: aardvark-dns).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 22:55:14 +00:00

201 lines
8.5 KiB
Bash
Executable File

#!/bin/bash
# =============================================================================
# scripts/lib/slackbuild-common.sh
#
# Shared helper functions sourced by every packages/*/*.SlackBuild script.
# Centralizing this logic means each individual SlackBuild only has to
# describe *how to compile* its component — fetching, checksum verification,
# and final .txz packaging are implemented once, here, and used the same way
# by all ten packages (unraid-podman's own SlackBuild has no upstream
# source to fetch, so it doesn't need this). This is what keeps the build
# scripts consistent and short instead of each reinventing (and potentially
# forgetting) checksum verification or Slackware package metadata.
#
# Every SlackBuild is expected to:
# 1. `. "$(dirname "$0")/../../scripts/lib/slackbuild-common.sh"`
# 2. `. "$(dirname "$0")/../../versions.env"`
# 3. Call sb_init, sb_fetch_and_verify, do its own compile steps into
# $PKG, then call sb_make_package.
#
# Not meant to be run directly.
# =============================================================================
set -eu
# -----------------------------------------------------------------------------
# sb_init <pkg-name>
#
# Sets up the standard SlackBuild working directories and exports the
# variables every subsequent helper (and the calling SlackBuild) relies on:
# CWD - directory the SlackBuild itself lives in (packages/<name>/)
# TMP - scratch/build directory (source is extracted and compiled here)
# PKG - staging directory that becomes the .txz payload
# OUTPUT - where the finished .txz + checksum files are written
# All three of TMP/PKG/OUTPUT are safe to delete/recreate; nothing outside
# of them is ever touched, and nothing under them is ever committed to git
# (see .gitignore: packages/**/src, packages/**/pkg, packages/**/work).
# -----------------------------------------------------------------------------
sb_init() {
local pkg_name="$1"
PRGNAM="$pkg_name"
CWD="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)"
TMP="${TMP:-$CWD/work}"
PKG="${PKG:-$TMP/package-$PRGNAM}"
OUTPUT="${OUTPUT:-$CWD/../../dist}"
export PRGNAM CWD TMP PKG OUTPUT
rm -rf "$TMP"
mkdir -p "$TMP" "$PKG" "$OUTPUT"
echo "==> [$PRGNAM] TMP=$TMP"
echo "==> [$PRGNAM] PKG=$PKG"
echo "==> [$PRGNAM] OUTPUT=$OUTPUT"
}
# -----------------------------------------------------------------------------
# sb_fetch_and_verify <url> <expected-sha256> <dest-filename>
#
# Downloads a source tarball into $TMP and verifies it against the SHA256
# pinned in versions.env before anything is extracted or built. Aborts the
# build loudly on any mismatch — a checksum mismatch means either
# versions.env is stale (a legitimate new upstream release) or the source is
# not what it claims to be; either way, an unreviewed build must not proceed.
# -----------------------------------------------------------------------------
sb_fetch_and_verify() {
local url="$1"
local expected_sha256="$2"
local dest_name="$3"
local dest_path="$TMP/$dest_name"
# All progress/diagnostic output here must go to stderr, not stdout:
# callers capture this function's return value via `tarball=$(sb_fetch_and_verify ...)`,
# and command substitution captures everything written to stdout — a
# stray stdout echo above the final `echo "$dest_path"` would get
# concatenated into that captured value instead of just printing to the
# log.
echo "==> [$PRGNAM] Fetching $url" >&2
curl -fL --retry 3 --retry-delay 2 -o "$dest_path" "$url"
local actual_sha256
actual_sha256=$(sha256sum "$dest_path" | awk '{print $1}')
if [ "$actual_sha256" != "$expected_sha256" ]; then
echo "!! [$PRGNAM] SHA256 MISMATCH for $dest_name" >&2
echo "!! expected: $expected_sha256" >&2
echo "!! actual: $actual_sha256" >&2
echo "!! Refusing to build against unverified source. If upstream" >&2
echo "!! genuinely released a new version, update versions.env via" >&2
echo "!! scripts/update-versions.sh instead of editing the hash by hand." >&2
exit 1
fi
echo "==> [$PRGNAM] SHA256 verified ($actual_sha256)" >&2
echo "$dest_path"
}
# -----------------------------------------------------------------------------
# sb_extract <tarball-path> [strip-components]
#
# Extracts a verified tarball into $TMP/src, normalizing away the
# "<repo>-<version>/" wrapper directory GitHub/cgit archives always contain,
# so every SlackBuild can `cd "$TMP/src"` regardless of upstream's archive
# layout.
# -----------------------------------------------------------------------------
sb_extract() {
local tarball="$1"
local strip="${2:-1}"
mkdir -p "$TMP/src"
tar -xf "$tarball" -C "$TMP/src" --strip-components="$strip"
echo "$TMP/src"
}
# -----------------------------------------------------------------------------
# sb_install_docs <license-file> [readme-file]
#
# Installs upstream's license (and optionally README) into the standard
# Slackware package documentation location, plus this project's own build
# metadata, so `installpkg` output and /usr/doc/<pkg>-<version>/ are
# populated per Slackware convention.
# -----------------------------------------------------------------------------
sb_install_docs() {
local license_file="$1"
local readme_file="${2:-}"
local docdir="$PKG/usr/doc/$PRGNAM-$VERSION"
mkdir -p "$docdir"
[ -f "$license_file" ] && cp "$license_file" "$docdir/"
[ -n "$readme_file" ] && [ -f "$readme_file" ] && cp "$readme_file" "$docdir/"
{
echo "Built by unraid-podman from upstream source."
echo "Package: $PRGNAM $VERSION"
echo "Built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
} > "$docdir/unraid-podman.build-info"
}
# -----------------------------------------------------------------------------
# sb_make_package
#
# Finalizes the Slackware package: installs slack-desc + doinst.sh (if
# present) into $PKG/install/, strips binaries, sets root:root ownership,
# runs makepkg to produce the .txz, then writes SHA256 + MD5 sidecar files
# next to it.
#
# - SHA256 is used by scripts/build-packages.sh / CI to verify build
# artifacts between jobs.
# - MD5 is additionally generated because Unraid's .plg <FILE> mechanism
# verifies downloads via MD5 by convention (see
# docs/ARCHITECTURE.md section 3.2) — scripts/release.sh reads the .md5
# file to populate plugin/podman.plg's entities.
#
# Produces: $OUTPUT/<name>-<version>-<arch>-<build><tag>.txz (+ .sha256, .md5)
# -----------------------------------------------------------------------------
sb_make_package() {
local version="$1"
local arch="$2"
local build="$3"
local tag="$4"
mkdir -p "$PKG/install"
if [ -f "$CWD/slack-desc" ]; then
cp "$CWD/slack-desc" "$PKG/install/slack-desc"
else
echo "!! [$PRGNAM] missing packages/$PRGNAM/slack-desc" >&2
exit 1
fi
[ -f "$CWD/doinst.sh" ] && cp "$CWD/doinst.sh" "$PKG/install/doinst.sh"
# Strip debug symbols where possible to keep package size down; ignore
# failures (e.g. static/stripped-already binaries, non-ELF files).
find "$PKG" -type f \( -perm -u+x -o -name '*.so*' \) -exec sh -c \
'file "$1" | grep -q ELF && strip --strip-unneeded "$1" 2>/dev/null || true' _ {} \;
# Reproducible builds: Slackware's own makepkg already sorts its file
# list (LC_COLLATE=C sort) before archiving, so member ORDER is already
# deterministic — but it only clamps file mtimes in the resulting tar
# when $SOURCE_DATE_EPOCH is set (verified by reading a real
# /sbin/makepkg: `if [ -n "${SOURCE_DATE_EPOCH}" ]; then MTIME=
# "--clamp-mtime --mtime=@${SOURCE_DATE_EPOCH}"; fi`). Without it, every
# separate build run stamps freshly-compiled files with its own wall-clock
# time, so two builds of the *same* source produce byte-different .txz
# files — which is exactly what broke release.yml's "rebuild in CI and
# verify it matches the checksums committed in podman.plg" step (found
# live: aardvark-dns's checksum differed between two separate Gitea
# Actions runs of the identical tagged commit). Deriving it from the
# repo's last commit time (not `date`/a random per-build value) keeps it
# stable across any number of rebuilds of the same commit, while still
# changing whenever the source actually does.
export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(cd "$CWD/../.." && git log -1 --format=%ct 2>/dev/null || echo 0)}"
local pkg_file="$PRGNAM-$version-$arch-$build$tag.txz"
( cd "$PKG" && makepkg --linkadd y --chown y "$OUTPUT/$pkg_file" )
( cd "$OUTPUT" && sha256sum "$pkg_file" > "$pkg_file.sha256" )
( cd "$OUTPUT" && md5sum "$pkg_file" > "$pkg_file.md5" )
echo "==> [$PRGNAM] built $OUTPUT/$pkg_file"
}