#!/usr/bin/env bash # fast-check: run only the minimal checks for the current jj change. # Run from anywhere inside a jj workspace of the repository. Never commits, never pushes. # See tools/README.md. set -uo pipefail S="${FAST_CHECK_SCRATCH:-${XDG_CACHE_HOME:-$HOME/.cache}/komorebi-fast-check}" HERE="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" FROM="" LIST=0 SELF="$(readlink -f "${BASH_SOURCE[0]}")" usage() { cat <<'USAGE' usage: fast-check [--list] [--range | --since REV] [--help] --list dry run: print the checks that would run --range diff trunk()..@ instead of just @ --since REV diff REV..@ (REV is a jj revset, e.g. wip-a3b) Env: FAST_CHECK_SCRATCH base dir for the persistent target dirs (default: ${XDG_CACHE_HOME:-~/.cache}/komorebi-fast-check) USAGE } # Evaluation-only nix check, attribute level: `nix flake show` (about 9 s, 0.4 GB) and the drvPath of one check # and one package of each platform (the Windows, Linux and macOS groups of the one root flake). `nix flake check # --no-build` forces every check of every system (up to 28 GB RSS for 5 minutes with --all-systems) and fails # without .git anyway (decision D9). Not caught here: an error inside a check's derivation body; the # pre-landing `nix build` of the check still catches that. nix_eval() { systemd-run --user --scope -q -p MemoryMax=6G nice -n 19 nix flake show >/dev/null || return 1 for a in checks.x86_64-linux.windows-clippy checks.x86_64-linux.linux-clippy checks.aarch64-darwin.macos-clippy \ packages.x86_64-linux.windows-komorebic packages.x86_64-linux.komorebic packages.aarch64-darwin.komorebic; do systemd-run --user --scope -q -p MemoryMax=6G nice -n 19 nix eval --raw ".#$a.drvPath" >/dev/null || return 1 done } while [ $# -gt 0 ]; do case "$1" in --list) LIST=1 ;; --range) FROM='trunk()' ;; --nix-eval) nix_eval; exit $? ;; --since) shift; FROM="${1:?--since needs a revset}" ;; -h | --help) usage; exit 0 ;; *) usage >&2; exit 2 ;; esac shift done ROOT="$(jj root 2>/dev/null)" || { echo "fast-check: not in a jj workspace" >&2; exit 2; } cd "$ROOT" || exit 2 if [ -n "$FROM" ]; then FILES="$(jj diff --name-only --from "$FROM" --to @)" || exit 2 else FILES="$(jj diff --name-only -r @)" || exit 2 fi want_kde=0 want_mac=0 want_win=0 want_native=0 want_nix=0 unclassified=() while IFS= read -r f; do [ -z "$f" ] && continue case "$f" in flake.nix | flake.lock | nix/* | *.nix) want_nix=1 ;; # the files the Rust sources include or read, before the docs are ignored docs/linux/komorebi.example.json | docs/linux/skhdrc.sample | schemas/linux/*) want_kde=1 ;; docs/macos/*.json | docs/macos/*.sample | docs/macos/cli/* | schemas/macos/*) want_mac=1 ;; docs/*.json | docs/*.sample | schemas/windows/*) want_win=1 ;; Cargo.toml | Cargo.lock | .cargo/* | rust-toolchain.toml | rustfmt.toml | deny.toml) want_win=1; want_kde=1; want_mac=1 ;; # the platform crates and the crates of one set, before the `crates/*` catch-all crates/komorebi-layouts/* | crates/komorebi-scrolling/*) want_native=1; want_win=1 ;; crates/komorebi-linux/* | crates/komorebi-hotkeys/* | scripts/linux/*) want_kde=1 ;; crates/komorebi-macos/*) want_mac=1 ;; crates/komorebi-windows/* | crates/komorebi-themes/* | crates/komorebi-e2e-agent/* | crates/komorebi-testwin/*) want_win=1 ;; komorebic/src/linux* | komorebic/tests/linux_* | crates/komorebi-client/src/linux.rs | crates/komorebi-client/tests/linux_* | komorebi/tests/linux_*) want_kde=1 ;; komorebic/src/macos* | crates/komorebi-client/src/macos.rs | komorebi-bar/src/macos*) want_mac=1 ;; crates/komorebi-client/*) want_win=1; want_kde=1; want_mac=1 ;; crates/*) want_native=1; want_kde=1; want_mac=1; want_win=1 ;; komorebi-bar/*) want_win=1; want_mac=1 ;; komorebi/* | komorebic/*) want_win=1; want_kde=1; want_mac=1 ;; *.md | docs/* | tools/* | LICENSE* | .github/* | *.png | *.jpg | *.gif) ;; komorebi-gui/* | komorebi-shortcuts/* | komorebic-no-console/* | e2e/*) want_win=1 ;; *) unclassified+=("$f") ;; esac done <<<"$FILES" NAMES=() DIRS=() CMDS=() add() { NAMES+=("$1"); DIRS+=("$2"); CMDS+=("$3"); } if [ "$want_native" = 1 ]; then add "native: shared crates tests" "." \ "CARGO_TARGET_DIR=$S/target-fast-native env -u CARGO_BUILD_TARGET nix shell nixpkgs#gcc -c cargo test --locked -p komorebi-layouts -p komorebi-scrolling" fi if [ "$want_win" = 1 ]; then # a bare cargo command builds default-members (the Windows crates) of the one workspace add "windows: clippy --all-targets -D warnings (dev shell, cross)" "." \ "CARGO_TARGET_DIR=$S/target-fast-win nix develop --command cargo clippy --locked --all-targets -- -D warnings" fi if [ "$want_kde" = 1 ]; then add "kde: cargo check + lib tests (komorebi-linux)" "." \ "export CARGO_TARGET_DIR=$S/kde-target COMMIT_HASH=\${COMMIT_HASH:-dev}; unset CARGO_BUILD_TARGET; nix shell nixpkgs#gcc -c bash -c 'cargo check --locked -p komorebi-linux && cargo check --locked -p komorebi && cargo test --locked -p komorebi-linux --lib && cargo test --locked -p komorebi-linux --test schemas -- --test-threads=1'" fi if [ "$want_mac" = 1 ]; then add "macos: type-check only (fake CC, cannot link)" "." \ "export CARGO_TARGET_DIR=$S/target-fast-mac COMMIT_HASH=\${COMMIT_HASH:-dev} CC_aarch64_apple_darwin=$HERE/cc-fake.sh AR_aarch64_apple_darwin=ar; PKG_CONFIG_PATH=\$(nix build --no-link --print-out-paths nixpkgs#openssl.dev)/lib/pkgconfig; export PKG_CONFIG_PATH; nix shell nixpkgs#gcc nixpkgs#pkg-config nixpkgs#openssl.dev -c cargo check --locked --target aarch64-apple-darwin -p komorebi -p komorebi-macos -p komorebic -p komorebi-client -p komorebi-bar --all-targets" fi if [ "$want_nix" = 1 ]; then add "nix: root flake evaluation only (flake show and the drvPath of one check and package per platform)" "." "$SELF --nix-eval" fi if [ "${#unclassified[@]}" -gt 0 ]; then echo "fast-check: unclassified (no check): ${unclassified[*]}" fi if [ "${#NAMES[@]}" -eq 0 ]; then echo "fast-check: nothing to run for this diff" exit 0 fi fail=0 for i in "${!NAMES[@]}"; do if [ "$LIST" = 1 ]; then echo "[list] ${NAMES[$i]} (cd ${DIRS[$i]}; ${CMDS[$i]})" continue fi echo "==> ${NAMES[$i]}" start=$SECONDS (cd "${DIRS[$i]}" && bash -c "${CMDS[$i]}") rc=$? el=$((SECONDS - start)) if [ $rc -eq 0 ]; then echo "<== PASS ${NAMES[$i]} (${el}s)"; else echo "<== FAIL ${NAMES[$i]} (${el}s, exit $rc)"; fail=1; fi done exit $fail