From ca90a19d734214918c54452f7955e3ec9d1c2652 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 12 Aug 2026 04:24:38 -0700 Subject: [PATCH 1/4] Push every output of a selected pkgbase --package meant a pkgbase to bin/build and a literal package name to bin/push-build, so deploy --package nvidia-580xx-utils built three packages and published one, leaving nvidia-580xx-dkms and opencl-nvidia-580xx behind with no indication anything was missing. Hit while deploying exactly that package. Selection now matches on the pkgbase recorded in .PKGINFO as well as on the package name, so a pkgbase ships all of its outputs and an individual name still selects just that one. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 8 +++++--- bin/push-build | 26 +++++++++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d63b0eb..f6433b6 100644 --- a/README.md +++ b/README.md @@ -228,9 +228,11 @@ setting is named for the repository rather than for building, which happens wherever you like. The same setting tells `bin/omarchy-pkgs release` which host to poke after a release push. -Split packages are selected by their own names, not their pkgbase — pushing -`nvidia-580xx-utils` does not carry `nvidia-580xx-dkms` along. Omit `--package` to -push everything built. +`--package` means the same thing as it does to `build`: a pkgbase, whose every +output ships together. Pushing `nvidia-580xx-utils` carries `nvidia-580xx-dkms` +and `opencl-nvidia-580xx` with it, because that is what the build produced. An +output's own name still selects just that one, for publishing a single package +on purpose. Omit `--package` to push everything built. Publishing signs and promotes everything staged on the host, not just what this push uploaded, so `push` stops when it finds packages already staged there — diff --git a/bin/push-build b/bin/push-build index 21523e8..bbb7d67 100755 --- a/bin/push-build +++ b/bin/push-build @@ -143,29 +143,41 @@ if [[ -z "$PACKAGES" && "$ASSUME_YES" == true ]] && ! on_repo_host; then exit 1 fi +# --package means the same thing here as it does to bin/build: a pkgbase, whose +# every output ships together. Selecting only the artifact whose filename matched +# would publish one third of a split package like nvidia-580xx-utils and silently +# leave nvidia-580xx-dkms and opencl-nvidia-580xx behind. An output's own name +# still matches, for pushing just one of them on purpose. +# +# pkgbase comes from .PKGINFO rather than the PKGBUILD: it is what makepkg +# actually recorded, and it needs no guessing about which directory built what. +pkgbase_of() { + bsdtar -xOf "$1" .PKGINFO 2>/dev/null | + awk -F ' = ' '$1 == "pkgbase" { print $2; exit }' +} + FILES=() if [[ -z "$PACKAGES" ]]; then FILES=("${ALL_FILES[@]}") else + declare -A MATCHED=() for file in "${ALL_FILES[@]}"; do # name-version-release-arch.pkg.tar.zst -> name pkgname="${file%-*-*-*.pkg.tar.*}" + pkgbase=$(pkgbase_of "$BUILD_OUTPUT_DIR/$file") for wanted in $PACKAGES; do - if [[ "$pkgname" == "$wanted" ]]; then + if [[ "$pkgname" == "$wanted" || "$pkgbase" == "$wanted" ]]; then FILES+=("$file") + MATCHED["$wanted"]=1 break fi done done for wanted in $PACKAGES; do - found=false - for file in "${FILES[@]}"; do - [[ "${file%-*-*-*.pkg.tar.*}" == "$wanted" ]] && found=true && break - done - if [[ "$found" != true ]]; then + if [[ -z "${MATCHED[$wanted]:-}" ]]; then print_error "No built artifact for '$wanted' in $BUILD_OUTPUT_DIR" - print_warning "Split packages are named after their outputs, not their pkgbase" + print_warning "Name a package or the pkgbase it was built from" exit 1 fi done From dbe1db4b03cd1b9cdfcc1d91ca6ce2a5f042d873 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 12 Aug 2026 04:59:22 -0700 Subject: [PATCH 2/4] Read the remote database without depending on bsdtar The partial-tree guard parsed omarchy.db with bsdtar, which is not installed on the repository host. Every sync there aborted with "the remote database exists but could not be read" -- a guard meant to catch a partial tree instead blocked a complete one, stopping a publish after sign, promote and update had already succeeded. GNU tar reads the database fine when it is a seekable file; the pipe was what defeated it originally, and that is already downloaded to a temp file. tar now leads, with bsdtar as a fallback for a tar too old to detect zstd. Co-Authored-By: Claude Opus 5 (1M context) --- bin/sync-repo | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bin/sync-repo b/bin/sync-repo index fc8394f..b0574c1 100755 --- a/bin/sync-repo +++ b/bin/sync-repo @@ -141,14 +141,24 @@ if grep -qx 'omarchy\.db' <<<"$REMOTE_LISTING"; then REMOTE_DB_FILE=$(mktemp) trap 'rm -f "$REMOTE_DB_FILE"' EXIT rclone cat "$REMOTE/$DESTINATION_DIRECTORY/omarchy.db" --s3-no-head >"$REMOTE_DB_FILE" 2>/dev/null - REMOTE_NAMES=$(bsdtar -tf "$REMOTE_DB_FILE" 2>/dev/null | sed 's|/.*||' | - sed -E 's/-[^-]+-[^-]+$//' | sort -u) + + # Download to a file rather than piping: GNU tar detects the compression from + # a seekable archive but not from a pipe, and the database has been both gzip + # and zstd over its life. bsdtar reads either too, but it is not present on + # every host, so tar leads and bsdtar covers a tar too old for zstd. + REMOTE_ENTRIES=$(tar -tf "$REMOTE_DB_FILE" 2>/dev/null) + if [[ -z "$REMOTE_ENTRIES" ]] && command -v bsdtar >/dev/null 2>&1; then + REMOTE_ENTRIES=$(bsdtar -tf "$REMOTE_DB_FILE" 2>/dev/null) + fi + + REMOTE_NAMES=$(printf '%s\n' "$REMOTE_ENTRIES" | sed 's|/.*||' | + sed -E 's/-[^-]+-[^-]+$//' | sort -u | grep -v '^$') if [[ -z "$REMOTE_NAMES" ]]; then print_error "The remote database exists but could not be read" echo "" echo "Refusing to sync rather than assume the remote is empty. Check that" - echo "bsdtar is installed and that omarchy.db is not corrupt." + echo "omarchy.db is not corrupt: rclone cat $REMOTE/$DESTINATION_DIRECTORY/omarchy.db | tar -t" exit 1 fi From f8c1cbb072878eaa25a9117a5e60ad7eaeacd122 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 12 Aug 2026 05:04:06 -0700 Subject: [PATCH 3/4] Add bin/setup to prepare a repository host The sync guard could not read the repository database because bsdtar was not installed on the host, and the first fix was to parse around its absence. The better answer is for the host to have what the tooling needs: libarchive ships the library pacman links against without necessarily installing the binary, so bsdtar being present was an assumption, not a fact. bin/setup installs the dependencies, enables Docker, creates the state directory, and installs and enables the release timers -- the steps the README previously listed by hand. It is idempotent and takes --check to report without changing anything. Signing credentials and the rclone remote hold secrets, so it reports on those rather than creating them. sync-repo goes back to reading the database with bsdtar alone, and says to run bin/setup when it is missing. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 26 +++---- bin/repo | 5 ++ bin/setup | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ bin/sync-repo | 22 +++--- 4 files changed, 217 insertions(+), 23 deletions(-) create mode 100755 bin/setup diff --git a/README.md b/README.md index f6433b6..46b60e3 100644 --- a/README.md +++ b/README.md @@ -560,21 +560,21 @@ State files are stored in `/root/.state/`: ### Installation ```bash -# Copy systemd units -cp /root/omarchy-pkgs/systemd/*.service /root/omarchy-pkgs/systemd/*.timer /etc/systemd/system/ - -# Reload systemd -systemctl daemon-reload - -# Enable and start timers -systemctl enable --now omarchy-check-versions.timer -systemctl enable --now omarchy-auto-release-edge.timer -systemctl enable --now omarchy-auto-release-stable.timer - -# Create state directory -mkdir -p /root/.state +ssh root@ 'cd /root/omarchy-pkgs && bin/setup' ``` +`bin/setup` installs the dependencies, enables Docker, creates the state +directory, and installs and enables the release timers. It is idempotent, so +run it again whenever a dependency is added. + +```bash +bin/repo setup --check # Report what is missing, change nothing +bin/repo setup --skip-timers # Prepare the host without the release timers +``` + +Signing credentials (`/root/.omarchy/build-credentials`) and the rclone remote +hold secrets, so setup reports on them rather than creating them. + ### Management ```bash diff --git a/bin/repo b/bin/repo index 0e2c40e..48c91cc 100755 --- a/bin/repo +++ b/bin/repo @@ -60,6 +60,7 @@ show_usage() { echo " sync Sync repository to remote" echo " push Upload local builds to the repository host and publish them there" echo " deploy Build locally, then push: one command from a build machine" + echo " setup Install everything the repository host needs" echo "" echo "Typical workflows:" echo " $0 release # Complete release workflow" @@ -135,6 +136,10 @@ deploy) "$SCRIPT_DIR/deploy" "$@" 2>&1 | tee "$LOG_FILE" exit ${PIPESTATUS[0]} ;; +setup) + "$SCRIPT_DIR/setup" "$@" 2>&1 | tee "$LOG_FILE" + exit ${PIPESTATUS[0]} + ;; -h | --help | help) show_usage ;; diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..d3bf979 --- /dev/null +++ b/bin/setup @@ -0,0 +1,187 @@ +#!/bin/bash +# Prepare this machine to serve as the Omarchy repository host. +# +# The repository host builds packages, signs them, keeps the published tree, and +# syncs it to the mirror. Everything it needs is installed and enabled here, so +# the tooling can assume its toolchain instead of working around whatever +# happens to be present. +# +# Run this on the host itself: +# ssh root@ 'cd /root/omarchy-pkgs && bin/setup' +# +# It is idempotent — run it again after adding a dependency. + +set -e + +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +source "$BUILD_ROOT/helpers/message-helpers.sh" + +CHECK_ONLY=false +SKIP_TIMERS=false + +# Packages, in "command:package" form so a missing tool names its own fix. +# +# tar, vercmp and repo-add come from base and pacman, which any Arch install +# already has. bsdtar does not: libarchive ships the library pacman links +# against without necessarily installing the binary. +REQUIREMENTS=( + "bsdtar:libarchive" # reads repo databases and .PKGINFO out of packages + "git:git" # PKGBUILD sources and release commits + "jq:jq" # package metadata in .omarchy/package.json + "curl:curl" # upstream version checks + "rsync:rsync" # receives uploads from bin/repo push + "gpg:gnupg" # package signing + "rclone:rclone" # publishes to the mirror + "docker:docker" # builds run in containers + "makepkg:base-devel" # source verification for releases +) + +STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}" +CREDENTIALS="/root/.omarchy/build-credentials" + +print_header "Omarchy Repository Host Setup" + +while [[ $# -gt 0 ]]; do + case $1 in + --check) + CHECK_ONLY=true + shift + ;; + --skip-timers) + SKIP_TIMERS=true + shift + ;; + -h | --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Install and enable everything the repository host needs." + echo "" + echo "Options:" + echo " --check Report what is missing, change nothing" + echo " --skip-timers Do not install or enable the release timers" + echo " -h, --help Show this help message" + exit 0 + ;; + *) + print_error "Unknown option: $1" + exit 1 + ;; + esac +done + +if ! command -v pacman >/dev/null 2>&1; then + print_error "This is not an Arch system — the repository host must be Arch" + exit 1 +fi + +if [[ "$CHECK_ONLY" != true && $EUID -ne 0 ]]; then + print_error "Run as root (installing packages and systemd units)" + exit 1 +fi + +# --- dependencies ------------------------------------------------------------ + +print_info "Checking dependencies..." +MISSING_PACKAGES=() +for requirement in "${REQUIREMENTS[@]}"; do + cmd="${requirement%%:*}" + pkg="${requirement#*:}" + if command -v "$cmd" >/dev/null 2>&1; then + print_step "$cmd" + else + print_warning "$cmd missing (provided by $pkg)" + MISSING_PACKAGES+=("$pkg") + fi +done +echo "" + +if [[ ${#MISSING_PACKAGES[@]} -gt 0 ]]; then + if [[ "$CHECK_ONLY" == true ]]; then + print_warning "Would install: ${MISSING_PACKAGES[*]}" + else + print_info "Installing: ${MISSING_PACKAGES[*]}" + pacman -S --needed --noconfirm "${MISSING_PACKAGES[@]}" + print_success "Dependencies installed" + fi +else + print_success "All dependencies present" +fi +echo "" + +# --- docker ------------------------------------------------------------------ + +if [[ "$CHECK_ONLY" == true ]]; then + if systemctl is-enabled docker.service >/dev/null 2>&1; then + print_success "docker.service is enabled" + else + print_warning "docker.service would be enabled" + fi +else + print_info "Enabling docker..." + systemctl enable --now docker.service + print_success "docker.service enabled" +fi +echo "" + +# --- state directory --------------------------------------------------------- + +if [[ -d "$STATE_DIR" ]]; then + print_success "State directory present: $STATE_DIR" +elif [[ "$CHECK_ONLY" == true ]]; then + print_warning "Would create $STATE_DIR" +else + mkdir -p "$STATE_DIR" + print_success "Created $STATE_DIR" +fi +echo "" + +# --- release timers ---------------------------------------------------------- + +if [[ "$SKIP_TIMERS" == true ]]; then + print_info "Skipping release timers (--skip-timers)" +elif [[ "$CHECK_ONLY" == true ]]; then + for timer in omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-stable; do + if systemctl is-enabled "$timer.timer" >/dev/null 2>&1; then + print_success "$timer.timer is enabled" + else + print_warning "$timer.timer would be enabled" + fi + done +else + print_info "Installing release timers..." + cp "$BUILD_ROOT"/systemd/*.service "$BUILD_ROOT"/systemd/*.timer /etc/systemd/system/ + systemctl daemon-reload + for timer in omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-stable; do + systemctl enable --now "$timer.timer" + print_step "$timer.timer" + done + print_success "Release timers enabled" +fi +echo "" + +# --- credentials ------------------------------------------------------------- + +# These hold secrets, so setup reports on them rather than creating them. +print_info "Checking credentials..." + +if [[ -f "$CREDENTIALS" ]]; then + print_success "Signing credentials present: $CREDENTIALS" +else + print_warning "Missing $CREDENTIALS" + echo " Must export GPG_PRIVATE_KEY and GPG_PASSPHRASE; the release" + echo " services source it before signing." +fi + +if rclone listremotes 2>/dev/null | grep -q '^pkgs.omarchy.org:'; then + print_success "rclone remote 'pkgs.omarchy.org' configured" +else + print_warning "rclone remote 'pkgs.omarchy.org' not configured" + echo " bin/repo sync publishes there; configure it with 'rclone config'." +fi +echo "" + +if [[ "$CHECK_ONLY" == true ]]; then + print_info "Check complete — nothing was changed" +else + print_success "Repository host ready" +fi diff --git a/bin/sync-repo b/bin/sync-repo index b0574c1..52fa72f 100755 --- a/bin/sync-repo +++ b/bin/sync-repo @@ -142,23 +142,25 @@ if grep -qx 'omarchy\.db' <<<"$REMOTE_LISTING"; then trap 'rm -f "$REMOTE_DB_FILE"' EXIT rclone cat "$REMOTE/$DESTINATION_DIRECTORY/omarchy.db" --s3-no-head >"$REMOTE_DB_FILE" 2>/dev/null - # Download to a file rather than piping: GNU tar detects the compression from - # a seekable archive but not from a pipe, and the database has been both gzip - # and zstd over its life. bsdtar reads either too, but it is not present on - # every host, so tar leads and bsdtar covers a tar too old for zstd. - REMOTE_ENTRIES=$(tar -tf "$REMOTE_DB_FILE" 2>/dev/null) - if [[ -z "$REMOTE_ENTRIES" ]] && command -v bsdtar >/dev/null 2>&1; then - REMOTE_ENTRIES=$(bsdtar -tf "$REMOTE_DB_FILE" 2>/dev/null) + if ! command -v bsdtar >/dev/null 2>&1; then + print_error "bsdtar is not installed" + echo "" + echo "The repository host needs it to read the repository database." + echo "Run bin/setup to install everything this host requires." + exit 1 fi - REMOTE_NAMES=$(printf '%s\n' "$REMOTE_ENTRIES" | sed 's|/.*||' | - sed -E 's/-[^-]+-[^-]+$//' | sort -u | grep -v '^$') + # bsdtar, not tar: repo-add has used both gzip and zstd for the database, and + # libarchive detects either without being told which. + REMOTE_NAMES=$(bsdtar -tf "$REMOTE_DB_FILE" 2>/dev/null | sed 's|/.*||' | + sed -E 's/-[^-]+-[^-]+$//' | sort -u) if [[ -z "$REMOTE_NAMES" ]]; then print_error "The remote database exists but could not be read" echo "" echo "Refusing to sync rather than assume the remote is empty. Check that" - echo "omarchy.db is not corrupt: rclone cat $REMOTE/$DESTINATION_DIRECTORY/omarchy.db | tar -t" + echo "omarchy.db is not corrupt:" + echo " rclone cat $REMOTE/$DESTINATION_DIRECTORY/omarchy.db | bsdtar -tf -" exit 1 fi From daf98026bca200695562c7e8fe46728f41cf8440 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 12 Aug 2026 05:43:23 -0700 Subject: [PATCH 4/4] Make bin/setup work on Ubuntu, which is what the host runs The first version checked for pacman and refused anything else, so it would have declined to run on the actual repository host. Nothing about that host needs to be Arch: makepkg, repo-add and signing all happen inside containers. Setup now detects apt or pacman and installs the right names for each -- bsdtar is libarchive-tools on Debian and libarchive on Arch. The requirement list drops gnupg and the Arch build tools, which the host never runs directly, leaving Docker, rclone, bsdtar, jq, git and rsync. Docker is checked before being installed. A host may be running a version from Docker's own repository, and replacing that underneath a working builder would be a poor trade for consistency; setup starts it if stopped and otherwise leaves it alone. Verified both paths: apt installs the five dependencies and docker.io on a bare Ubuntu 24.04 container, and an Arch host with Docker already running is left untouched. A missing systemctl now reports that a container cannot be a repository host instead of failing on an unknown command. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 12 ++++-- bin/setup | 123 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 100 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 46b60e3..5f1e891 100644 --- a/README.md +++ b/README.md @@ -563,9 +563,15 @@ State files are stored in `/root/.state/`: ssh root@ 'cd /root/omarchy-pkgs && bin/setup' ``` -`bin/setup` installs the dependencies, enables Docker, creates the state -directory, and installs and enables the release timers. It is idempotent, so -run it again whenever a dependency is added. +`bin/setup` installs the dependencies, ensures Docker is running, creates the +state directory, and installs and enables the release timers. It works on +Debian/Ubuntu and on Arch, and is idempotent, so run it again whenever a +dependency is added. + +The host does not need to be Arch: makepkg, repo-add and package signing all +run inside containers, so it needs only Docker, rclone, bsdtar, jq, git and +rsync. Docker is left alone when it already works, rather than replacing a +working installation from Docker's own repository with the distribution's. ```bash bin/repo setup --check # Report what is missing, change nothing diff --git a/bin/setup b/bin/setup index d3bf979..7b1427c 100755 --- a/bin/setup +++ b/bin/setup @@ -1,10 +1,10 @@ #!/bin/bash # Prepare this machine to serve as the Omarchy repository host. # -# The repository host builds packages, signs them, keeps the published tree, and -# syncs it to the mirror. Everything it needs is installed and enabled here, so -# the tooling can assume its toolchain instead of working around whatever -# happens to be present. +# The host receives uploads, promotes packages into the published tree, and +# syncs that tree to the mirror. Everything Arch-specific — makepkg, repo-add, +# package signing — happens inside containers, so the host itself needs very +# little and does not need to be Arch. The production host is Ubuntu. # # Run this on the host itself: # ssh root@ 'cd /root/omarchy-pkgs && bin/setup' @@ -19,23 +19,6 @@ source "$BUILD_ROOT/helpers/message-helpers.sh" CHECK_ONLY=false SKIP_TIMERS=false -# Packages, in "command:package" form so a missing tool names its own fix. -# -# tar, vercmp and repo-add come from base and pacman, which any Arch install -# already has. bsdtar does not: libarchive ships the library pacman links -# against without necessarily installing the binary. -REQUIREMENTS=( - "bsdtar:libarchive" # reads repo databases and .PKGINFO out of packages - "git:git" # PKGBUILD sources and release commits - "jq:jq" # package metadata in .omarchy/package.json - "curl:curl" # upstream version checks - "rsync:rsync" # receives uploads from bin/repo push - "gpg:gnupg" # package signing - "rclone:rclone" # publishes to the mirror - "docker:docker" # builds run in containers - "makepkg:base-devel" # source verification for releases -) - STATE_DIR="${OMARCHY_STATE_DIR:-/root/.state}" CREDENTIALS="/root/.omarchy/build-credentials" @@ -55,6 +38,7 @@ while [[ $# -gt 0 ]]; do echo "Usage: $0 [OPTIONS]" echo "" echo "Install and enable everything the repository host needs." + echo "Works on Debian/Ubuntu (apt) and Arch (pacman)." echo "" echo "Options:" echo " --check Report what is missing, change nothing" @@ -69,18 +53,65 @@ while [[ $# -gt 0 ]]; do esac done -if ! command -v pacman >/dev/null 2>&1; then - print_error "This is not an Arch system — the repository host must be Arch" +# --- distribution ------------------------------------------------------------ + +# Package names differ where it matters: bsdtar is libarchive-tools on Debian +# and libarchive on Arch, and Docker is docker.io rather than docker. +if command -v apt-get >/dev/null 2>&1; then + DISTRO="debian" + PKG_BSDTAR="libarchive-tools" + PKG_DOCKER="docker.io" +elif command -v pacman >/dev/null 2>&1; then + DISTRO="arch" + PKG_BSDTAR="libarchive" + PKG_DOCKER="docker" +else + print_error "Unsupported distribution — need apt-get or pacman" exit 1 fi +print_info "Distribution: $DISTRO" + 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. +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 "container; run setup on the host itself." + exit 1 +fi + +install_packages() { + case "$DISTRO" in + debian) + apt-get update -qq + DEBIAN_FRONTEND=noninteractive apt-get install -y "$@" + ;; + arch) + pacman -S --needed --noconfirm "$@" + ;; + esac +} + # --- dependencies ------------------------------------------------------------ +# Only what the host runs directly. Signing and repo-add happen in containers, +# so gnupg and the Arch build tools are deliberately absent from this list. +REQUIREMENTS=( + "bsdtar:$PKG_BSDTAR" # reads repo databases and .PKGINFO out of packages + "git:git" # pulls this repository + "jq:jq" # package metadata in .omarchy/package.json + "rsync:rsync" # receives uploads from bin/repo push + "rclone:rclone" # publishes to the mirror +) + print_info "Checking dependencies..." MISSING_PACKAGES=() for requirement in "${REQUIREMENTS[@]}"; do @@ -100,7 +131,7 @@ if [[ ${#MISSING_PACKAGES[@]} -gt 0 ]]; then print_warning "Would install: ${MISSING_PACKAGES[*]}" else print_info "Installing: ${MISSING_PACKAGES[*]}" - pacman -S --needed --noconfirm "${MISSING_PACKAGES[@]}" + install_packages "${MISSING_PACKAGES[@]}" print_success "Dependencies installed" fi else @@ -110,16 +141,42 @@ echo "" # --- docker ------------------------------------------------------------------ -if [[ "$CHECK_ONLY" == true ]]; then - if systemctl is-enabled docker.service >/dev/null 2>&1; then - print_success "docker.service is enabled" +# Docker is left alone when it already works. A host may well 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..." + +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" + elif [[ "$CHECK_ONLY" == true ]]; then + print_warning "Docker is installed but not running; would start it" else - print_warning "docker.service would be enabled" + print_info "Docker is installed but not running — starting it" + systemctl enable --now docker.service + if docker info >/dev/null 2>&1; then + print_success "Docker started" + else + print_error "Docker is installed but still not responding" + echo " Check 'systemctl status docker' — builds cannot run without it." + exit 1 + fi fi +elif [[ "$CHECK_ONLY" == true ]]; then + print_warning "Would install $PKG_DOCKER and enable it" else - print_info "Enabling docker..." + print_info "Installing $PKG_DOCKER..." + install_packages "$PKG_DOCKER" systemctl enable --now docker.service - print_success "docker.service enabled" + if docker info >/dev/null 2>&1; then + print_success "Docker installed and running" + else + print_error "Docker installed but not responding" + echo " Check 'systemctl status docker' — builds cannot run without it." + exit 1 + fi fi echo "" @@ -137,10 +194,12 @@ echo "" # --- release timers ---------------------------------------------------------- +TIMERS=(omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-stable) + if [[ "$SKIP_TIMERS" == true ]]; then print_info "Skipping release timers (--skip-timers)" elif [[ "$CHECK_ONLY" == true ]]; then - for timer in omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-stable; do + for timer in "${TIMERS[@]}"; do if systemctl is-enabled "$timer.timer" >/dev/null 2>&1; then print_success "$timer.timer is enabled" else @@ -151,7 +210,7 @@ else print_info "Installing release timers..." cp "$BUILD_ROOT"/systemd/*.service "$BUILD_ROOT"/systemd/*.timer /etc/systemd/system/ systemctl daemon-reload - for timer in omarchy-check-versions omarchy-auto-release-edge omarchy-auto-release-stable; do + for timer in "${TIMERS[@]}"; do systemctl enable --now "$timer.timer" print_step "$timer.timer" done