#!/usr/bin/env bash # The Python package's whole check, as CI runs it (.github/workflows/wai-quantum-py.yml) # and as a person runs it on a build host: # # bash wai/wai-quantum-py/ci/check.sh [--write-notebooks] # # 1. builds the command line and the job service from this checkout (the surfaces # the package is held to); # 2. clippy on the extension module, warnings as errors; # 3. builds the wheel with maturin (pinned), and installs it in a fresh venv with # the pinned, hashed test requirements (tests/requirements.lock); # 4. pytest, every test required (none may skip: WAI_QUANTUM_REQUIRE_ALL=1); # 5. runs the doc notebooks against a job service started here, each held to its # stored outputs (--write-notebooks stores them instead); # 6. installs the same wheel alone on Python 3.9 and runs tests/smoke.py. # # Needs cargo (the toolchain rust-toolchain.toml names), uv and node (or WAI_QUANTUM_NODE). Scratch goes # under $WORK (default: $RUNNER_TEMP/wai-quantum-py, or a new temporary directory); # cargo builds into $CARGO_TARGET_DIR (default: $WORK/target). set -euo pipefail PKG="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WAI="$(cd "$PKG/.." && pwd)" WRITE=0 [ "${1:-}" = "--write-notebooks" ] && WRITE=1 WORK="${WORK:-${RUNNER_TEMP:-$(mktemp -d)}/wai-quantum-py}" mkdir -p "$WORK" export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$WORK/target}" PY="${WAI_QUANTUM_PYTHON:-3.12}" MATURIN=1.15.0 step() { echo; echo "== $*"; } step "the command line and the job service, from this checkout" (cd "$WAI/wai-quantum-cli" && cargo build --locked --release) (cd "$WAI/wai-quantum-service" && cargo build --locked --release) CLI="$CARGO_TARGET_DIR/release/wai-quantum" SVC="$CARGO_TARGET_DIR/release/wai-quantum-service" "$CLI" --version step "clippy on the extension module" (cd "$PKG" && cargo clippy --locked --release --all-targets -- -D warnings) step "the wheel (maturin $MATURIN)" rm -rf "$WORK/tools" "$WORK/dist" "$WORK/test" "$WORK/py39" uv venv -q --python "$PY" "$WORK/tools" uv pip install -q --python "$WORK/tools/bin/python" "maturin==$MATURIN" (cd "$PKG" && "$WORK/tools/bin/maturin" build --release --locked -o "$WORK/dist") WHEEL="$(ls "$WORK"/dist/wai_quantum-*.whl)" echo "built $(basename "$WHEEL")" case "$(basename "$WHEEL")" in *-cp39-abi3-*) ;; *) echo "::error::the wheel is not abi3 from Python 3.9: $WHEEL"; exit 1 ;; esac step "a fresh venv: the wheel and the pinned test requirements" uv venv -q --python "$PY" "$WORK/test" uv pip install -q --python "$WORK/test/bin/python" --require-hashes -r "$PKG/tests/requirements.lock" uv pip install -q --python "$WORK/test/bin/python" --no-deps "$WHEEL" "$WORK/test/bin/python" -I -c 'import wai_quantum, wai_quantum.widgets as w; assert w.HAVE_WIDGETS; print("wai_quantum", wai_quantum.__version__, "from", wai_quantum.__file__)' step "node: the widgets' front ends parse" NODE="${WAI_QUANTUM_NODE:-$(command -v node || true)}" [ -n "$NODE" ] || { echo "::error::no node (or WAI_QUANTUM_NODE): the widgets' front ends are tested with it"; exit 1; } export WAI_QUANTUM_NODE="$NODE" "$NODE" --version for f in "$PKG"/python/wai_quantum/static/*.js "$PKG"/tests/front_ends.mjs; do "$NODE" --check "$f"; done step "pytest" export WAI_QUANTUM_CLI="$CLI" WAI_QUANTUM_SERVICE_BIN="$SVC" WAI_QUANTUM_REQUIRE_ALL=1 export IPYTHONDIR="$WORK/ipython" JUPYTER_PLATFORM_DIRS=1 (cd "$PKG" && "$WORK/test/bin/pytest" -q -p no:cacheprovider tests 2>&1 | tee "$WORK/pytest.log") grep -qE '^[0-9]+ passed' "$WORK/pytest.log" || { echo "::error::pytest did not report passes"; exit 1; } if grep -qE '[0-9]+ (failed|skipped|error)' "$WORK/pytest.log"; then echo "::error::a test failed, erred or skipped"; exit 1; fi step "the doc notebooks, against a job service started here" port=$(( 18300 + RANDOM % 500 )) cfg="$WORK/qsvc"; rm -rf "$cfg"; mkdir -p "$cfg" WAI_QSVC_KEY="$cfg/service.key" WAI_QSVC_STORE="$cfg/store" WAI_QSVC_ADDR="127.0.0.1:$port" WAI_QSVC_BUDGET_GB=4 WAI_QSVC_JOB_GB=2 WAI_QSVC_WORKERS=1 \ WAI_QSVC_MIN_FREE_PCT=0 WAI_QSVC_CLIENT_BURST=10000 "$SVC" serve > "$cfg/service.log" 2>&1 & pid=$! trap 'kill $pid 2>/dev/null || true' EXIT for _ in $(seq 1 100); do curl -fs -o /dev/null "http://127.0.0.1:$port/v1/health" && break; sleep 0.2; done nb=(getting-started bell-receipt equivalence noisy-run service-job) files=(); for n in "${nb[@]}"; do files+=("$PKG/notebooks/$n.ipynb"); done # (An empty array expands under set -u only this way in the bash macOS ships.) args=(); [ "$WRITE" = 1 ] && args=(--write) (cd "$PKG" && XDG_CONFIG_HOME="$WORK/nbconfig" WAI_QUANTUM_SERVICE="http://127.0.0.1:$port" "$WORK/test/bin/python" tests/run_notebooks.py ${args[@]+"${args[@]}"} "${files[@]}") kill $pid 2>/dev/null || true wait $pid 2>/dev/null || true trap - EXIT step "the same wheel alone on Python 3.9" uv venv -q --python 3.9 "$WORK/py39" uv pip install -q --python "$WORK/py39/bin/python" --no-deps "$WHEEL" (cd "$WORK" && "$WORK/py39/bin/python" -I "$PKG/tests/smoke.py") echo echo "wai-quantum-py: every check passed ($(grep -E '^[0-9]+ passed' "$WORK/pytest.log"))"