Files
clawhdf5/scripts/ci-test.sh
T
osobhandClaude Opus 5 a29c1b224b test: let the interop suites find a Python that actually has h5py
Every Python interop suite had stopped running on this machine: the h5py
writer round-trips, the facade suite, netCDF4 and the reference files.
`python3` is 3.14, nothing on the box has h5py, and PEP 668 refuses to
install it into a system interpreter at all — so the availability probes
all returned false and each suite skipped without failing.

A silent skip here is exactly how the v5 compound-datatype bug reached a
release, so the probes now read `CLAWHDF5_PYTHON` and `ci-test.sh` picks
up `.venv/bin/python` on its own. The detection sits at the top of the
script rather than beside the interop step, because the non-ignored
suites run in the earlier `cargo test` step and would otherwise still
miss it. `CLAWHDF5_REQUIRE_INTEROP=1` continues to turn a skip into a
failure.

Verified against a venv with h5py 3.16 / HDF5 2.0.0: 94 interop tests
across the four suites, all passing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-19 20:44:54 -07:00

151 lines
4.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# CI test script — runs fmt, clippy (all targets + feature matrix), tests,
# Python interop suites, bench compilation, and no_std checks.
#
# Usage:
# ./scripts/ci-test.sh
#
# Environment:
# CLAWHDF5_REQUIRE_INTEROP=1 Fail (instead of skip) when python3 with
# h5py/netCDF4/xarray is missing. CI sets this.
# Unset locally, the interop steps are skipped
# if python3+h5py is not importable.
# CLAWHDF5_FUZZ_SECONDS=N Run each cargo-fuzz target for N seconds
# (needs nightly + cargo-fuzz). Default: skip.
#
# Exit codes:
# 0 — all checks passed
# 1 — one or more checks failed
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Interop suites drive a Python interpreter. On a PEP 668 "externally managed"
# system h5py can only live in a virtualenv, so pick one up here — before any
# test step, since the non-ignored interop suites read the same variable.
if [ -z "${CLAWHDF5_PYTHON:-}" ] && [ -x "$SCRIPT_DIR/../.venv/bin/python" ]; then
export CLAWHDF5_PYTHON="$SCRIPT_DIR/../.venv/bin/python"
fi
PASS=0
FAIL=0
STEPS=()
run_step() {
local name="$1"
shift
echo ""
echo "==> [$name]"
if "$@" 2>&1; then
echo " ✓ PASS: $name"
PASS=$((PASS + 1))
STEPS+=("PASS: $name")
else
echo " ✗ FAIL: $name"
FAIL=$((FAIL + 1))
STEPS+=("FAIL: $name")
fi
}
# All steps always run so one failure doesn't hide the others; the summary
# and the exit code at the end are the verdict.
# 1. Format check
run_step "cargo fmt --check" cargo fmt --check
# 2. Clippy over every target (lib, bins, tests, benches, examples). Without
# --all-targets, test and bench code is never linted. clawhdf5-py is
# excluded because it needs PyO3/Python headers.
run_step "cargo clippy --all-targets" cargo clippy \
--workspace \
--exclude clawhdf5-py \
--all-targets \
-- -D warnings
# 3. Clippy over clawhdf5-format's optional features, which the default
# workspace build never compiles (szip is left out: it needs libaec).
run_step "cargo clippy (format feature matrix)" cargo clippy \
-p clawhdf5-format \
--all-targets \
--features parallel,lz4,zstd,pcodec,fast-checksum \
-- -D warnings
# The HNSW index's parallel bulk build is feature-gated too.
run_step "cargo clippy (ann parallel)" cargo clippy \
-p clawhdf5-ann \
--all-targets \
--features parallel \
-- -D warnings
# 4. Tests (exclude clawhdf5-py)
run_step "cargo test" cargo test \
--workspace \
--exclude clawhdf5-py
run_step "cargo test (format feature matrix)" cargo test \
-p clawhdf5-format \
--features parallel,lz4,zstd,pcodec,fast-checksum
run_step "cargo test (ann parallel)" cargo test \
-p clawhdf5-ann \
--features parallel
# 5. Python interop suites. The h5py writer tests are #[ignore]d so a plain
# `cargo test` stays hermetic; run them explicitly here.
# On a PEP 668 "externally managed" system h5py can only live in a
# virtualenv, so honour CLAWHDF5_PYTHON (and a local .venv) rather than
# skipping — the tests read the same variable.
PYTHON="${CLAWHDF5_PYTHON:-python3}"
if "$PYTHON" -c "import h5py" >/dev/null 2>&1 || [ "${CLAWHDF5_REQUIRE_INTEROP:-0}" = "1" ]; then
run_step "h5py interop (format, ignored tests)" cargo test \
-p clawhdf5-format --test writer_h5py_tests -- --include-ignored
else
echo ""
echo "==> [h5py interop] SKIPPED: no h5py in $PYTHON"
echo " (set CLAWHDF5_PYTHON=/path/to/venv/bin/python, or create .venv;"
echo " CLAWHDF5_REQUIRE_INTEROP=1 makes this a failure instead)"
STEPS+=("SKIP: h5py interop (format, ignored tests)")
fi
# 6. Benches must keep compiling (they are not run).
run_step "cargo bench --no-run" cargo bench \
--workspace \
--exclude clawhdf5-py \
--no-run
# 7. no_std check
run_step "check-nostd.sh" "$SCRIPT_DIR/check-nostd.sh"
# 8. Optional fuzz smoke run
if [ -n "${CLAWHDF5_FUZZ_SECONDS:-}" ]; then
fuzz_smoke() {
local crate target
for crate in clawhdf5-format clawhdf5-agent; do
cd "$SCRIPT_DIR/../crates/$crate" || return 1
for target in $(cargo +nightly fuzz list); do
echo "--- fuzz: $crate/$target"
cargo +nightly fuzz run "$target" -- \
-max_total_time="$CLAWHDF5_FUZZ_SECONDS" || return 1
done
done
}
run_step "fuzz smoke (${CLAWHDF5_FUZZ_SECONDS}s/target)" fuzz_smoke
fi
# Summary
echo ""
echo "========================================"
echo " CI Summary"
echo "========================================"
for s in "${STEPS[@]}"; do
echo " $s"
done
echo "----------------------------------------"
echo " $PASS passed, $FAIL failed"
echo "========================================"
if [ "$FAIL" -gt 0 ]; then
exit 1
fi
exit 0