ci: add shared Arch build image/script for GTK4 product repos

breadpad's CI used to rebuild libadwaita from source in an uncached
Fedora container on every push and broke repeatedly on version drift.
The fix there was a pinned Arch container (current gtk4/libadwaita/
gtk4-layer-shell/graphene are prebuilt pacman packages, no source
build needed) — this centralizes that image/script here so every
GTK4 layer-shell product in the ecosystem can share it instead of
each repo carrying its own copy.

ci/build.sh takes a product repo root + cargo command, and reads an
optional ci/deps.txt from that repo for product-specific extra pacman
packages (EXTRA_PKGS build-arg) without forking the Containerfile.

Product repos should pin this to a commit sha, not track main — an
unrelated change here would otherwise silently affect every product's
next release build.
This commit is contained in:
Breadway 2026-08-04 18:06:20 +08:00
parent f86e299f4a
commit 3f5f241985
2 changed files with 84 additions and 0 deletions

30
ci/Containerfile Normal file
View file

@ -0,0 +1,30 @@
# Shared CI build environment for bread-ecosystem GTK4/libadwaita apps.
#
# Arch base: current gtk4/libadwaita/gtk4-layer-shell/graphene are all
# available as prebuilt pacman packages, so no from-source library builds
# are needed (unlike Fedora, where breadpad's CI used to rebuild libadwaita
# from source on every single run and broke repeatedly on version drift).
#
# Base image pinned by digest, package set frozen at build time: this image
# only changes when someone deliberately rebuilds it, not on every push.
# Product repos that depend on this file should pin it to a commit sha
# (see each product's ci/bread-ecosystem.rev), not track `main` — otherwise
# an unrelated change here silently breaks every product's next release.
#
# EXTRA_PKGS lets a product layer on extra pacman packages (see that
# product's ci/deps.txt) without forking this file.
FROM archlinux@sha256:fae033b815a16f930325c2697e620362be4d2e5d739a301b10ad1fc9c8643a06
ARG EXTRA_PKGS=""
RUN pacman -Syu --noconfirm --needed \
base-devel \
git \
pkgconf \
rust \
gtk4 \
libadwaita \
gtk4-layer-shell \
graphene \
${EXTRA_PKGS} \
&& pacman -Scc --noconfirm

54
ci/build.sh Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Shared CI build script for bread-ecosystem GTK4/libadwaita apps.
#
# Builds (or reuses, via docker's own layer cache) the pinned Arch image
# from ci/Containerfile, then runs the given cargo command inside it
# against a product repo checkout.
#
# Usage: ci/build.sh <product-repo-root> <cargo-command...>
# e.g. ci/build.sh /path/to/breadpad cargo build --release --locked
#
# If <product-repo-root>/ci/deps.txt exists (one pacman package per line,
# '#' comments and blank lines ignored), those packages are installed on
# top of the shared base image.
#
# Cargo's registry/git caches are shared across all products (same crates
# regardless of which app is building); CARGO_TARGET_DIR is cached
# per-product. Both persist in named docker volumes across runs.
set -euo pipefail
if [ $# -lt 2 ]; then
echo "usage: build.sh <product-repo-root> <cargo-command...>" >&2
exit 1
fi
REPO_ROOT="$(cd "$1" && pwd)"
shift
CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PRODUCT="$(basename "$REPO_ROOT")"
EXTRA_PKGS=""
if [ -f "${REPO_ROOT}/ci/deps.txt" ]; then
EXTRA_PKGS="$(grep -vE '^\s*(#|$)' "${REPO_ROOT}/ci/deps.txt" | tr '\n' ' ')"
fi
docker build \
--build-arg "EXTRA_PKGS=${EXTRA_PKGS}" \
-t "bread-ci:${PRODUCT}" \
-f "${CI_DIR}/Containerfile" "${CI_DIR}"
docker run --rm \
-v "${REPO_ROOT}:/workspace" \
-v "bread-ci-cargo-registry:/root/.cargo/registry" \
-v "bread-ci-cargo-git:/root/.cargo/git" \
-v "bread-ci-${PRODUCT}-target:/cargo-target" \
-w /workspace \
-e CARGO_TARGET_DIR=/cargo-target \
"bread-ci:${PRODUCT}" \
bash -c '
set -euo pipefail
"$@"
mkdir -p /workspace/target
cp -a /cargo-target/. /workspace/target/
' bash "$@"