yams/.build.yml
Trevon 08322369cb
Search/grep UX, runtime hardening, and CI stabilization (#39)
* test(storage): add crash-replay and handle-failure coverage for ReferenceCounter

Adds 6 test cases:
- Recovery after forced close preserves committed data
- Uncommitted transaction rolled back after forced close
- Corrupted database file rejected on open
- Consistent stats after interrupted commit recovery
- Multiple PENDING transactions survive force close
- WAL checkpoint survives forced close

* fix(ci): remove ccache — conflicts with OpenBLAS ASM as direct compiler

* fix(ci): use explicit CC/CXX paths to prevent Conan from using ccache directly

ccache was picked up by Conan/CMake as the compiler itself (/usr/bin/ccache)
rather than as a transparent wrapper, causing OpenBLAS ASM compilation to fail
with 'ccache: invalid option -- D'. Setting explicit compiler paths prevents this.

* fix(ci): add YAMS_SKIP_CCACHE to avoid OpenBLAS ASM ccache conflict

setup.sh wraps CC/CXX with ccache for faster incremental builds, but
this causes Conan/CMake to use ccache directly as the ASM compiler for
OpenBLAS, where it fails on -D flags. Add an opt-out gate in setup.sh
and set YAMS_SKIP_CCACHE=1 in .build.yml for sourcehut CI VMs.

* fix(ci): add libomp-dev, libfaiss-dev, libopenblas-dev for FAISS build

* fix(ci): limit meson compile to -j1 to prevent OOM on sourcehut VMs

* fix(build): restore hooks and shrink srht

Compile the storage library with YAMS_TESTING in test builds so
debug-only hooks link again.

Include the POSIX chmod header used by the storage utility
Catch2 coverage cases.

Prune SourceHut fallback stage archives to runtime files so
branch artifacts stay under the upload limit.

* refactor(storage): drop dead code, add coverage

* refactor(cli): polish grep/search graph UX

* fix(daemon): stabilize shutdown and CI paths

- force vector-backed tests to opt out of CI-wide disable flags
- reset reference prune statements between deletes
- make optional-plugin and POSIX-only test assumptions portable
- extract ServiceManager open/shutdown helpers for safer WAL recovery work

* test(daemon): add db vacuum coverage

* test(daemon): add WAL startup recovery

- cover stale WAL startup recovery in ServiceManager
- cover repeated restart cycles with seeded WAL sidecars
- assert daemon recovery reporting for corrupt data-dir DBs
- verify oversized metadata DBs shrink after WAL recovery shutdown

* refactor(grep): consolidate indexed content scan

* fix(grep): remove pre-scan candidate truncation

* refactor(runtime): split startup, harden storage

* feat(bench): add manifest retrieval corpus

* fix(bench): run fusion experiment on simeon

* fix(cli): sharpen search graph hints

* fix(build): refresh version metadata

* fix(ci): stabilize sync search and srht packaging

* fix(ci): unblock windows and shrink artifacts

* fix(ci): stabilize linux runtime checks

* fix(build): shrink srht manifest
2026-05-27 12:15:17 -04:00

383 lines
14 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 (setup.sh uses build/release for Release builds).
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
- libboost-all-dev
- liburing-dev # enables io_uring async path instead of fallback
- libomp-dev # OpenMP runtime for FAISS parallel compute
- libfaiss-dev # FAISS vector similarity library
- libopenblas-dev # optimized BLAS for FAISS/vector math
- libarchive-dev # enables archive extraction features
- libacl1-dev # required for libarchive ACL support
- 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
- xz-utils # for extracting Zig tarball
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})"
- install_zig: |
set -e
# Install Zig 0.15.2 stable for zyp PDF plugin
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ZIG_ARCH="x86_64" ;;
aarch64) ZIG_ARCH="aarch64" ;;
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
ZIG_VERSION="0.15.2"
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" -o /tmp/zig.tar.xz
sudo tar -xJf /tmp/zig.tar.xz -C /opt
sudo mv /opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION} /opt/zig
sudo ln -sf /opt/zig/zig /usr/local/bin/zig
rm /tmp/zig.tar.xz
zig version
echo "PATH=/opt/zig:${PATH}" >> ~/.buildenv
- 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 (default, overridden by setup.sh):"
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})"
# Use setup.sh with environment variables for consistent build configuration
export YAMS_COMPILER=gcc
export YAMS_CPPSTD=20
export YAMS_CONAN_HOST_PROFILE=./conan/profiles/host-linux-gcc
export YAMS_CONAN_ARCH=x86_64
# Avoid ccache environment wrapping in CI VMs — interferes with
# Conan/OpenBLAS ASM compilation (ccache doesn't handle -D flags)
export YAMS_SKIP_CCACHE=1
# Configure FAST_MODE via extra Meson flags
if [ "${FAST_MODE:-0}" = "1" ]; then
echo "FAST_MODE=1: build will skip ONNX and tests"
export YAMS_EXTRA_MESON_FLAGS="-Dyams-version=${CLEAN_VERSION_MESON} -Denable-onnx=disabled -Dplugin-onnx=false -Dbuild-tests=false"
else
export YAMS_EXTRA_MESON_FLAGS="-Dyams-version=${CLEAN_VERSION_MESON}"
fi
# Set install prefix to /usr for Linux packages (matches GitHub workflow)
export YAMS_INSTALL_PREFIX="/usr"
# Run setup.sh (Release build) - it now respects all YAMS_* environment variables
./setup.sh Release
# Build the project (single-job: prevents OOM on large files like mcp_server.cpp)
meson compile -C build/release -j1
# Install the built artifacts into build-local staging directory (BUILD_DIR/STAGE_DIR).
meson install -C build/release --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
if [ -d yams ] && { [ -f yams/meson.build ] || [ -f yams/CMakeLists.txt ]; }; then
REPO_DIR="yams"
else
REPO_DIR="."
fi
bash "${REPO_DIR}/scripts/srht-collect-artifacts.sh"
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"