Files
omarchy-pkgs/bin/build
T
Marcelo Alcantara 191e1e7db5 Add builder flags for CI and resumed builds
Three opt-in knobs for bin/build, each defaulting to today's behaviour:

OMARCHY_KEEP_BUILD_WORKSPACE=1 keeps build-output/$MIRROR/$ARCH instead
of wiping it, and build/build.sh now folds any packages already there
into omarchy-build.db even when no database exists yet, so packages
built by an earlier job (or a previous, interrupted run) resolve as
dependencies of what builds next.

OMARCHY_SKIP_BUILDER_IMAGE=1 uses the omarchy-pkg-builder image already
present instead of building it, so a workflow can build the image once
with an external BuildKit cache and fan out over package jobs that all
run the same bytes. A missing image is an error, not a silent rebuild.

OMARCHY_DEFER_RUNTIME_DEPS=true builds the omarchy/omarchy-settings pair
with --nodeps, installing only their makedepends and checkdepends
explicitly. The pair depends on each other and on packages a sharded
pipeline builds in other jobs, so they cannot resolve in isolation; the
assembled set is installed in one verified transaction downstream. The
request is refused for anything but exactly that pair, on the host
before Docker starts and again inside the container.

Also fix make_dir_writable: chown -R can succeed on part of the tree
and fail on files a previous container left behind as another uid, and
the old `|| chmod` fallback only ran when chown failed outright. Always
follow with chmod.
2026-09-02 23:15:30 +10:00

248 lines
8.3 KiB
Python
Executable File

#!/bin/bash
# Abort if anything fails
set -e
# Source common functions
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"
print_header "Omarchy Package Builder"
DRY_RUN=false
# Knobs for builds driven from CI or resumed by hand. Each defaults to the
# historical behaviour, so an unadorned `bin/build` is unchanged.
#
# OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output/$MIRROR/$ARCH instead of
# wiping it, so packages built by an earlier
# job (or a previous, interrupted run) seed
# the build database and resolve as
# dependencies of what builds now.
# OMARCHY_SKIP_BUILDER_IMAGE=1 use the omarchy-pkg-builder image already
# present instead of building it; a workflow
# that builds the image once with an external
# BuildKit cache can then fan out over many
# package jobs without each one rebuilding it.
# OMARCHY_DEFER_RUNTIME_DEPS=true build the omarchy/omarchy-settings pair
# with --nodeps (see build/build.sh); only
# for a pipeline that verifies the install
# transaction afterwards.
KEEP_BUILD_WORKSPACE=${OMARCHY_KEEP_BUILD_WORKSPACE:-0}
SKIP_BUILDER_IMAGE=${OMARCHY_SKIP_BUILDER_IMAGE:-0}
DEFER_RUNTIME_DEPS=${OMARCHY_DEFER_RUNTIME_DEPS:-false}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--arch)
ARCH="$2"
update_arch_paths
shift 2
;;
--mirror)
MIRROR="$2"
if ! validate_mirror "$MIRROR"; then
print_error "Invalid mirror: $MIRROR (must be one of: $VALID_MIRRORS)"
exit 1
fi
update_arch_paths
shift 2
;;
--package)
shift
PACKAGES=""
while [[ $# -gt 0 && ! "$1" =~ ^-- ]]; do
PACKAGES="$PACKAGES $1"
shift
done
PACKAGES="${PACKAGES# }"
;;
--dry-run)
DRY_RUN=true
shift
;;
-h | --help)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --arch <arch> Target architecture (x86_64 or aarch64, default: x86_64)"
echo " --mirror <mirror> Mirror to use (edge, rc, or stable, default: edge)"
echo " --package <names> Build only the specified package(s) (space-separated)"
echo " --dry-run Show what would build without running makepkg"
echo " -h, --help Show this help message"
echo ""
echo "This script builds packages from pkgbuilds/ based on .omarchy/package.json:"
echo " --mirror edge: builds packages not marked skip_build=true"
echo " --mirror stable: builds fast-ring packages not marked skip_build=true"
echo " --package: explicitly builds selected packages, even with skip_build=true"
echo ""
echo "Or build specific packages with --package:"
echo " Package names should match directories in pkgbuilds/"
echo ""
echo "Examples:"
echo " $0 --arch aarch64"
echo " $0 --mirror stable"
echo " $0 --package yay"
echo " $0 --package yay elephant cursor-bin"
echo ""
echo "Environment (for CI and resumed builds; defaults keep today's behaviour):"
echo " OMARCHY_KEEP_BUILD_WORKSPACE=1 keep build-output and reuse packages already there"
echo " OMARCHY_SKIP_BUILDER_IMAGE=1 use the existing builder image instead of building it"
echo " OMARCHY_DEFER_RUNTIME_DEPS=true build the omarchy pair with --nodeps (transaction verified later)"
echo ""
exit 0
;;
*)
print_error "Unknown option: $1"
exit 1
;;
esac
done
if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then
print_error "OMARCHY_DEFER_RUNTIME_DEPS must be true or false"
exit 1
fi
# Deferring runtime dependencies is only sound for the omarchy pair, and only
# when both halves are built together: the pair depends on each other and on
# packages that a sharded pipeline builds in other jobs, and the consumer of
# this mode installs the assembled set in one verified transaction. Check the
# request here so a misuse fails before Docker starts.
if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then
deferred_runtime=0
deferred_settings=0
deferred_count=0
for package in $PACKAGES; do
((deferred_count += 1))
case $package in
omarchy|omarchy-dev) deferred_runtime=1 ;;
omarchy-settings|omarchy-settings-dev) deferred_settings=1 ;;
*)
print_error "OMARCHY_DEFER_RUNTIME_DEPS only applies to the omarchy/omarchy-settings pair, not $package"
exit 1
;;
esac
done
if (( deferred_runtime != 1 || deferred_settings != 1 || deferred_count != 2 )); then
print_error "OMARCHY_DEFER_RUNTIME_DEPS requires --package with exactly the omarchy pair"
exit 1
fi
fi
# Show target architecture and mirror after parsing args
print_info "Target architecture: $ARCH"
print_info "Mirror: $MIRROR"
print_info "Build workspace: $BUILD_OUTPUT_DIR"
print_info "Final output: $REPO_DIR"
if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then
print_info "Runtime dependency checks: deferred to the install transaction"
fi
if [[ "$DRY_RUN" == true ]]; then
print_warning "DRY RUN MODE - build plan only; no Docker or makepkg will run"
ARCH="$ARCH" \
MIRROR="$MIRROR" \
PACKAGES="$PACKAGES" \
DRY_RUN=true \
DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS" \
PKGBUILDS_DIR="$PKGBUILDS_DIR" \
BUILD_OUTPUT_DIR="$BUILD_OUTPUT_DIR" \
FINAL_OUTPUT_DIR="$REPO_DIR" \
HELPERS_DIR="$BUILD_ROOT/helpers" \
SRC_DIR="$SRC_DIR" \
"$BUILD_ROOT/build/build.sh"
exit $?
fi
# Create directories if they don't exist
mkdir -p "$BUILD_OUTPUT_DIR" "$REPO_DIR" "$SRC_DIR"
# Check Docker is available
check_docker
# Setup QEMU for aarch64 builds on x86_64 hosts
if [[ "$(uname -m)" == "x86_64" && "$ARCH" == "aarch64" ]]; then
# Check if QEMU is already working
if ! docker run --rm --platform linux/arm64 alpine:3.21 /bin/true >/dev/null 2>&1; then
print_info "Setting up QEMU for ARM64 emulation..."
setup_qemu
fi
fi
# Clean build-output directory to start fresh, unless the caller seeded it
# with packages from an earlier job or is resuming an interrupted run.
if [[ $KEEP_BUILD_WORKSPACE == "1" ]]; then
print_info "Keeping existing build workspace..."
else
print_info "Cleaning build workspace..."
rm -rf "$BUILD_OUTPUT_DIR"/*
fi
mkdir -p "$BUILD_OUTPUT_DIR"
# Show package info
if [[ -n "$PACKAGES" ]]; then
print_info "Building packages: $PACKAGES"
else
print_info "Building unscoped packages for $MIRROR mirror"
fi
# Build/update the Docker image, unless the caller prepared the exact image
# already (a workflow building it once with an external BuildKit cache). A
# missing image is an error rather than a silent rebuild: the point of the
# 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
print_error "Prepared builder image is unavailable: $IMAGE_TAG"
exit 1
fi
print_info "Using prepared builder image: $IMAGE_TAG"
else
build_docker_image "$BUILD_DIR" "$ARCH" "$MIRROR"
fi
print_info "Running package build..."
# Create output directories if they don't exist
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"
# Build Docker arguments
DOCKER_ARGS=(
--rm
-e ARCH="$ARCH"
-e MIRROR="$MIRROR"
-e PACKAGES="$PACKAGES"
-e OMARCHY_RC_PINS="${OMARCHY_RC_PINS:-}"
-e DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS"
-v "$BUILD_ROOT/build-output:/build-output"
-v "$REPO_ROOT:/pkgs.omarchy.org"
-v "$BUILD_DIR:/build:ro"
-v "$BUILD_ROOT/helpers:/helpers:ro"
-v "$BUILD_ROOT/pkgbuilds:/pkgbuilds:ro"
)
# Run the builder with assembled args
PLATFORM_ARG=$(get_platform_arg "$ARCH")
docker run $PLATFORM_ARG "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
BUILD_RESULT=$?
# Summary
echo ""
if [[ $BUILD_RESULT -eq 0 ]]; then
print_success "Build completed successfully!"
else
print_warning "Some packages failed (see details above)"
exit $BUILD_RESULT
fi