Merge pull request #276 from maralcbr/omacom/builder-flags
Add builder flags for CI and resumed builds
This commit is contained in:
3 files changed
+167
-17
No files matched your search
@@ -13,6 +13,27 @@ print_header "Omarchy Package Builder"
|
|||||||
|
|
||||||
DRY_RUN=false
|
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
|
# Parse command line arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
@@ -67,6 +88,11 @@ while [[ $# -gt 0 ]]; do
|
|||||||
echo " $0 --package yay"
|
echo " $0 --package yay"
|
||||||
echo " $0 --package yay elephant cursor-bin"
|
echo " $0 --package yay elephant cursor-bin"
|
||||||
echo ""
|
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
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -76,11 +102,45 @@ while [[ $# -gt 0 ]]; do
|
|||||||
esac
|
esac
|
||||||
done
|
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
|
# Show target architecture and mirror after parsing args
|
||||||
print_info "Target architecture: $ARCH"
|
print_info "Target architecture: $ARCH"
|
||||||
print_info "Mirror: $MIRROR"
|
print_info "Mirror: $MIRROR"
|
||||||
print_info "Build workspace: $BUILD_OUTPUT_DIR"
|
print_info "Build workspace: $BUILD_OUTPUT_DIR"
|
||||||
print_info "Final output: $REPO_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
|
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 Docker or makepkg will run"
|
||||||
@@ -88,6 +148,7 @@ if [[ "$DRY_RUN" == true ]]; then
|
|||||||
MIRROR="$MIRROR" \
|
MIRROR="$MIRROR" \
|
||||||
PACKAGES="$PACKAGES" \
|
PACKAGES="$PACKAGES" \
|
||||||
DRY_RUN=true \
|
DRY_RUN=true \
|
||||||
|
DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS" \
|
||||||
PKGBUILDS_DIR="$PKGBUILDS_DIR" \
|
PKGBUILDS_DIR="$PKGBUILDS_DIR" \
|
||||||
BUILD_OUTPUT_DIR="$BUILD_OUTPUT_DIR" \
|
BUILD_OUTPUT_DIR="$BUILD_OUTPUT_DIR" \
|
||||||
FINAL_OUTPUT_DIR="$REPO_DIR" \
|
FINAL_OUTPUT_DIR="$REPO_DIR" \
|
||||||
@@ -112,9 +173,14 @@ if [[ "$(uname -m)" == "x86_64" && "$ARCH" == "aarch64" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean build-output directory to start fresh
|
# Clean build-output directory to start fresh, unless the caller seeded it
|
||||||
print_info "Cleaning build workspace..."
|
# with packages from an earlier job or is resuming an interrupted run.
|
||||||
rm -rf "$BUILD_OUTPUT_DIR"/*
|
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"
|
mkdir -p "$BUILD_OUTPUT_DIR"
|
||||||
|
|
||||||
# Show package info
|
# Show package info
|
||||||
@@ -124,8 +190,20 @@ else
|
|||||||
print_info "Building unscoped packages for $MIRROR mirror"
|
print_info "Building unscoped packages for $MIRROR mirror"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build/update the Docker image
|
# Build/update the Docker image, unless the caller prepared the exact image
|
||||||
build_docker_image "$BUILD_DIR" "$ARCH" "$MIRROR"
|
# 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..."
|
print_info "Running package build..."
|
||||||
|
|
||||||
@@ -144,6 +222,7 @@ DOCKER_ARGS=(
|
|||||||
-e MIRROR="$MIRROR"
|
-e MIRROR="$MIRROR"
|
||||||
-e PACKAGES="$PACKAGES"
|
-e PACKAGES="$PACKAGES"
|
||||||
-e OMARCHY_RC_PINS="${OMARCHY_RC_PINS:-}"
|
-e OMARCHY_RC_PINS="${OMARCHY_RC_PINS:-}"
|
||||||
|
-e DEFER_RUNTIME_DEPS="$DEFER_RUNTIME_DEPS"
|
||||||
-v "$BUILD_ROOT/build-output:/build-output"
|
-v "$BUILD_ROOT/build-output:/build-output"
|
||||||
-v "$REPO_ROOT:/pkgs.omarchy.org"
|
-v "$REPO_ROOT:/pkgs.omarchy.org"
|
||||||
-v "$BUILD_DIR:/build:ro"
|
-v "$BUILD_DIR:/build:ro"
|
||||||
@@ -152,7 +231,6 @@ DOCKER_ARGS=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Run the builder with assembled args
|
# Run the builder with assembled args
|
||||||
IMAGE_TAG="omarchy-pkg-builder:latest-$ARCH-$MIRROR"
|
|
||||||
PLATFORM_ARG=$(get_platform_arg "$ARCH")
|
PLATFORM_ARG=$(get_platform_arg "$ARCH")
|
||||||
|
|
||||||
docker run $PLATFORM_ARG "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
|
docker run $PLATFORM_ARG "${DOCKER_ARGS[@]}" "$IMAGE_TAG" /build/build.sh
|
||||||
|
|||||||
+76
-9
@@ -12,9 +12,37 @@ BUILD_OUTPUT_DIR=${BUILD_OUTPUT_DIR:-/build-output/$MIRROR/$ARCH}
|
|||||||
FINAL_OUTPUT_DIR=${FINAL_OUTPUT_DIR:-/pkgs.omarchy.org/$MIRROR/$ARCH}
|
FINAL_OUTPUT_DIR=${FINAL_OUTPUT_DIR:-/pkgs.omarchy.org/$MIRROR/$ARCH}
|
||||||
HELPERS_DIR=${HELPERS_DIR:-/helpers}
|
HELPERS_DIR=${HELPERS_DIR:-/helpers}
|
||||||
SRC_DIR=${SRC_DIR:-/src}
|
SRC_DIR=${SRC_DIR:-/src}
|
||||||
|
# Set by bin/build from OMARCHY_DEFER_RUNTIME_DEPS after it has checked the
|
||||||
|
# request; re-checked here so the container never trusts a stray value.
|
||||||
|
DEFER_RUNTIME_DEPS=${DEFER_RUNTIME_DEPS:-false}
|
||||||
|
|
||||||
source "$HELPERS_DIR/package-metadata.sh"
|
source "$HELPERS_DIR/package-metadata.sh"
|
||||||
|
|
||||||
|
if [[ $DEFER_RUNTIME_DEPS != "false" && $DEFER_RUNTIME_DEPS != "true" ]]; then
|
||||||
|
echo "DEFER_RUNTIME_DEPS must be true or false" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
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 ;;
|
||||||
|
*)
|
||||||
|
echo "Runtime dependency deferral only applies to the omarchy pair, not $package" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if (( deferred_runtime != 1 || deferred_settings != 1 || deferred_count != 2 )); then
|
||||||
|
echo "Runtime dependency deferral requires exactly the omarchy pair" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$DRY_RUN" != true ]]; then
|
if [[ "$DRY_RUN" != true ]]; then
|
||||||
# Import GPG keys
|
# Import GPG keys
|
||||||
/build/import-gpg-keys.sh || exit 1
|
/build/import-gpg-keys.sh || exit 1
|
||||||
@@ -48,13 +76,16 @@ EOF
|
|||||||
# Create an empty database
|
# Create an empty database
|
||||||
repo-add omarchy-build.db.tar.zst >/dev/null 2>&1
|
repo-add omarchy-build.db.tar.zst >/dev/null 2>&1
|
||||||
ln -sf omarchy-build.db.tar.zst omarchy-build.db
|
ln -sf omarchy-build.db.tar.zst omarchy-build.db
|
||||||
else
|
fi
|
||||||
# Database exists, check if we need to rebuild it from packages
|
# Fold any packages already in the workspace into the database, whether
|
||||||
if ls *.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | grep -q .; then
|
# they came with an existing database or were dropped in by an earlier
|
||||||
echo "==> Rebuilding build database from existing packages..."
|
# workflow job (OMARCHY_KEEP_BUILD_WORKSPACE). Without this a seeded
|
||||||
ls *.pkg.tar.* | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | xargs -r repo-add omarchy-build.db.tar.zst >/dev/null 2>&1
|
# workspace with no database would leave those packages invisible to
|
||||||
ln -sf omarchy-build.db.tar.zst omarchy-build.db
|
# dependency resolution.
|
||||||
fi
|
if ls *.pkg.tar.* 2>/dev/null | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | grep -q .; then
|
||||||
|
echo "==> Rebuilding build database from existing packages..."
|
||||||
|
ls *.pkg.tar.* | grep -v '\.sig$' | grep -v 'omarchy-build\.db' | xargs -r repo-add omarchy-build.db.tar.zst >/dev/null 2>&1
|
||||||
|
ln -sf omarchy-build.db.tar.zst omarchy-build.db
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add omarchy repo if it has a database (stable packages)
|
# Add omarchy repo if it has a database (stable packages)
|
||||||
@@ -224,6 +255,31 @@ refresh_vcs_pkgver_preserving_local_pkgrel() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# With runtime dependency checks deferred, makepkg runs --nodeps, so the
|
||||||
|
# build-time dependencies it would normally install with -s have to be
|
||||||
|
# installed explicitly: makedepends and checkdepends, including the
|
||||||
|
# architecture-suffixed variants for the current CARCH.
|
||||||
|
install_deferred_build_dependencies() {
|
||||||
|
local pkg="$1"
|
||||||
|
local -a build_deps=()
|
||||||
|
|
||||||
|
mapfile -t build_deps < <(
|
||||||
|
CARCH="$ARCH" bash -c '
|
||||||
|
source PKGBUILD
|
||||||
|
arch_makedepends="makedepends_${CARCH}[@]"
|
||||||
|
arch_checkdepends="checkdepends_${CARCH}[@]"
|
||||||
|
printf "%s\n" \
|
||||||
|
"${makedepends[@]}" "${!arch_makedepends}" \
|
||||||
|
"${checkdepends[@]}" "${!arch_checkdepends}"
|
||||||
|
' | awk 'NF && !seen[$0]++'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (( ${#build_deps[@]} )); then
|
||||||
|
echo " Installing build-only dependencies for $pkg..."
|
||||||
|
sudo pacman -S --needed --noconfirm -- "${build_deps[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Build a package
|
# Build a package
|
||||||
build_package() {
|
build_package() {
|
||||||
local pkg="$1"
|
local pkg="$1"
|
||||||
@@ -280,9 +336,20 @@ build_package() {
|
|||||||
# Build package without signing (signing is done separately)
|
# Build package without signing (signing is done separately)
|
||||||
# PACMAN override uses a wrapper that adds --ask 4 to auto-resolve conflicts
|
# PACMAN override uses a wrapper that adds --ask 4 to auto-resolve conflicts
|
||||||
# (e.g. rustup replacing rust) since --noconfirm defaults to 'N' on those prompts
|
# (e.g. rustup replacing rust) since --noconfirm defaults to 'N' on those prompts
|
||||||
MAKEPKG_FLAGS="-scf --noconfirm"
|
local -a makepkg_flags=(-scf --noconfirm)
|
||||||
|
if [[ $DEFER_RUNTIME_DEPS == "true" ]]; then
|
||||||
|
# The pair's runtime dependencies (each other, and packages other jobs
|
||||||
|
# of the same pipeline build) are not resolvable here; the assembled set
|
||||||
|
# is installed in one verified transaction downstream. Only the
|
||||||
|
# build-time dependencies are installed, then makepkg skips the check.
|
||||||
|
install_deferred_build_dependencies "$pkg" || {
|
||||||
|
FAILED_PACKAGES="$FAILED_PACKAGES $pkg"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
makepkg_flags=(-cf --noconfirm --nodeps)
|
||||||
|
fi
|
||||||
|
|
||||||
if PACMAN=/usr/local/bin/pacman-for-makepkg makepkg $MAKEPKG_FLAGS; then
|
if PACMAN=/usr/local/bin/pacman-for-makepkg makepkg "${makepkg_flags[@]}"; then
|
||||||
# Ensure output directory exists
|
# Ensure output directory exists
|
||||||
mkdir -p "$BUILD_OUTPUT_DIR"
|
mkdir -p "$BUILD_OUTPUT_DIR"
|
||||||
|
|
||||||
|
|||||||
@@ -61,9 +61,14 @@ get_platform_arg() {
|
|||||||
|
|
||||||
make_dir_writable() {
|
make_dir_writable() {
|
||||||
local dir="$1"
|
local dir="$1"
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
if (( EUID == 0 )); then
|
||||||
chmod -R 777 "$dir"
|
chmod -R 777 "$dir"
|
||||||
else
|
else
|
||||||
sudo chown -R $(id -u):$(id -g) "$dir" 2>/dev/null || chmod -R 777 "$dir"
|
# chown can succeed on part of the tree and fail on the rest (files a
|
||||||
|
# previous container left behind as another uid); the old `|| chmod`
|
||||||
|
# fallback only ran when chown failed outright, leaving those files
|
||||||
|
# unwritable. Always follow with chmod so the whole tree is usable.
|
||||||
|
sudo chown -R "$(id -u):$(id -g)" "$dir" 2>/dev/null || true
|
||||||
|
chmod -R 777 "$dir"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user