Skip to content

Instantly share code, notes, and snippets.

@CiprianSpiridon
Last active July 19, 2026 04:01
Show Gist options
  • Select an option

  • Save CiprianSpiridon/e4f9785e1ba69c296824b1fdd3c4f61e to your computer and use it in GitHub Desktop.

Select an option

Save CiprianSpiridon/e4f9785e1ba69c296824b1fdd3c4f61e to your computer and use it in GitHub Desktop.
#!/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