211 lines
6.7 KiB
Bash
211 lines
6.7 KiB
Bash
#!/usr/bin/env bash
|
|
# yams/scripts/local-ci/matrix.sh
|
|
#
|
|
# Simulate the Ubuntu GitHub Actions job in a Docker container.
|
|
#
|
|
# What it does inside the container:
|
|
# 1) Configure, build, and test YAMS
|
|
# 2) Install into a local prefix (inside the mounted repo)
|
|
# 3) Configure, build, and test a minimal downstream consumer against find_package(Yams)
|
|
#
|
|
# Requirements on host:
|
|
# - Docker available on PATH
|
|
# - This repository checked out locally
|
|
#
|
|
# Example:
|
|
# bash scripts/local-ci/matrix.sh
|
|
# bash scripts/local-ci/matrix.sh --cmake-args "-DYAMS_ENABLE_COVERAGE=ON"
|
|
#
|
|
# Notes:
|
|
# - The container image is built from docker/local-ci/ubuntu.Dockerfile (Ubuntu 24.04)
|
|
# - macOS jobs cannot be containerized; this script targets Linux (Ubuntu) CI parity.
|
|
# - EXTRA CMake args are passed via strings and expanded with 'eval' inside the container.
|
|
# Only pass trusted content.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
# Defaults
|
|
IMAGE_DEFAULT="yams/ci-ubuntu:24.04"
|
|
DOCKERFILE_DEFAULT="${REPO_ROOT}/docker/local-ci/ubuntu.Dockerfile"
|
|
REPO_DIR_DEFAULT="${REPO_ROOT}"
|
|
GENERATOR_DEFAULT="Ninja"
|
|
BUILD_TYPE_DEFAULT="Release"
|
|
INSTALL_PREFIX_DEFAULT="/work/prefix"
|
|
JOBS_DEFAULT=""
|
|
# resolved inside container via nproc if empty
|
|
EXTRA_CMAKE_ARGS_DEFAULT=""
|
|
# e.g. '-DYAMS_USE_VCPKG=ON'
|
|
CONSUMER_CMAKE_ARGS_DEFAULT=""
|
|
# e.g. '-DCMAKE_VERBOSE_MAKEFILE=ON'
|
|
PULL_FLAG=""
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Options:
|
|
-r, --repo PATH Path to repository root (default: ${REPO_DIR_DEFAULT})
|
|
-i, --image NAME:TAG Docker image tag to use (default: ${IMAGE_DEFAULT})
|
|
-f, --dockerfile PATH Path to Dockerfile for local CI image (default: ${DOCKERFILE_DEFAULT})
|
|
--skip-build-image Do not build the local CI Docker image (use existing)
|
|
--pull Force pulling base layers when building the image
|
|
|
|
-g, --generator NAME CMake generator (default: ${GENERATOR_DEFAULT})
|
|
-b, --build-type TYPE CMake build type (default: ${BUILD_TYPE_DEFAULT})
|
|
-p, --prefix PATH Install prefix inside container (default: ${INSTALL_PREFIX_DEFAULT})
|
|
-j, --jobs N Parallel build jobs (default: autodetect in container)
|
|
--cmake-args "ARGS" Extra args for main project configure (quoted string)
|
|
--consumer-cmake-args "ARGS" Extra args for consumer configure (quoted string)
|
|
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
$(basename "$0")
|
|
$(basename "$0") --cmake-args "-DYAMS_BUILD_TESTS=ON"
|
|
$(basename "$0") -j 8 --consumer-cmake-args "-DCMAKE_VERBOSE_MAKEFILE=ON"
|
|
EOF
|
|
}
|
|
|
|
REPO_DIR="${REPO_DIR_DEFAULT}"
|
|
IMAGE="${IMAGE_DEFAULT}"
|
|
DOCKERFILE="${DOCKERFILE_DEFAULT}"
|
|
SKIP_BUILD="0"
|
|
GENERATOR="${GENERATOR_DEFAULT}"
|
|
BUILD_TYPE="${BUILD_TYPE_DEFAULT}"
|
|
INSTALL_PREFIX="${INSTALL_PREFIX_DEFAULT}"
|
|
JOBS="${JOBS_DEFAULT}"
|
|
EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS_DEFAULT}"
|
|
CONSUMER_CMAKE_ARGS="${CONSUMER_CMAKE_ARGS_DEFAULT}"
|
|
|
|
# Parse args
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-r|--repo)
|
|
REPO_DIR="${2:-}"; shift 2;;
|
|
-i|--image)
|
|
IMAGE="${2:-}"; shift 2;;
|
|
-f|--dockerfile)
|
|
DOCKERFILE="${2:-}"; shift 2;;
|
|
--skip-build-image)
|
|
SKIP_BUILD="1"; shift;;
|
|
--pull)
|
|
PULL_FLAG="--pull"; shift;;
|
|
-g|--generator)
|
|
GENERATOR="${2:-}"; shift 2;;
|
|
-b|--build-type)
|
|
BUILD_TYPE="${2:-}"; shift 2;;
|
|
-p|--prefix)
|
|
INSTALL_PREFIX="${2:-}"; shift 2;;
|
|
-j|--jobs)
|
|
JOBS="${2:-}"; shift 2;;
|
|
--cmake-args)
|
|
EXTRA_CMAKE_ARGS="${2:-}"; shift 2;;
|
|
--consumer-cmake-args)
|
|
CONSUMER_CMAKE_ARGS="${2:-}"; shift 2;;
|
|
-h|--help)
|
|
usage; exit 0;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
usage
|
|
exit 2;;
|
|
esac
|
|
done
|
|
|
|
# Basic checks
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "Error: Docker not found on PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "${REPO_DIR}" ]]; then
|
|
echo "Error: Repo directory not found: ${REPO_DIR}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${SKIP_BUILD}" != "1" ]]; then
|
|
if [[ ! -f "${DOCKERFILE}" ]]; then
|
|
echo "Error: Dockerfile not found: ${DOCKERFILE}" >&2
|
|
exit 1
|
|
fi
|
|
echo "==> Building local CI Docker image: ${IMAGE}"
|
|
docker build ${PULL_FLAG} -t "${IMAGE}" -f "${DOCKERFILE}" "${REPO_DIR}"
|
|
else
|
|
echo "==> Skipping image build. Using existing image: ${IMAGE}"
|
|
fi
|
|
|
|
# Resolve absolute repo path for mounting
|
|
REPO_ABS="$(cd "${REPO_DIR}" && pwd)"
|
|
|
|
echo "==> Running CI simulation in container"
|
|
echo " Repo: ${REPO_ABS}"
|
|
echo " Image: ${IMAGE}"
|
|
echo " Generator: ${GENERATOR}"
|
|
echo " Build type: ${BUILD_TYPE}"
|
|
echo " Install prefix: ${INSTALL_PREFIX}"
|
|
echo " Jobs: ${JOBS:-'(auto)'}"
|
|
echo " Extra CMake: ${EXTRA_CMAKE_ARGS:-'(none)'}"
|
|
echo " Consumer CMake: ${CONSUMER_CMAKE_ARGS:-'(none)'}"
|
|
|
|
# Run CI steps in container
|
|
# Pass configuration via environment variables to avoid quoting issues.
|
|
docker run --rm \
|
|
-v "${REPO_ABS}:/work:rw" \
|
|
-w /work \
|
|
-e GEN="${GENERATOR}" \
|
|
-e BUILD_TYPE="${BUILD_TYPE}" \
|
|
-e INSTALL_PREFIX="${INSTALL_PREFIX}" \
|
|
-e JOBS="${JOBS}" \
|
|
-e EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS}" \
|
|
-e CONSUMER_EXTRA_CMAKE_ARGS="${CONSUMER_CMAKE_ARGS}" \
|
|
"${IMAGE}" bash -lc '
|
|
set -euxo pipefail
|
|
|
|
# Determine jobs inside container if not provided
|
|
if [[ -z "${JOBS:-}" ]]; then
|
|
if command -v nproc >/dev/null 2>&1; then
|
|
JOBS="$(nproc)"
|
|
else
|
|
JOBS="4"
|
|
fi
|
|
fi
|
|
|
|
# 1) Clean previous artifacts
|
|
rm -rf build consumer_build "${INSTALL_PREFIX}" || true
|
|
mkdir -p "${INSTALL_PREFIX}"
|
|
|
|
# 2) Configure main project
|
|
# Use eval to allow EXTRA_CMAKE_ARGS to inject -D definitions and other flags.
|
|
eval cmake -S . -B build -G \"${GEN}\" \
|
|
-DCMAKE_BUILD_TYPE=\"${BUILD_TYPE}\" \
|
|
-DYAMS_BUILD_PROFILE=dev \
|
|
-DYAMS_BUILD_TESTS=ON \
|
|
-DCMAKE_INSTALL_PREFIX=\"${INSTALL_PREFIX}\" \
|
|
${EXTRA_CMAKE_ARGS}
|
|
|
|
# 3) Build & test main project
|
|
cmake --build build -j "${JOBS}"
|
|
ctest --test-dir build --output-on-failure
|
|
|
|
# 4) Install main project (headers + libs + package config)
|
|
cmake --install build
|
|
|
|
# 5) Configure consumer project against installed prefix (if present)
|
|
if [[ -d test/consumer ]]; then
|
|
eval cmake -S test/consumer -B consumer_build -G \"${GEN}\" \
|
|
-DCMAKE_PREFIX_PATH=\"${INSTALL_PREFIX}\" \
|
|
${CONSUMER_EXTRA_CMAKE_ARGS}
|
|
|
|
# 6) Build & test consumer
|
|
cmake --build consumer_build -j "${JOBS}"
|
|
ctest --test-dir consumer_build --output-on-failure
|
|
else
|
|
echo "Skipping downstream consumer: directory 'test/consumer' not found."
|
|
fi
|
|
|
|
echo "Container CI steps completed successfully."
|
|
'
|
|
|
|
echo "==> Local containerized CI completed successfully."
|