|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) |
| 5 | +check_disk="${repo_root}/scripts/check-disk-space.sh" |
| 6 | +tmp=$(mktemp -d) |
| 7 | +trap 'rm -rf "$tmp"' EXIT |
| 8 | + |
| 9 | +mkdir -p "$tmp/bin" |
| 10 | +cat >"$tmp/bin/df" <<'MOCK' |
| 11 | +#!/usr/bin/env bash |
| 12 | +printf '%s\n' \ |
| 13 | + 'Filesystem 1024-blocks Used Available Capacity Mounted on' \ |
| 14 | + "/dev/mock ${MOCK_DISK_TOTAL_KIB} 1 ${MOCK_DISK_AVAILABLE_KIB} 1% /" |
| 15 | +MOCK |
| 16 | +chmod +x "$tmp/bin/df" |
| 17 | + |
| 18 | +run_check() { |
| 19 | + PATH="$tmp/bin:$PATH" \ |
| 20 | + BUZZ_DISK_BUILD_RESERVE_GIB=15 \ |
| 21 | + BUZZ_DISK_MIN_FREE_GIB=10 \ |
| 22 | + "$check_disk" "$@" |
| 23 | +} |
| 24 | + |
| 25 | +# A 15 GiB build reserve must still leave the configured 10 GiB minimum. |
| 26 | +MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 27 | +MOCK_DISK_AVAILABLE_KIB=$((25 * 1024 * 1024)) \ |
| 28 | + run_check >/dev/null |
| 29 | + |
| 30 | +if MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 31 | + MOCK_DISK_AVAILABLE_KIB=$((24 * 1024 * 1024)) \ |
| 32 | + run_check >"$tmp/low.out" 2>&1; then |
| 33 | + echo "disk preflight accepted insufficient post-build free space" >&2 |
| 34 | + exit 1 |
| 35 | +fi |
| 36 | +grep -Fq 'Disk preflight blocked' "$tmp/low.out" |
| 37 | +grep -Fq 'minimum is 10 GiB' "$tmp/low.out" |
| 38 | +grep -Fq 'BUZZ_SKIP_DISK_PREFLIGHT=1' "$tmp/low.out" |
| 39 | + |
| 40 | +if PATH="$tmp/bin:$PATH" \ |
| 41 | + MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 42 | + MOCK_DISK_AVAILABLE_KIB=$((40 * 1024 * 1024)) \ |
| 43 | + BUZZ_DISK_BUILD_RESERVE_GIB=15 \ |
| 44 | + BUZZ_DISK_MIN_FREE_GIB=invalid \ |
| 45 | + "$check_disk" >"$tmp/invalid.out" 2>&1; then |
| 46 | + echo "disk preflight accepted an invalid absolute minimum" >&2 |
| 47 | + exit 1 |
| 48 | +fi |
| 49 | +grep -Fq 'BUZZ_DISK_MIN_FREE_GIB must be a non-negative integer' \ |
| 50 | + "$tmp/invalid.out" |
| 51 | + |
| 52 | +# The escape hatch bypasses only this guard, leaving the other hooks intact. |
| 53 | +MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 54 | +MOCK_DISK_AVAILABLE_KIB=1 \ |
| 55 | +BUZZ_SKIP_DISK_PREFLIGHT=1 \ |
| 56 | + run_check >/dev/null |
| 57 | + |
| 58 | +# Verify the behavior through a real Git pre-push, not only config inspection. |
| 59 | +fixture="$tmp/hook-repo" |
| 60 | +remote="$tmp/remote.git" |
| 61 | +git init --bare -q "$remote" |
| 62 | +git init -q -b main "$fixture" |
| 63 | +git -C "$fixture" config user.name test |
| 64 | +git -C "$fixture" config user.email test@example.com |
| 65 | +git -C "$fixture" remote add origin "$remote" |
| 66 | +mkdir -p "$fixture/scripts" |
| 67 | +cp "$check_disk" "$fixture/scripts/check-disk-space.sh" |
| 68 | +cp "$repo_root/scripts/check-branch-skew.sh" "$fixture/scripts/check-branch-skew.sh" |
| 69 | +cp "$repo_root/lefthook.yml" "$fixture/lefthook.yml" |
| 70 | +git -C "$fixture" add . |
| 71 | +git -C "$fixture" -c core.hooksPath=/dev/null commit -qm baseline |
| 72 | +git -C "$fixture" -c core.hooksPath=/dev/null push -q -u origin main |
| 73 | +( |
| 74 | + # Use the Hermit stub rather than a bare `lefthook`: CI's `changes` job runs |
| 75 | + # this script without activating Hermit, so only the pinned repo binary is |
| 76 | + # guaranteed to resolve. |
| 77 | + cd "$fixture" |
| 78 | + "$repo_root/bin/lefthook" install --force >/dev/null |
| 79 | +) |
| 80 | + |
| 81 | +# Documentation-only pushes skip all guarded jobs. |
| 82 | +printf '%s\n' '# docs' >"$fixture/README.md" |
| 83 | +git -C "$fixture" add . |
| 84 | +git -C "$fixture" -c core.hooksPath=/dev/null commit -qm docs-change |
| 85 | +PATH="$tmp/bin:$PATH" \ |
| 86 | +MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 87 | +MOCK_DISK_AVAILABLE_KIB=1 \ |
| 88 | + git -C "$fixture" push -q |
| 89 | + |
| 90 | +mkdir -p "$fixture/crates/example/src" |
| 91 | +printf '%s\n' 'pub fn example() {}' >"$fixture/crates/example/src/lib.rs" |
| 92 | +git -C "$fixture" add . |
| 93 | +git -C "$fixture" -c core.hooksPath=/dev/null commit -qm heavy-change |
| 94 | +if PATH="$tmp/bin:$PATH" \ |
| 95 | + MOCK_DISK_TOTAL_KIB=$((100 * 1024 * 1024)) \ |
| 96 | + MOCK_DISK_AVAILABLE_KIB=$((24 * 1024 * 1024)) \ |
| 97 | + git -C "$fixture" push >"$tmp/hook.out" 2>&1; then |
| 98 | + echo "real pre-push hook bypassed the disk preflight" >&2 |
| 99 | + exit 1 |
| 100 | +fi |
| 101 | +if ! grep -Fq 'Disk preflight blocked' "$tmp/hook.out"; then |
| 102 | + echo "real pre-push failed for an unexpected reason:" >&2 |
| 103 | + cat "$tmp/hook.out" >&2 |
| 104 | + exit 1 |
| 105 | +fi |
| 106 | + |
| 107 | +# Unsupported df output fails open so the hook remains portable. |
| 108 | +cat >"$tmp/bin/df" <<'MOCK' |
| 109 | +#!/usr/bin/env bash |
| 110 | +echo unsupported |
| 111 | +MOCK |
| 112 | +chmod +x "$tmp/bin/df" |
| 113 | +run_check >"$tmp/unsupported.out" 2>&1 |
| 114 | +grep -Fq 'could not determine free space' "$tmp/unsupported.out" |
| 115 | + |
| 116 | +# One guarded pre-push job each for rust-tests, desktop-check, desktop-typecheck, |
| 117 | +# desktop-test, desktop-tauri-checks, and mobile-test. Bump when a build-heavy |
| 118 | +# job is added to lefthook.yml. |
| 119 | +[[ "$(grep -Fc 'run: ./scripts/check-disk-space.sh &&' "$repo_root/lefthook.yml")" -eq 6 ]] |
| 120 | +if grep -Fq 'setup:' "$repo_root/lefthook.yml"; then |
| 121 | + echo "disk preflight must not use Lefthook setup on pinned version 2.1.3" >&2 |
| 122 | + exit 1 |
| 123 | +fi |
| 124 | +grep -Fq 'scripts/test-check-disk-space.sh' \ |
| 125 | + "$repo_root/.github/workflows/ci.yml" |
| 126 | + |
| 127 | +echo "disk-space preflight tests passed" |
0 commit comments