diff --git a/bin/build b/bin/build index 2d32294b..7e6ae58d 100755 --- a/bin/build +++ b/bin/build @@ -145,7 +145,7 @@ if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then fi if [[ "$DRY_RUN" == true ]]; then - print_warning "DRY RUN MODE - build plan only; no Docker or makepkg will run" + print_warning "DRY RUN MODE - build plan only; no container or makepkg will run" ARCH="$ARCH" \ MIRROR="$MIRROR" \ PACKAGES="$PACKAGES" \ @@ -163,8 +163,8 @@ fi # Create directories if they don't exist mkdir -p "$BUILD_OUTPUT_DIR" "$REPO_DIR" "$SRC_DIR" -# Check Docker is available -check_docker +# Check the selected container engine is available +check_engine # A foreign target architecture runs under QEMU user emulation. Probe by # actually running a container for the target platform: that is the only @@ -172,9 +172,11 @@ check_docker HOST_ARCH=$(uname -m) [[ "$HOST_ARCH" == "arm64" ]] && HOST_ARCH=aarch64 if [[ "$HOST_ARCH" != "$ARCH" ]]; then - if ! docker run --rm "$(get_platform_arg "$ARCH")" alpine:3.21 /bin/true >/dev/null 2>&1; then + PROBE_IMAGE="alpine:3.21" + [[ "$CONTAINER_ENGINE" == "podman" ]] && PROBE_IMAGE="docker.io/library/alpine:3.21" + if ! "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$ARCH")" "$PROBE_IMAGE" /bin/true >/dev/null 2>&1; then print_info "Setting up QEMU for $ARCH emulation on this $HOST_ARCH host..." - setup_qemu + setup_qemu "$ARCH" fi fi @@ -201,7 +203,7 @@ fi # flag is that every job runs the same bytes. IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR" if [[ $SKIP_BUILDER_IMAGE == "1" ]]; then - if ! docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then + if ! "$CONTAINER_ENGINE" image inspect "$IMAGE_TAG" >/dev/null 2>&1; then print_error "Prepared builder image is unavailable: $IMAGE_TAG" exit 1 fi @@ -216,9 +218,12 @@ print_info "Running package build..." mkdir -p "$BUILD_OUTPUT_DIR" mkdir -p "$REPO_DIR" -# Ensure output directories are writable by container user -make_dir_writable "$BUILD_OUTPUT_DIR" -make_dir_writable "$REPO_DIR" +# Rootful Docker writes as the image uid, so retain its existing permission +# workaround. Rootless Podman uses keep-id and must leave ownership/modes alone. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$BUILD_OUTPUT_DIR" + make_dir_writable "$REPO_DIR" +fi # Build Docker arguments DOCKER_ARGS=( @@ -235,10 +240,16 @@ DOCKER_ARGS=( -v "$BUILD_ROOT/pkgbuilds:/pkgbuilds:ro" ) +# Podman-created images can leave WORKDIR owned by a remapped uid. Mount the +# existing host-user-owned workspace so the builder can write there. +if [[ "$CONTAINER_ENGINE" == "podman" ]]; then + DOCKER_ARGS+=(-v "$SRC_DIR:/src") +fi + # Run the builder with assembled args PLATFORM_ARG=$(get_platform_arg "$ARCH") -docker run "$PLATFORM_ARG" "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh +"$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" "$PLATFORM_ARG" "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh BUILD_RESULT=$? diff --git a/bin/clean-docker b/bin/clean-docker index 4a5fc945..93c9a659 100755 --- a/bin/clean-docker +++ b/bin/clean-docker @@ -1,21 +1,27 @@ #!/bin/bash -# Clean Docker builder images and cache +# Clean builder images and cache for the selected container engine # Forces a fresh image build on next run set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" -print_header "Cleaning Docker Builder Images" +check_engine +print_header "Cleaning Container Builder Images" # Remove all omarchy-pkg-builder images print_info "Removing omarchy-pkg-builder images..." -docker images omarchy-pkg-builder -q | xargs -r docker rmi -f 2>/dev/null || true +"$CONTAINER_ENGINE" images omarchy-pkg-builder -q | xargs -r "$CONTAINER_ENGINE" rmi -f 2>/dev/null || true -# Clear buildx cache for these builds -print_info "Clearing buildx cache..." -docker buildx prune -a -f +# Clear this engine's build cache. +print_info "Clearing build cache..." +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + "$CONTAINER_ENGINE" buildx prune -a -f +else + "$CONTAINER_ENGINE" image prune --build-cache -f +fi -print_success "Docker builder cache cleared" +print_success "Container builder cache cleared" print_info "Next build will create fresh images" diff --git a/bin/omarchy-release b/bin/omarchy-release index 762bd80d..12e689bd 100755 --- a/bin/omarchy-release +++ b/bin/omarchy-release @@ -18,6 +18,7 @@ set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/host-helpers.sh" @@ -374,8 +375,8 @@ iso_checkout() { # prints a usable omarchy-iso checkout, cloning to tmp if neede build_iso() { # build_iso [--rc] local version="$1" rc_flag="${2:-}" dir dir=$(iso_checkout) || return 1 - if ! command -v docker >/dev/null || ! docker info >/dev/null 2>&1; then - print_warning "Docker unavailable — cannot build the ISO here. Run on a Docker machine:" + if [[ -z "$CONTAINER_ENGINE" ]] || ! "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_warning "No container engine is available — cannot build the ISO here. Run on a Docker or Podman machine:" echo " cd $dir && bin/omarchy-iso-release ${rc_flag:+$rc_flag }$version" return 1 fi @@ -1034,10 +1035,10 @@ cmd_doctor() { else print_warning "no build host configured — set OMARCHY_REPO_HOST, --host, or write an ssh destination (root@ or an ssh-config alias) to $BUILD_ROOT/.repo-host; until then builds trigger on the 6h timer only" fi - if command -v docker >/dev/null && docker info >/dev/null 2>&1; then - print_success "docker available (ISO builds possible here)" + if [[ -n "$CONTAINER_ENGINE" ]] && "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_success "$CONTAINER_ENGINE available (ISO builds possible here)" else - print_warning "docker unavailable — ISO builds will print instructions instead" + print_warning "container engine unavailable — ISO builds will print instructions instead" fi echo "" if ((failures == 0)); then diff --git a/bin/remove-package b/bin/remove-package index eedcfb03..4700402b 100755 --- a/bin/remove-package +++ b/bin/remove-package @@ -62,8 +62,8 @@ if [[ ! -d "$REPO_DIR" ]]; then exit 1 fi -# Check Docker is available -check_docker +# Check the selected container engine is available +check_engine # Find package files to confirm before running Docker cd "$REPO_DIR" @@ -99,11 +99,13 @@ acquire_release_lock || exit 1 print_info "Removing package..." -# Ensure directory is writable by container user -make_dir_writable "$REPO_DIR" +# Rootless Podman uses keep-id and leaves host ownership/modes intact. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$REPO_DIR" +fi # Run the removal script in the host-native image. -docker run --rm "$(get_platform_arg "$TOOL_ARCH")" \ +"$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \ -e ARCH="$ARCH" \ -e MIRROR="$MIRROR" \ -v "$REPO_ROOT:/pkgs.omarchy.org" \ diff --git a/bin/setup b/bin/setup index 6727d96c..69dfe0e9 100755 --- a/bin/setup +++ b/bin/setup @@ -15,6 +15,7 @@ set -e BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") source "$BUILD_ROOT/helpers/message-helpers.sh" +source "$BUILD_ROOT/helpers/docker-helpers.sh" source "$BUILD_ROOT/helpers/paths.sh" CHECK_ONLY=false @@ -62,10 +63,12 @@ if command -v apt-get >/dev/null 2>&1; then DISTRO="debian" PKG_BSDTAR="libarchive-tools" PKG_DOCKER="docker.io" + PKG_PODMAN="podman" elif command -v pacman >/dev/null 2>&1; then DISTRO="arch" PKG_BSDTAR="libarchive" PKG_DOCKER="docker" + PKG_PODMAN="podman" else print_error "Unsupported distribution — need apt-get or pacman" exit 1 @@ -73,18 +76,36 @@ fi print_info "Distribution: $DISTRO" +# A fresh repository host still defaults to Docker. An explicit Podman choice, +# or a working Podman selected by the shared helper, is left alone. +if [[ -z "$CONTAINER_ENGINE" ]]; then + CONTAINER_ENGINE=docker + export CONTAINER_ENGINE +fi +if ! container_engine_supported; then + print_error "Unsupported CONTAINER_ENGINE: $CONTAINER_ENGINE (use docker or podman)" + exit 1 +fi +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + PKG_ENGINE="$PKG_DOCKER" + ENGINE_NAME="Docker" +else + PKG_ENGINE="$PKG_PODMAN" + ENGINE_NAME="Podman" +fi + if [[ "$CHECK_ONLY" != true && $EUID -ne 0 ]]; then print_error "Run as root (installing packages and systemd units)" exit 1 fi -# Docker and the release timers are both systemd units. Say so plainly rather -# than failing later on a missing command — a container is the usual way to end -# up here, and it cannot be a repository host. +# The release timers are systemd units. Say so plainly rather than failing +# later on a missing command — a container is the usual way to end up here, +# and it cannot be a repository host. if [[ "$CHECK_ONLY" != true ]] && ! command -v systemctl >/dev/null 2>&1; then print_error "systemctl not found — the repository host must run systemd" echo "" - echo "Docker and the release timers are systemd units. This looks like a" + echo "The release timers are systemd units. This looks like a" echo "container; run setup on the host itself." exit 1 fi @@ -140,24 +161,27 @@ else fi echo "" -# --- docker ------------------------------------------------------------------ +# --- container engine -------------------------------------------------------- -# Docker is left alone when it already works. A host may well be running a +# The selected engine is left alone when it already works. A host may be running a # version from Docker's own repository rather than the distribution's, and # replacing that underneath a working builder would be a poor trade for # tidiness. -print_info "Checking Docker..." +print_info "Checking $ENGINE_NAME..." -if command -v docker >/dev/null 2>&1; then - print_step "docker present: $(docker --version 2>/dev/null | head -1)" - if docker info >/dev/null 2>&1; then - print_success "Docker is installed and running — leaving it alone" +if command -v "$CONTAINER_ENGINE" >/dev/null 2>&1; then + print_step "$CONTAINER_ENGINE present: $("$CONTAINER_ENGINE" --version 2>/dev/null | head -1)" + if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_success "$ENGINE_NAME is installed and available — leaving it alone" elif [[ "$CHECK_ONLY" == true ]]; then - print_warning "Docker is installed but not running; would start it" + print_warning "$ENGINE_NAME is installed but unavailable" + elif [[ "$CONTAINER_ENGINE" == "podman" ]]; then + print_error "Podman is installed but unavailable to the current user" + exit 1 else print_info "Docker is installed but not running — starting it" systemctl enable --now docker.service - if docker info >/dev/null 2>&1; then + if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then print_success "Docker started" else print_error "Docker is installed but still not responding" @@ -166,16 +190,24 @@ if command -v docker >/dev/null 2>&1; then fi fi elif [[ "$CHECK_ONLY" == true ]]; then - print_warning "Would install $PKG_DOCKER and enable it" -else - print_info "Installing $PKG_DOCKER..." - install_packages "$PKG_DOCKER" - systemctl enable --now docker.service - if docker info >/dev/null 2>&1; then - print_success "Docker installed and running" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + print_warning "Would install $PKG_ENGINE and enable it" else - print_error "Docker installed but not responding" - echo " Check 'systemctl status docker' — builds cannot run without it." + print_warning "Would install $PKG_ENGINE" + fi +else + print_info "Installing $PKG_ENGINE..." + install_packages "$PKG_ENGINE" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + systemctl enable --now docker.service + fi + if "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + print_success "$ENGINE_NAME installed and available" + else + print_error "$ENGINE_NAME installed but not responding" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + echo " Check 'systemctl status docker' — builds cannot run without it." + fi exit 1 fi fi diff --git a/bin/sign b/bin/sign index c517b9ba..e1e99cf3 100755 --- a/bin/sign +++ b/bin/sign @@ -52,8 +52,8 @@ if [[ ! -d "$BUILD_OUTPUT_DIR" ]]; then exit 1 fi -# Check Docker is available -check_docker +# Check the selected container engine is available +check_engine # Check GPG credentials are in environment if [[ -z "$GPG_PRIVATE_KEY" ]]; then @@ -76,11 +76,13 @@ build_docker_image "$BUILD_DIR" "$TOOL_ARCH" "$MIRROR" print_info "Running package signing..." -# Ensure output directory is writable by container user -make_dir_writable "$BUILD_OUTPUT_DIR" +# Rootless Podman uses keep-id and leaves host ownership/modes intact. +if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$BUILD_OUTPUT_DIR" +fi # Run the signing script in the host-native image. -docker run --rm "$(get_platform_arg "$TOOL_ARCH")" \ +"$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$TOOL_ARCH")" \ -e ARCH="$ARCH" \ -e MIRROR="$MIRROR" \ -e GPG_PRIVATE_KEY="$GPG_PRIVATE_KEY" \ diff --git a/bin/update-repo b/bin/update-repo index 894e1972..2fda5493 100755 --- a/bin/update-repo +++ b/bin/update-repo @@ -11,12 +11,11 @@ source "$BUILD_ROOT/helpers/docker-helpers.sh" source "$BUILD_ROOT/helpers/paths.sh" source "$BUILD_ROOT/helpers/lock-helpers.sh" -# Function to update repository database using Docker +# Function to update repository database using the selected container engine update_database() { - print_info "Updating repository database in Docker container..." + print_info "Updating repository database in a container..." - # Check Docker is available - check_docker + check_engine # Ensure output directory exists if [[ ! -d "$REPO_DIR" ]]; then @@ -25,8 +24,9 @@ update_database() { exit 1 fi - # Make output directory writable for container - make_dir_writable "$REPO_DIR" + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + make_dir_writable "$REPO_DIR" + fi # repo-add is architecture- and mirror-independent. Use the host-native edge # image, which also lets bootstrap-rc run before that channel exists remotely. @@ -37,7 +37,7 @@ update_database() { } build_docker_image "$BUILD_DIR" "$tool_arch" "edge" - docker run --rm "$(get_platform_arg "$tool_arch")" \ + "$CONTAINER_ENGINE" run "${CONTAINER_RUN_ARGS[@]}" --rm "$(get_platform_arg "$tool_arch")" \ -e ARCH="$ARCH" \ -e MIRROR="$MIRROR" \ -v "$REPO_ROOT:/output" \ diff --git a/helpers/docker-helpers.sh b/helpers/docker-helpers.sh index c108340e..3ec61974 100644 --- a/helpers/docker-helpers.sh +++ b/helpers/docker-helpers.sh @@ -1,14 +1,57 @@ -# Docker helper functions for Omarchy package build system +# Container engine helpers for Omarchy package build system -check_docker() { - if ! command -v docker &>/dev/null; then - print_error "Docker is not installed" +container_engine_supported() { + [[ "$CONTAINER_ENGINE" == "docker" || "$CONTAINER_ENGINE" == "podman" ]] +} + +if [[ -z "${CONTAINER_ENGINE:-}" ]]; then + for engine in docker podman; do + if command -v "$engine" >/dev/null 2>&1 && "$engine" info >/dev/null 2>&1; then + CONTAINER_ENGINE="$engine" + break + fi + done +fi +export CONTAINER_ENGINE + +# Rootless Podman otherwise maps the image's builder uid 1000 to a subordinate +# host uid. keep-id makes files written through bind mounts belong to the user +# who invoked the build. +CONTAINER_RUN_ARGS=() +if [[ "$CONTAINER_ENGINE" == "podman" ]]; then + CONTAINER_RUN_ARGS+=("--userns=keep-id:uid=1000,gid=1000") +fi + +check_engine() { + if [[ -n "$CONTAINER_ENGINE" ]] && ! container_engine_supported; then + print_error "Unsupported CONTAINER_ENGINE: $CONTAINER_ENGINE (use docker or podman)" exit 1 fi - if ! docker info &>/dev/null; then - print_error "Docker daemon is not running" - print_warning "Start Docker with: sudo systemctl start docker" + if [[ -z "$CONTAINER_ENGINE" ]]; then + print_error "No working container engine found (tried Docker, then Podman)" + if command -v docker >/dev/null 2>&1; then + print_warning "Docker is installed but unavailable. Start it with: sudo systemctl start docker" + elif command -v podman >/dev/null 2>&1; then + print_warning "Podman is installed but 'podman info' failed" + else + print_warning "Install Docker or Podman" + fi + exit 1 + fi + + if ! command -v "$CONTAINER_ENGINE" >/dev/null 2>&1; then + print_error "$CONTAINER_ENGINE is not installed" + exit 1 + fi + + if ! "$CONTAINER_ENGINE" info >/dev/null 2>&1; then + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + print_error "Docker daemon is not running or is not accessible" + print_warning "Start Docker with: sudo systemctl start docker" + else + print_error "Podman is not available to the current user" + fi exit 1 fi } @@ -22,8 +65,22 @@ docker_native_arch() { } setup_qemu() { + local target_arch="${1:-aarch64}" + + if [[ "$CONTAINER_ENGINE" == "podman" ]]; then + local registration="/proc/sys/fs/binfmt_misc/qemu-$target_arch" + if [[ -r "$registration" ]] && grep -q '^flags:.*F' "$registration"; then + print_success "QEMU $target_arch emulation is registered" + return 0 + fi + + print_error "Rootless Podman requires host QEMU binfmt registration with the F flag" + print_info "Install it with: sudo pacman -S qemu-user-static qemu-user-static-binfmt" + exit 1 + fi + # Register emulators for builds whose target differs from the host. - if ! docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes >/dev/null 2>&1; then + if ! "$CONTAINER_ENGINE" run --rm --privileged docker.io/multiarch/qemu-user-static --reset -p yes --credential yes >/dev/null 2>&1; then print_error "Failed to set up QEMU emulation" exit 1 fi @@ -47,15 +104,24 @@ build_docker_image() { ;; esac - print_info "Building Docker image for $arch ($platform) using $mirror mirror..." - - docker buildx build \ - --platform "$platform" \ - --build-arg MIRROR="$mirror" \ - --load \ - -t "$image_tag" \ - -f "$build_dir/Dockerfile" \ - "$build_dir" + print_info "Building container image for $arch ($platform) using $mirror mirror..." + + if [[ "$CONTAINER_ENGINE" == "docker" ]]; then + "$CONTAINER_ENGINE" buildx build \ + --platform "$platform" \ + --build-arg MIRROR="$mirror" \ + --load \ + -t "$image_tag" \ + -f "$build_dir/Dockerfile" \ + "$build_dir" + else + "$CONTAINER_ENGINE" build \ + --platform "$platform" \ + --build-arg MIRROR="$mirror" \ + -t "$image_tag" \ + -f "$build_dir/Dockerfile" \ + "$build_dir" + fi } get_platform_arg() {