diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-14 17:03:08 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-14 17:03:08 +0200 |
| commit | abccc01065b74f06191c7b408ee2903aca9fdcfb (patch) | |
| tree | f21136474e50ebc8e52300536e11a7c7813460ff /.extras/image-builder/bootstrap.sh | |
| parent | 8e3003d162ec1e362523c0d3c232753950d2d343 (diff) | |
| download | sbo-slackbuilds-abccc01065b74f06191c7b408ee2903aca9fdcfb.tar.gz sbo-slackbuilds-abccc01065b74f06191c7b408ee2903aca9fdcfb.zip | |
extract docker tooling to ../sbo-dockerbuild
The test-build tool and image-builder chain grew from a .extras/ helper into a
standalone toolchain, now maintained in its own repo (../sbo-dockerbuild). Remove
them here (scripts, examples, design docs) and repoint CLAUDE.md: test-building
is now done with the installed `test-build` CLI, deps resolved from the SBo tree,
both trees verified per bump. Package-maintenance files stay: nvchecker.toml,
hooks/, assets/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to '.extras/image-builder/bootstrap.sh')
| -rwxr-xr-x | .extras/image-builder/bootstrap.sh | 552 |
1 files changed, 0 insertions, 552 deletions
diff --git a/.extras/image-builder/bootstrap.sh b/.extras/image-builder/bootstrap.sh deleted file mode 100755 index 9893e66..0000000 --- a/.extras/image-builder/bootstrap.sh +++ /dev/null @@ -1,552 +0,0 @@ -#!/bin/bash -# bootstrap.sh — build the sbo-base:{ver} image FROM scratch from NAS trees. -# Run as root (installpkg). Adapted from forge slackware/docker-images. -set -euo pipefail -HERE="$(cd "$(dirname "$0")" && pwd)" -source "${HERE}/config" -LOG_TAG=bootstrap -source "${HERE}/lib.sh" - -REGISTRY_IMAGE="${REGISTRY}/sbo-base" -[[ "${EUID}" -eq 0 ]] || _err "run as root." -mkdir -p "${HASH_DIR}" - -# ============================================================================ -# Package list -# -# Names only - the version and build suffix are resolved automatically -# from PACKAGES.TXT for the x86_64 target architecture. -# -# slackpkg and its runtime dependencies (perl, gnupg2, gpgme) are included -# so users can run slackpkg inside the container without extra setup. -# ============================================================================ - -PACKAGES=( - # Base system - aaa_base - coreutils - aaa_glibc-solibs - aaa_libraries - aaa_terminfo - pam - cracklib - libpwquality - acl - attr - bash - bin - brotli - bzip2 - c-ares - cpio - curl - cyrus-sasl - devs - dialog - diffutils - duktape - elvis - etc - file - findutils - flex - gawk - gnutls - grep - groff - gzip - iproute2 - iptables - jansson - less - libcgroup - libpsl - librsvg - libtermcap - mtr - ncurses - net-tools - network-scripts - nghttp2 - nghttp3 - ngtcp2 - nvi - openssh - patch - pcre2 - pinentry - pkgtools - polkit - procps-ng - quota - rsync - screen - sed - elogind - shadow - sharutils - strace - sudo - sysfsutils - sysvinit - sysvinit-scripts - tar - time - tree - eudev - usbutils - utempter - util-linux - wget - which - whois - xz - zlib - # Package management - series ap - slackpkg - # Perl runtime required by slackpkg - series d - perl - # e2fsprogs provides libcom_err.so.2 required by perl on 15.0 - e2fsprogs - # libunistring - Unicode string library required by gnupg2 dependencies - libunistring - # gnupg2 runtime libraries (series l) - required for gpg to start at all - libgpg-error - libgcrypt - libassuan - libksba - npth - sqlite - icu4c - # GPG/PKCS support for slackpkg (series l) - gpgme - # GnuPG binaries (series n) - # gnupg = version 1, called by slackpkg on Slackware 15.0 - # gnupg2 = version 2, called by slackpkg on -current - gnupg - gnupg2 - # Full OpenSSL and CA bundle - required for HTTPS inside the container - openssl - ca-certificates -) - -# ============================================================================ -# Argument parsing -# ============================================================================ - -FORCE=false -OPT_VERSION="" -while [[ $# -gt 0 ]]; do - case "$1" in - --version) OPT_VERSION="$2"; shift 2 ;; - --force) FORCE=true; shift ;; - *) _err "unknown argument: $1" ;; - esac -done -if [[ -n "${OPT_VERSION}" ]]; then - BUILD_VARIANTS=("${OPT_VERSION}") -else - BUILD_VARIANTS=("${VARIANTS[@]}") -fi - -# ============================================================================ -# ChangeLog tracking -# ============================================================================ - -# changelog_changed VERSION -> 0 if changed (or --force), else 1 -changelog_changed() { - local version="$1" - require_mount "${version}" # loud fail if NAS unmounted, not silent skip - local repo_key="slackware64-${version}" - local hash_file="${HASH_DIR}/${repo_key}.sha256" - local url; url="$(mirror_path "${MIRROR}" "${repo_key}/ChangeLog.txt")" - local tmp; tmp="$(mktemp)" - if ! fetch "${url}" "${tmp}"; then - rm -f "${tmp}" - _warn "${repo_key}: cannot read ChangeLog (${url}); skipping." - return 1 - fi - local live; live=$(sha256sum "${tmp}" | cut -d' ' -f1); rm -f "${tmp}" - if [[ "${FORCE}" == "true" ]]; then - echo "${live}" > "${hash_file}"; return 0 - fi - local stored=""; [[ -f "${hash_file}" ]] && stored=$(cat "${hash_file}") - if [[ "${live}" == "${stored}" ]]; then - _log "${repo_key}: ChangeLog unchanged; skipping."; return 1 - fi - _log "${repo_key}: ChangeLog changed."; echo "${live}" > "${hash_file}"; return 0 -} - -# ============================================================================ -# Package discovery via PACKAGES.TXT -# -# Search order: patches/ -> main repo -> extra/ -# This mirrors Slackware's own install priority: a patched package in -# patches/ always takes precedence over the same package in the main tree. -# ============================================================================ - -# download_pkgtxt PKGTXT_DIR PKG_PATH -# Downloads PACKAGES.TXT from patches/, the repo root, and extra/ into -# PKGTXT_DIR. Sets global associative array PKGTXT[loc] -> local file path. -# Uses -L to follow HTTP redirects (some mirrors use them) and checks that -# the downloaded file is non-empty before marking it as available. -declare -gA PKGTXT=() - -download_pkgtxt() { - local PKGTXT_DIR="$1" - local PKG_PATH="$2" - mkdir -p "${PKGTXT_DIR}" - PKGTXT=() - local LOC URL DEST - for LOC in patches main extra; do - case "${LOC}" in - patches) URL="${PKG_PATH}/patches/PACKAGES.TXT" ;; - main) URL="${PKG_PATH}/PACKAGES.TXT" ;; - extra) URL="${PKG_PATH}/extra/PACKAGES.TXT" ;; - esac - DEST="${PKGTXT_DIR}/${LOC}.txt" - if fetch "${URL}" "${DEST}" && [[ -s "${DEST}" ]]; then - PKGTXT["${LOC}"]="${DEST}" - _log " PACKAGES.TXT [${LOC}]: $(wc -l < "${DEST}") lines" - else - rm -f "${DEST}" - _log " PACKAGES.TXT [${LOC}]: not available at ${URL}" - fi - done -} - -# find_package NAME -# Searches PKGTXT files in order: patches -> main -> extra. -# Prints the path relative to PKG_PATH, e.g.: -# slackware64/a/bash-5.2-x86_64-1.txz -# -# Base name extraction uses the Slackware convention: the filename has the -# form PKGBASE-VERSION-ARCH-BUILD.txz where VERSION, ARCH and BUILD are -# guaranteed to contain no dashes. Reversing, cutting the last three fields, -# and reversing again always yields the correct PKGBASE: -# echo aaa_glibc-solibs-2.42-x86_64-1.txz | rev | cut -d- -f4- | rev -# -> aaa_glibc-solibs -find_package() { - local PKG="$1" - [[ "${#PKGTXT[@]}" -gt 0 ]] || { - _warn "find_package called with empty PKGTXT array - PACKAGES.TXT not loaded" - return 1 - } - for LOC in patches main extra; do - [[ -v "PKGTXT[${LOC}]" ]] || continue - local RESULT - RESULT=$(awk -v want="${PKG}" ' - /^PACKAGE NAME:/ { - filename = $NF - # PKGBASE = all fields except the last three (VERSION ARCH BUILD) - n = split(filename, parts, "-") - base = parts[1] - for (i = 2; i <= n-3; i++) base = base "-" parts[i] - if (base == want) found = filename - } - /^PACKAGE LOCATION:/ && found { - loc = $NF; sub(/^\.\//, "", loc) - print loc "/" found - found = ""; exit - } - ' "${PKGTXT[${LOC}]}") - if [[ -n "${RESULT}" ]]; then - echo "${RESULT}" - return 0 - fi - done - return 1 -} - -# ============================================================================ -# Build one variant -# ============================================================================ - -build_variant() { - local VERSION="$1" - require_mount "${VERSION}" - local REPO_KEY="slackware64-${VERSION}" - local PKG_PATH; PKG_PATH="$(mirror_path "${MIRROR}" "${REPO_KEY}")" - local TAG="${REGISTRY_IMAGE}:${VERSION}" - # Bake the LAN HTTP mirror into the base image: the full-image build serves - # the NFS mirror over HTTP on the bridge (see build-full-image.sh), rooted - # at the variant's mirror tree, so slackpkg pulls from LAN not the internet. - # The URL is variant-agnostic (the server's --directory selects the tree). - local SLACKPKG_MIRROR="http://${HTTP_MIRROR_HOST}:${HTTP_MIRROR_PORT}/" - - _log "=== Building ${TAG} from ${PKG_PATH} ===" - - local WORKDIR - WORKDIR="$(mktemp -d /tmp/slackware-bootstrap.XXXXXX)" - # shellcheck disable=SC2064 - trap "rm -rf '${WORKDIR}'" EXIT - - local ROOTFS="${WORKDIR}/rootfs" - local PKGCACHE="${WORKDIR}/packages" - local PKGTXT_DIR="${WORKDIR}/pkgtxt" - mkdir -p "${ROOTFS}" "${PKGCACHE}" - - # --- Download PACKAGES.TXT index files --- - download_pkgtxt "${PKGTXT_DIR}" "${PKG_PATH}" - - # --- Download packages --- - _log "Locating and downloading packages..." - local PKG RELPATH FILENAME URL - for PKG in "${PACKAGES[@]}"; do - RELPATH=$(find_package "${PKG}") || { - _warn " '${PKG}' not found in any PACKAGES.TXT - skipping" - continue - } - FILENAME="${RELPATH##*/}" - URL="${PKG_PATH}/${RELPATH}" - _log " ${FILENAME}" - fetch "${URL}" "${PKGCACHE}/${FILENAME}" || { - _warn " Failed to download ${URL}" - } - done - - # --- Install packages --- - _log "Installing packages..." - for PKG in "${PKGCACHE}"/*.txz; do - [[ -f "${PKG}" ]] || continue - ROOT="${ROOTFS}" installpkg --terse "${PKG}" - done - - # --- Run ldconfig to create library symlinks --- - # Some packages (notably aaa_libraries on stable releases) install - # versioned library files (e.g. libtinfo.so.6.3) and rely on ldconfig - # to create the unversioned symlinks (e.g. libtinfo.so.6). On -current - # the symlinks are included in the package tarballs directly; on 15.0 - # they are not. Running ldconfig here ensures the symlinks exist before - # any chroot operations and before the dependency check below. - _log "Running ldconfig..." - chroot "${ROOTFS}" /sbin/ldconfig - - # --- Check for missing library dependencies --- - # Run ldd on key binaries inside the chroot and report anything missing - # before it surfaces as a runtime failure inside the container. - _log "Checking for missing library dependencies in key binaries..." - local DEPCHECK_OK=true - for BIN in /usr/bin/gpg /usr/bin/gpg2 /usr/sbin/slackpkg /usr/bin/perl /bin/wget /usr/bin/openssl; do - [[ -f "${ROOTFS}${BIN}" ]] || continue - local MISSING_LIBS - MISSING_LIBS=$(chroot "${ROOTFS}" ldd "${BIN}" 2>/dev/null | grep "not found" || true) - if [[ -n "${MISSING_LIBS}" ]]; then - _warn "${BIN} has unresolved dependencies:" - while IFS= read -r lib; do - _warn " ${lib}" - done <<< "${MISSING_LIBS}" - DEPCHECK_OK=false - fi - done - if [[ "${DEPCHECK_OK}" == "true" ]]; then - _log "Dependency check passed." - else - _warn "Add the missing packages to PACKAGES and rebuild with --force." - fi - - # --- Prepare rootfs for container use --- - _log "Configuring rootfs for container use..." - - # Remove the empty update.d hook directory so that update-ca-certificates - # does not look for run-parts (which ships with dcron, not installed here). - rm -rf "${ROOTFS}/etc/ca-certificates/update.d" - - # Disable init scripts that manage hardware absent in a container - local SVC - for SVC in rc.acpid rc.pcmcia rc.setterm rc.udev; do - [[ -f "${ROOTFS}/etc/rc.d/${SVC}" ]] && chmod -x "${ROOTFS}/etc/rc.d/${SVC}" - done - - # Each container generates its own SSH host keys on first start - rm -f "${ROOTFS}"/etc/ssh/*key* - - # /etc/mtab should reflect the container's actual mounts - rm -f "${ROOTFS}/etc/mtab" - ln -s /proc/mounts "${ROOTFS}/etc/mtab" - - # No hardware clock in a container - sed -i -e '/^if \[ -x \/sbin\/hwclock/,/^fi$/s/^/#/' \ - "${ROOTFS}/etc/rc.d/rc.S" 2>/dev/null || true - - # The filesystem write-check would drop the container into a recovery shell - sed -i -e '/^if touch \/fsrwtestfile/,/^fi$/s/^/#/' \ - "${ROOTFS}/etc/rc.d/rc.S" 2>/dev/null || true - - # Skip all filesystem checks at boot - touch "${ROOTFS}/etc/fastboot" - - # setterm is not useful in a container - sed -i -e '/\/bin\/setterm/s/^/# /' \ - "${ROOTFS}/etc/rc.d/rc.M" 2>/dev/null || true - - # Cannot write to the hardware clock at shutdown - sed -i -e '/systohc/s/^/# /' \ - "${ROOTFS}/etc/rc.d/rc.6" 2>/dev/null || true - - # Container-appropriate fstab - cat > "${ROOTFS}/etc/fstab" <<'FSTAB' -devtmpfs /dev devtmpfs defaults 0 0 -devpts /dev/pts devpts gid=5,mode=620 0 0 -tmpfs /dev/shm tmpfs defaults,nodev,nosuid,mode=1777 0 0 -FSTAB - - # No TTYs in a container; disable agetty and reduce console definitions - sed -i -e '/agetty/s/^c/#c/' \ - "${ROOTFS}/etc/inittab" 2>/dev/null || true - sed -i -e '/^c3\|^c4\|^c5\|^c6/s/^/# /' \ - "${ROOTFS}/etc/inittab" 2>/dev/null || true - - # Invalidate the root password (containers use other means of access) - sed -i -e '/^root/s/^root::/root:!:/' \ - "${ROOTFS}/etc/shadow" 2>/dev/null || true - - # Terminal configuration - printf 'export TERM=linux\n' > "${ROOTFS}/etc/profile.d/term.sh" - chmod +x "${ROOTFS}/etc/profile.d/term.sh" - - # Source /etc/profile from .bashrc so interactive sessions are fully - # initialised (Docker sets / as the home directory for root in containers) - printf '. /etc/profile\n' > "${ROOTFS}/.bashrc" - - # DNS resolver - printf 'nameserver 1.1.1.1\nnameserver 8.8.4.4\n' \ - >> "${ROOTFS}/etc/resolv.conf" - - # Configure slackpkg for silent non-interactive use in a container - if [[ -f "${ROOTFS}/etc/slackpkg/slackpkg.conf" ]]; then - sed -i 's/DIALOG=on/DIALOG=off/' "${ROOTFS}/etc/slackpkg/slackpkg.conf" - sed -i 's/POSTINST=on/POSTINST=off/' "${ROOTFS}/etc/slackpkg/slackpkg.conf" - sed -i 's/SPINNING=on/SPINNING=off/' "${ROOTFS}/etc/slackpkg/slackpkg.conf" - sed -i 's/WGETFLAGS="--passive-ftp/& --no-verbose/' \ - "${ROOTFS}/etc/slackpkg/slackpkg.conf" - fi - - # Configure the slackpkg mirror - [[ -f "${ROOTFS}/etc/slackpkg/mirrors" ]] && \ - printf '%s\n' "${SLACKPKG_MIRROR}" >> "${ROOTFS}/etc/slackpkg/mirrors" - - # slackpkg requires an explicit opt-in for -current - if [[ "${VERSION}" == "current" ]]; then - mkdir -p "${ROOTFS}/var/lib/slackpkg" - touch "${ROOTFS}/var/lib/slackpkg/current" - fi - - # Set a predictable locale for all chroot operations. - # Without this, perl emits locale warnings when the host locale - # (e.g. en_US.UTF-8) is not installed in the minimal rootfs. - export LC_ALL=C - - # Rebuild the CA certificate database inside the rootfs. - # Without this, wget cannot verify Let's Encrypt certificates and every - # HTTPS download by slackpkg fails with an SSL error. That SSL error in - # turn causes the interactive "do you want to import the GPG key?" prompt - # even when -batch=on -default_answer=y is set. - _log "Refreshing CA certificate database..." - chroot "${ROOTFS}" /usr/sbin/update-ca-certificates --fresh 1>/dev/null || _warn "CA certificate update failed; SSL verification may not work correctly." - - # Import the Slackware GPG key and update the package list. - # 'yes YES |' feeds a pre-emptive answer to the interactive import prompt - # as a safety net; with the CA database now valid the prompt should not - # appear because the download succeeds on the first attempt. - _log "Importing Slackware GPG key via slackpkg..." - # Check slackpkg's own exit status, not the pipeline's: 'yes' takes SIGPIPE - # when slackpkg exits first, which can make the pipeline non-zero even on - # success (false "GPG update failed" warning). Wrapping in 'if' exempts the - # pipeline from set -e/pipefail so we can read PIPESTATUS[1] (slackpkg's own - # status) instead of the SIGPIPE-poisoned pipeline status. - local gpg_rc - if yes YES | chroot "${ROOTFS}" /usr/sbin/slackpkg -batch=on -default_answer=y update gpg - then gpg_rc="${PIPESTATUS[1]}"; else gpg_rc="${PIPESTATUS[1]}"; fi - [[ "${gpg_rc}" -eq 0 ]] || _warn "slackpkg GPG update failed; check the output above." - - _log "Updating slackpkg package list..." - chroot "${ROOTFS}" /usr/sbin/slackpkg -batch=on -default_answer=y update || _warn "slackpkg update failed; cache will build on first container run." - - # --- Clean up --- - _log "Cleaning up rootfs..." - rm -f "${ROOTFS}"/boot/* - rm -f "${ROOTFS}"/tmp/[A-Za-z]* - rm -f "${ROOTFS}"/var/mail/* - rm -rf "${ROOTFS}"/dev/* - rm -rf "${ROOTFS}"/usr/share/locale/* - rm -rf "${ROOTFS}"/usr/info/* - rm -rf "${ROOTFS}"/usr/man/* - (cd "${ROOTFS}/usr/doc" 2>/dev/null && \ - find . -type d -mindepth 2 -maxdepth 2 | grep -v '/cups-' | \ - xargs rm -rf) || true - rm -rf "${ROOTFS}"/usr/doc/*/html - rm -f "${ROOTFS}"/usr/doc/*/*.{pdf,db,gz,bz2,xz,txt,TXT} 2>/dev/null || true - rm -rf "${ROOTFS}"/usr/share/gtk-doc - rm -rf "${ROOTFS}"/usr/share/help - find "${ROOTFS}"/usr/share/ -type d -name doc | xargs rm -rf - # Keep only the terminfo entries the container actually needs - find "${ROOTFS}"/usr/share/terminfo/ -type f \ - ! -name 'linux' ! -name 'xterm' ! -name 'screen.linux' \ - -delete 2>/dev/null || true - find "${ROOTFS}"/usr/share/terminfo/ -xtype l -delete 2>/dev/null || true - # gpg-agent socket files left behind by slackpkg in the chroot. - # tar cannot archive Unix domain sockets; removing them eliminates - # the "socket ignored" warnings when the image tarball is created. - rm -f "${ROOTFS}"/root/.gnupg/S.* - - # Kernel-specific content has no meaning in a container - rm -rf "${ROOTFS}"/usr/src - rm -rf "${ROOTFS}"/lib/modules - rm -rf "${ROOTFS}"/lib64/modules 2>/dev/null || true - - # --- Build Docker image --- - _log "Creating image tarball..." - tar -C "${ROOTFS}" -czf "${WORKDIR}/rootfs.tar.gz" . - - local BUILD_DATE - BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" - - cat > "${WORKDIR}/Dockerfile" <<DOCKERFILE -FROM scratch -LABEL maintainer="Eric Hameleers <alien@slackware.com>" -ADD rootfs.tar.gz / -ENV container=docker -ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -LABEL org.opencontainers.image.created="${BUILD_DATE}" -LABEL org.opencontainers.image.title="Slackware Linux ${VERSION} (x86_64)" -LABEL org.opencontainers.image.description="Slackware Linux ${VERSION} base container image (x86_64)" -LABEL org.opencontainers.image.vendor="Slackware Linux" -LABEL org.opencontainers.image.licenses="GPL-2.0-or-later AND LGPL-2.1-or-later" -LABEL org.opencontainers.image.url="http://www.slackware.com" -LABEL org.opencontainers.image.source="https://forge.slackware.nl/slackware/docker-images" -LABEL org.opencontainers.image.revision="${VERSION}" -LABEL slackware.version="${VERSION}" -LABEL slackware.arch="x86_64" -CMD ["/bin/bash"] -DOCKERFILE - - # Explicit exit checks: build_variant runs in an `if ! (...)` condition - # (see Main), which suppresses `set -e` in this subshell, so a failed build - # would otherwise push a nonexistent tag and log "Done". - _log "Building and pushing ${TAG}..." - if ! docker build --no-cache -t "${TAG}" "${WORKDIR}"; then - _warn "build failed for ${TAG}; not pushing." - return 1 - fi - if ! docker push "${TAG}"; then - _warn "push failed for ${TAG}." - return 1 - fi - _log "=== Done: ${TAG} ===" -} - -# ============================================================================ -# Main -# ============================================================================ - -rc=0 -for VERSION in "${BUILD_VARIANTS[@]}"; do - if changelog_changed "${VERSION}"; then - if ! ( build_variant "${VERSION}" ); then - _warn "variant ${VERSION} failed; continuing." - rc=1 - fi - fi -done -exit "${rc}" |
