Last active
July 19, 2026 04:01
-
-
Save CiprianSpiridon/e4f9785e1ba69c296824b1fdd3c4f61e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Fast local build/test/lint loop for hgDB. | |
| # | |
| # Speed stack and why it matters: | |
| # | |
| # - This script scopes work to explicitly selected or changed crates. Use -p | |
| # before `--`; arguments after `--` are passed to Cargo/Nextest as filters. | |
| # - cargo-nextest is the normal test runner: process isolation and parallel | |
| # scheduling make focused suites fast and failures clear. Its repo config uses | |
| # fail-fast=false, retries=0, and a bounded slow timeout, so all real failures | |
| # remain visible and flaky tests are never rerun-until-green. | |
| # - Nextest does not run doctests. Use this script's `doctest` mode when public | |
| # API documentation changes. | |
| # - .cargo/config.toml sets `rustc-wrapper = "kache"`. Kache content-addresses | |
| # compiler outputs and reuses them across invocations and worktrees. | |
| # - .kache.toml sets `cache_executables = true`, so Kache stores expensive | |
| # Nextest binaries instead of passing them through for another multi-minute | |
| # relink. It also sets `local_max_size = "80GiB"`; the canonical store remains | |
| # ~/Library/Caches/kache. | |
| # | |
| # Preserve the established paths: normal artifacts stay in this checkout's | |
| # target/, Kache stays in ~/Library/Caches/kache, and Clippy alone uses | |
| # target/clippy. Never set KACHE_CONFIG, KACHE_CACHE_DIR, or CARGO_TARGET_DIR to | |
| # work around permissions; doing so creates a separate cold artifact graph. | |
| # | |
| # Focused examples: | |
| # scripts/dev-fast.sh test -p hgdb-planner -- geo | |
| # scripts/dev-fast.sh test-all -p hgdb-server -- --test phase3_e2e | |
| # scripts/dev-fast.sh lint -p hgdb-planner | |
| # Defaults to crates touched by this branch or working tree. Use --all when you | |
| # intentionally want the whole workspace. | |
| set -euo pipefail | |
| MODE="check" | |
| BASE_REF="main" | |
| ALL=0 | |
| LIB_ONLY=1 | |
| DRY_RUN=0 | |
| PACKAGES=() | |
| EXTRA_ARGS=() | |
| usage() { | |
| cat <<'USAGE' | |
| Usage: scripts/dev-fast.sh [mode] [options] [-- extra cargo/nextest args] | |
| Modes: | |
| check cargo check changed crates (default) | |
| build cargo build changed crates | |
| test run lib/unit tests for changed crates; uses nextest if installed | |
| test-all run all test targets for changed crates; uses nextest if installed | |
| doctest run cargo doctests for changed crates (nextest does not run these) | |
| lint clippy changed crates in target/clippy | |
| all check + test + lint on changed crates | |
| Options: | |
| --all operate on the whole workspace | |
| --base <ref> diff base for changed-crate detection (default: main) | |
| -p, --package <p> package name to operate on; may be repeated | |
| --dry-run print commands without running them | |
| -h, --help show this help | |
| Examples: | |
| scripts/dev-fast.sh | |
| scripts/dev-fast.sh test | |
| scripts/dev-fast.sh all | |
| scripts/dev-fast.sh lint --base origin/main | |
| scripts/dev-fast.sh test -p hgdb-storage -- --nocapture | |
| scripts/dev-fast.sh doctest --all | |
| USAGE | |
| } | |
| if [ "${1:-}" != "" ]; then | |
| case "$1" in | |
| check|build|test|test-all|doctest|lint|all) | |
| MODE="$1" | |
| shift | |
| ;; | |
| esac | |
| fi | |
| while [ "$#" -gt 0 ]; do | |
| case "$1" in | |
| --all) | |
| ALL=1 | |
| shift | |
| ;; | |
| --base) | |
| BASE_REF="${2:?--base requires a ref}" | |
| shift 2 | |
| ;; | |
| -p|--package) | |
| PACKAGES+=("${2:?--package requires a package name}") | |
| shift 2 | |
| ;; | |
| --dry-run) | |
| DRY_RUN=1 | |
| shift | |
| ;; | |
| --) | |
| shift | |
| EXTRA_ARGS+=("$@") | |
| break | |
| ;; | |
| -h|--help) | |
| usage | |
| exit 0 | |
| ;; | |
| *) | |
| echo "unknown argument: $1" >&2 | |
| usage >&2 | |
| exit 2 | |
| ;; | |
| esac | |
| done | |
| CRATE_FILTER=( | |
| 'crates/*/Cargo.toml' | |
| 'crates/*/build.rs' | |
| 'crates/*/src/**' | |
| 'crates/*/tests/**' | |
| 'crates/*/benches/**' | |
| 'crates/*/examples/**' | |
| ) | |
| ROOT_FILTER=( | |
| 'Cargo.toml' | |
| 'Cargo.lock' | |
| '.cargo/config.toml' | |
| '.config/nextest.toml' | |
| 'bacon.toml' | |
| 'scripts/dev-fast.sh' | |
| ) | |
| manifest_package_name() { | |
| local manifest="$1" | |
| awk ' | |
| /^\[package\]/ { in_package = 1; next } | |
| /^\[/ { in_package = 0 } | |
| in_package && $1 == "name" { | |
| name = $3 | |
| gsub(/"/, "", name) | |
| print name | |
| exit | |
| } | |
| ' "$manifest" | |
| } | |
| all_packages() { | |
| find crates -mindepth 2 -maxdepth 2 -name Cargo.toml -print \ | |
| | while IFS= read -r manifest; do | |
| manifest_package_name "$manifest" | |
| done \ | |
| | sort -u | |
| } | |
| changed_files() { | |
| local base | |
| base="$(git merge-base HEAD "$BASE_REF" 2>/dev/null || echo HEAD)" | |
| git diff --name-only "$base"...HEAD -- "${CRATE_FILTER[@]}" "${ROOT_FILTER[@]}" | |
| git diff --name-only --cached -- "${CRATE_FILTER[@]}" "${ROOT_FILTER[@]}" | |
| git diff --name-only -- "${CRATE_FILTER[@]}" "${ROOT_FILTER[@]}" | |
| git ls-files --others --exclude-standard -- "${CRATE_FILTER[@]}" "${ROOT_FILTER[@]}" | |
| } | |
| changed_packages() { | |
| local files | |
| files="$(changed_files | sort -u)" | |
| if [ -z "$files" ]; then | |
| return 0 | |
| fi | |
| if printf '%s\n' "$files" | grep -Eq '^(Cargo\.toml|Cargo\.lock|\.cargo/config\.toml|\.config/nextest\.toml|bacon\.toml|scripts/dev-fast\.sh)$'; then | |
| all_packages | |
| return 0 | |
| fi | |
| printf '%s\n' "$files" \ | |
| | awk -F/ '$1 == "crates" && NF >= 3 { print "crates/" $2 "/Cargo.toml" }' \ | |
| | sort -u \ | |
| | while IFS= read -r manifest; do | |
| [ -f "$manifest" ] && manifest_package_name "$manifest" | |
| done \ | |
| | sort -u | |
| } | |
| resolve_packages() { | |
| if [ "${#PACKAGES[@]}" -gt 0 ]; then | |
| printf '%s\n' "${PACKAGES[@]}" | |
| elif [ "$ALL" -eq 1 ]; then | |
| all_packages | |
| else | |
| changed_packages | |
| fi | |
| } | |
| RESOLVED_PACKAGES=() | |
| while IFS= read -r line; do | |
| [ -n "$line" ] && RESOLVED_PACKAGES+=("$line") | |
| done < <(resolve_packages) | |
| if [ "${#RESOLVED_PACKAGES[@]}" -eq 0 ]; then | |
| echo "No crate changes found vs ${BASE_REF}; use --all or -p <package> to force work." | |
| exit 0 | |
| fi | |
| run_cmd() { | |
| printf '+' | |
| printf ' %q' "$@" | |
| printf '\n' | |
| if [ "$DRY_RUN" -eq 0 ]; then | |
| "$@" | |
| fi | |
| } | |
| run_cargo_with_packages() { | |
| local subcommand="$1" | |
| shift | |
| local args=() | |
| local pkg | |
| for pkg in "${RESOLVED_PACKAGES[@]}"; do | |
| args+=("-p" "$pkg") | |
| done | |
| if [ "${#EXTRA_ARGS[@]}" -gt 0 ]; then | |
| run_cmd cargo "$subcommand" "${args[@]}" "$@" "${EXTRA_ARGS[@]}" | |
| else | |
| run_cmd cargo "$subcommand" "${args[@]}" "$@" | |
| fi | |
| } | |
| run_nextest_or_cargo_test() { | |
| local args=() | |
| local pkg | |
| for pkg in "${RESOLVED_PACKAGES[@]}"; do | |
| args+=("-p" "$pkg") | |
| done | |
| if [ "$LIB_ONLY" -eq 1 ]; then | |
| args+=("--lib") | |
| fi | |
| if cargo nextest --version >/dev/null 2>&1; then | |
| if [ "${#EXTRA_ARGS[@]}" -gt 0 ]; then | |
| run_cmd cargo nextest run "${args[@]}" "${EXTRA_ARGS[@]}" | |
| else | |
| run_cmd cargo nextest run "${args[@]}" | |
| fi | |
| else | |
| echo "cargo-nextest not installed; falling back to cargo test." >&2 | |
| if [ "${#EXTRA_ARGS[@]}" -gt 0 ]; then | |
| run_cmd cargo test "${args[@]}" --no-fail-fast "${EXTRA_ARGS[@]}" | |
| else | |
| run_cmd cargo test "${args[@]}" --no-fail-fast | |
| fi | |
| fi | |
| } | |
| echo "Mode: $MODE" | |
| echo "Packages:" | |
| printf ' + %s\n' "${RESOLVED_PACKAGES[@]}" | |
| case "$MODE" in | |
| check) | |
| run_cargo_with_packages check --all-targets | |
| ;; | |
| build) | |
| run_cargo_with_packages build | |
| ;; | |
| test) | |
| LIB_ONLY=1 | |
| run_nextest_or_cargo_test | |
| ;; | |
| test-all) | |
| LIB_ONLY=0 | |
| run_nextest_or_cargo_test | |
| ;; | |
| doctest) | |
| run_cargo_with_packages test --doc --no-fail-fast | |
| ;; | |
| lint) | |
| run_cargo_with_packages clippy --all-targets --no-deps --target-dir target/clippy -- -D warnings | |
| ;; | |
| all) | |
| run_cargo_with_packages check --all-targets | |
| LIB_ONLY=1 | |
| run_nextest_or_cargo_test | |
| run_cargo_with_packages clippy --all-targets --no-deps --target-dir target/clippy -- -D warnings | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment