yams/.build.yml

428 lines
16 KiB
YAML

# .build.yml
#
# SourceHut build manifest for YAMS (single Linux target).
# Key points:
# - Compute version from Git ref (GIT_REF) or release channel (RELEASE_CHANNEL) at runtime; no manifest expressions.
# - Persist computed version into ~/.buildenv for subsequent tasks.
# - Add static build tags for search/badges.
# - Gate packaging steps so they run for tag builds and for nightly/weekly channels.
# - Install Conan via pipx (or venv fallback), since Debian doesn't provide python3-conan.
image: debian/trixie
secrets:
- 46f739e5-4538-45dd-a79f-bf173b7a2ed9
sources:
- https://git.sr.ht/~trevon/yams
# Static metadata for search and badges (unrelated to Git tags)
tags:
- linux
- release
- cmake
- conan
- yams
- nightly
- weekly
environment:
# Define consistent directories.
BUILD_DIR: build/release
STAGE_DIR: stage # relative to BUILD_DIR (Meson --destdir uses builddir as base)
# Optional: set this to "nightly" or "weekly" via scheduler/env to produce
# date/week-based releases on non-tag builds.
# RELEASE_CHANNEL:
packages:
# Build dependencies (Debian/apt)
- build-essential # gcc, make, etc.
- cmake
- meson
- ninja-build
- zip
- ccache
- dpkg-dev
- fakeroot
- rpm
- lld
- clang
- llvm
- python3
- python3-pip
- python3-venv
- pipx
- git
- libncurses-dev
- libtinfo-dev
- pkg-config
- liburing-dev # enables io_uring async path instead of fallback
- libarchive-dev # enables archive extraction features
- libtag1-dev # enables taglib (audio metadata) detection
- libsqlite3-dev # enables SQLite database support
- libssl-dev
- libcurl4-gnutls-dev # use gnutls variant; required by libmediainfo-dev (conflicts with openssl variant)
- libsqlite3-dev
- libncurses-dev
- protobuf-compiler
- libprotobuf-dev
- libmediainfo-dev # enables MediaInfo-based video metadata extraction
- mediainfo # MediaInfo CLI tool used at runtime for JSON output
tasks:
# Determine version from the Git ref and persist to ~/.buildenv for later tasks.
- compute_version: |
set -e
# Detect repository root (sr.ht often checks out into ./yams). Support Meson or CMake projects.
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
CHANNEL="${RELEASE_CHANNEL:-}"
# Auto-channelize main branch builds as nightly when not tagged
if [[ -z "${CHANNEL}" && "${GIT_REF:-}" == "refs/heads/main" ]]; then
CHANNEL="nightly"
export RELEASE_CHANNEL="nightly"
# Persist for subsequent tasks which may source this
echo "RELEASE_CHANNEL=nightly" >> ~/.buildenv
fi
# Determine numeric base version from latest tag (strip leading v), fallback to 0.0.0
RAW_TAG_NUMERIC="$(git describe --tags --abbrev=0 2>/dev/null || echo 0.0.0)"
BASE_NUMERIC_VERSION="${RAW_TAG_NUMERIC#v}"
if [ "$CHANNEL" = "nightly" ]; then
CLEAN_VERSION_DISPLAY="nightly-$(date -u +%Y%m%d)"
CLEAN_VERSION_CMAKE="${BASE_NUMERIC_VERSION}"
elif [ "$CHANNEL" = "weekly" ]; then
CLEAN_VERSION_DISPLAY="weekly-$(date -u +%G)w$(date -u +%V)"
CLEAN_VERSION_CMAKE="${BASE_NUMERIC_VERSION}"
else
# Prefer tag-triggered builds; otherwise use last tag as numeric
if [[ "${GIT_REF:-}" =~ ^refs/tags/ ]]; then
RAW_TAG="${GIT_REF#refs/tags/}"
CLEAN_VERSION_DISPLAY="${RAW_TAG#v}"
CLEAN_VERSION_CMAKE="${RAW_TAG#v}"
else
CLEAN_VERSION_DISPLAY="${BASE_NUMERIC_VERSION}"
CLEAN_VERSION_CMAKE="${BASE_NUMERIC_VERSION}"
fi
fi
# Persist for subsequent tasks
echo "YAMS_VERSION=${CLEAN_VERSION_DISPLAY}" >> ~/.buildenv
echo "YAMS_VERSION_CMAKE=${CLEAN_VERSION_CMAKE}" >> ~/.buildenv
echo "Computed YAMS_VERSION(display)=${CLEAN_VERSION_DISPLAY}, YAMS_VERSION_CMAKE=${CLEAN_VERSION_CMAKE} (GIT_REF=${GIT_REF:-n/a}, RELEASE_CHANNEL=${CHANNEL:-n/a})"
- setup: |
set -e
# Ensure pipx/venv/pip path is prepared
export PATH="${HOME}/.local/bin:${PATH}"
# Configure git identity and signing key (requested)
git config --global user.name "Trevon" || true
git config --global user.email "git@trevon.dev" || true
git config --global user.signingkey "C1E8284DAE319BA313FCB40038686EA797D7A8A7" || true
# Note: actual signed commits/tags require the private key in the agent; we only set identity here.
# Configure ccache (optional acceleration)
export CCACHE_DIR="${HOME}/.cache/ccache"
mkdir -p "${CCACHE_DIR}"
echo "CCACHE_DIR=${CCACHE_DIR}" >> ~/.buildenv
if command -v ccache >/dev/null 2>&1; then
ccache --set-config=max_size=2G || true
ccache --zero-stats || true
ccache -s || true
fi
# Install Conan via pipx when available; otherwise fallback to user pip or an isolated venv
if ! command -v conan >/dev/null 2>&1; then
if command -v pipx >/dev/null 2>&1; then
pipx install 'conan<3' --pip-args='--upgrade' || true
fi
fi
if ! command -v conan >/dev/null 2>&1; then
python3 -m pip install --user --upgrade 'conan<3' || true
hash -r
fi
if ! command -v conan >/dev/null 2>&1; then
python3 -m venv /opt/conan
. /opt/conan/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install 'conan<3'
export PATH="/opt/conan/bin:${PATH}"
hash -r
fi
# Persist Conan PATH and CPU count for subsequent tasks and fail fast if missing
if command -v conan >/dev/null 2>&1; then
CONAN_DIR="$(dirname "$(command -v conan)")"
echo "PATH=${CONAN_DIR}:\$PATH" >> ~/.buildenv
else
echo "Conan is not available after installation attempts" >&2
exit 1
fi
CONAN_CPU_COUNT="${CONAN_CPU_COUNT:-$(nproc || getconf _NPROCESSORS_ONLN || echo 2)}"
echo "CONAN_CPU_COUNT=${CONAN_CPU_COUNT}" >> ~/.buildenv
# Detect and move into repository root
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
# Configure Conan (use explicit GCC host/build profiles for Debian CI)
conan --version
conan profile detect --force || true
echo "Conan profile configured (clang):"
conan profile show -pr default || conan profile show || true
- yams_semver_guard: |
set -e
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
if [ -f scripts/ci/check_yams_semver.py ]; then
echo "Running YAMS semver guard..."
python3 scripts/ci/check_yams_semver.py
else
echo "No semver guard script found; skipping."
fi
- build: |
set -e
export PATH="${HOME}/.local/bin:/opt/conan/bin:${PATH}"
if ! command -v conan >/dev/null 2>&1; then
echo "conan not found in PATH; aborting build" >&2
exit 1
fi
if [ -d yams ] && [ -f yams/meson.build ]; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
export YAMS_DISABLE_MODEL_PRELOAD=1
CLEAN_VERSION_MESON="${YAMS_VERSION}"
echo "Starting build (Meson version ${CLEAN_VERSION_MESON})"
# Fetch dependencies and generate Meson toolchain/deps.
conan install . \
-of "${BUILD_DIR}" \
-pr:h=./conan/profiles/host-linux-gcc \
-pr:b=./conan/profiles/host-linux-gcc \
-s build_type=Release \
-c tools.build:jobs="${CONAN_CPU_COUNT}" \
--build=missing
# Configure with Meson.
# NOTE: Do not pass -Dversion here; no custom Meson option is defined. Runtime/display versioning
# is handled separately and project() version remains the static value in meson.build until
# a dedicated yams-version option is introduced.
# Conan 2 generates the Meson native file under: <build dir>/build-<buildtype-lowercase>/conan/
MESON_EXTRA_ARGS=""
if [ "${FAST_MODE:-0}" = "1" ]; then
echo "FAST_MODE=1: disabling ONNX and tests for quicker build"
MESON_EXTRA_ARGS="${MESON_EXTRA_ARGS} -Denable-onnx=disabled -Dplugin-onnx=false -Dbuild-tests=false"
fi
# Find the Meson toolchain file robustly.
TOOLCHAIN_FILE=""
# Check the path confirmed from developer docs.
if [ -f "${BUILD_DIR}/build-release/conan/conan_meson_native.ini" ]; then
TOOLCHAIN_FILE="${BUILD_DIR}/build-release/conan/conan_meson_native.ini"
fi
# If not found in the primary location, search for it.
if [ -z "$TOOLCHAIN_FILE" ]; then
TOOLCHAIN_FILE=$(find "${BUILD_DIR}" -name "conan_meson_native.ini" -print -quit)
fi
if [ -z "$TOOLCHAIN_FILE" ] || [ ! -f "$TOOLCHAIN_FILE" ]; then
echo "ERROR: Could not find conan_meson_native.ini in ${BUILD_DIR}" >&2
ls -R "${BUILD_DIR}" >&2
exit 1
fi
echo "Found Meson toolchain file: ${TOOLCHAIN_FILE}"
meson setup "${BUILD_DIR}" \
--native-file "${TOOLCHAIN_FILE}" \
${MESON_EXTRA_ARGS}
# Build the project.
meson compile -C "${BUILD_DIR}"
# Install the built artifacts into build-local staging directory (BUILD_DIR/STAGE_DIR).
meson install -C "${BUILD_DIR}" --destdir "${STAGE_DIR}"
# Show ccache stats after build (if available)
if command -v ccache >/dev/null 2>&1; then
ccache -s || true
fi
- test: |
set -e
# Run test suite; do not fail the build solely due to test failures so we can publish logs
if [ -d yams ] && [ -f yams/meson.build ]; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
mkdir -p artifacts || true
if [ -d "${BUILD_DIR}" ]; then
echo "Running meson tests in ${BUILD_DIR}..."
meson test -C "${BUILD_DIR}" --print-errorlogs || true
# Collect logs and common debug info
if [ -d "${BUILD_DIR}/meson-logs" ]; then
tar -C "${BUILD_DIR}" -czf artifacts/meson-logs.tar.gz meson-logs || true
cp -vf "${BUILD_DIR}/meson-logs/testlog.txt" artifacts/testlog.txt 2>/dev/null || true
cp -vf "${BUILD_DIR}/meson-logs/meson-log.txt" artifacts/meson-log.txt 2>/dev/null || true
fi
# Export compile_commands if generated
if [ -f "${BUILD_DIR}/compile_commands.json" ]; then
cp -vf "${BUILD_DIR}/compile_commands.json" artifacts/compile_commands.json || true
fi
else
echo "BUILD_DIR ${BUILD_DIR} missing; skipping meson tests" | tee artifacts/test-notes.txt
fi
- package: |
set -e
# Package on tag builds or when explicitly channelized as nightly/weekly
# Auto-channelize main branch builds as nightly when not tagged
if [[ -z "${RELEASE_CHANNEL:-}" && "${GIT_REF:-}" == "refs/heads/main" ]]; then
export RELEASE_CHANNEL=nightly
fi
if [[ "${GIT_REF:-}" != refs/tags/* && "${RELEASE_CHANNEL:-}" != "nightly" && "${RELEASE_CHANNEL:-}" != "weekly" ]]; then
echo "Skipping packaging (not a tag build and no release channel): GIT_REF=${GIT_REF:-n/a} RELEASE_CHANNEL=${RELEASE_CHANNEL:-n/a}"
exit 0
fi
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
CLEAN_VERSION="${YAMS_VERSION}"
echo "Packaging the release..."
# Package from the staged install under BUILD_DIR/STAGE_DIR
if [ ! -d "${BUILD_DIR}/${STAGE_DIR}" ]; then
echo "ERROR: Stage directory ${BUILD_DIR}/${STAGE_DIR} not found" >&2
ls -al "${BUILD_DIR}" || true
exit 1
fi
cd "${BUILD_DIR}/${STAGE_DIR}"
tar -czf "../yams-${CLEAN_VERSION}-linux-x86_64.tar.gz" .
echo "Created asset: yams-${CLEAN_VERSION}-linux-x86_64.tar.gz"
# Also publish a raw stage archive for debugging/inspection
tar -czf "../yams-${CLEAN_VERSION}-stage.tar.gz" .
if command -v zip >/dev/null 2>&1; then
# Only attempt zip if there are regular files to include; otherwise zip exits with "Nothing to do!"
if find . -type f -print -quit | grep -q .; then
zip -qry "../yams-${CLEAN_VERSION}-stage.zip" . || echo "zip failed; continuing (tarball already created)"
else
echo "Stage contains no regular files; skipping zip archive"
fi
fi
- package_meson: |
set -e
# Package on tag builds or when explicitly channelized as nightly/weekly
if [[ "${GIT_REF:-}" != refs/tags/* && "${RELEASE_CHANNEL:-}" != "nightly" && "${RELEASE_CHANNEL:-}" != "weekly" ]]; then
echo "Skipping packaging (not a tag build and no release channel): GIT_REF=${GIT_REF:-n/a} RELEASE_CHANNEL=${RELEASE_CHANNEL:-n/a}"
exit 0
fi
if [ -d yams ] && [ -f yams/meson.build ]; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
cd "${REPO_DIR}"
CLEAN_VERSION="${YAMS_VERSION}"
echo "Packaging with Meson..."
if [ -x scripts/build-deb.sh ]; then
scripts/build-deb.sh "${CLEAN_VERSION}" "${BUILD_DIR}"
else
echo "scripts/build-deb.sh not found; skipping distro package step"
fi
- collect_artifacts: |
set -e
ART_DIR="artifacts"
mkdir -p "${ART_DIR}"
rm -f "${ART_DIR}/yams"*.tar.gz "${ART_DIR}/yams"*.deb "${ART_DIR}/yams"*.rpm 2>/dev/null || true
# Determine repository root
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
# Publish a minimal, size-safe artifact set so SourceHut can retain them reliably.
# Policy:
# - Tag builds: prefer distro packages (.deb, .rpm) if available; fall back to the tarball.
# - Nightly/weekly: publish only the primary tarball (avoid duplicates and stage archives).
shopt -s nullglob || true
if [[ "${GIT_REF:-}" == refs/tags/* ]]; then
# Prefer distro packages on tagged releases
if compgen -G "${REPO_DIR}/${BUILD_DIR}/*.deb" > /dev/null; then
for f in "${REPO_DIR}/${BUILD_DIR}"/*.deb; do cp -v "$f" "${ART_DIR}/"; done
fi
if compgen -G "${REPO_DIR}/${BUILD_DIR}/*.rpm" > /dev/null; then
for f in "${REPO_DIR}/${BUILD_DIR}"/*.rpm; do cp -v "$f" "${ART_DIR}/"; done
fi
# Fallback to tarball when packages are missing
if ! compgen -G "${ART_DIR}/*" > /dev/null; then
for f in "${REPO_DIR}/${BUILD_DIR}"/yams-*-linux-x86_64.tar.gz; do cp -v "$f" "${ART_DIR}/"; done
fi
else
# Nightly/weekly: publish only the primary tarball
for f in "${REPO_DIR}/${BUILD_DIR}"/yams-*-linux-x86_64.tar.gz; do cp -v "$f" "${ART_DIR}/"; done
fi
# Provide stable filenames expected by SourceHut (no globbing in artifacts list)
if compgen -G "${ART_DIR}/*.tar.gz" > /dev/null; then
TARBALL=$(ls -1 "${ART_DIR}"/*.tar.gz | head -1)
cp -v "$TARBALL" "${ART_DIR}/yams.tar.gz"
fi
if compgen -G "${ART_DIR}/*.deb" > /dev/null; then
DEB=$(ls -1 "${ART_DIR}"/*.deb | head -1)
cp -v "$DEB" "${ART_DIR}/yams.deb"
fi
if compgen -G "${ART_DIR}/*.rpm" > /dev/null; then
RPM=$(ls -1 "${ART_DIR}"/*.rpm | head -1)
cp -v "$RPM" "${ART_DIR}/yams.rpm"
fi
echo "Collected artifacts:" && ls -l "${ART_DIR}" || true
# Fallback for CI branches: if no artifacts were collected (packaging gated),
# compress the staged install to provide a basic artifact for inspection.
if [ "$(find "${ART_DIR}" -type f | wc -l | tr -d ' ')" -eq 0 ]; then
if [ -d "${REPO_DIR}/${BUILD_DIR}/${STAGE_DIR}" ]; then
echo "No release artifacts; creating CI tarball from ${REPO_DIR}/${BUILD_DIR}/${STAGE_DIR}" >&2
tar -C "${REPO_DIR}/${BUILD_DIR}/${STAGE_DIR}" -czf "${ART_DIR}/yams.tar.gz" .
ls -l "${ART_DIR}" || true
else
echo "WARNING: Stage dir ${REPO_DIR}/${BUILD_DIR}/${STAGE_DIR} missing; no artifacts to publish" >&2
fi
fi
echo "collect_artifacts completed successfully"
exit 0
artifacts:
# SourceHut interprets artifact names literally (no shell globs). Provide
# stable filenames in collect_artifacts.
- "artifacts/yams.tar.gz"
- "artifacts/yams.deb"
- "artifacts/yams.rpm"
- "artifacts/meson-logs.tar.gz"
- "artifacts/testlog.txt"
- "artifacts/meson-log.txt"
- "artifacts/compile_commands.json"