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]>
151 lines
4.9 KiB
Bash
Executable File
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
|