diff --git a/docs/install.md b/docs/install.md index aac6c95e69fe..6a7494797d8c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -32,6 +32,9 @@ cargo install --locked cargo-nextest # Install a local codex binary with reuse of previous build artifacts. ./install.sh +# If your main volume is tight on space, keep Cargo build artifacts on another disk. +CARGO_TARGET_DIR=/path/on/larger/disk/codex-target ./install.sh + # Or build Codex manually from the Rust workspace. cd codex-rs cargo build diff --git a/install.sh b/install.sh index 435295102a4a..b0581ac3f2b5 100755 --- a/install.sh +++ b/install.sh @@ -18,6 +18,7 @@ Environment knobs: CODEX_SOURCE_BIN Prebuilt binary path to install (skips build unless CODEX_REBUILD=always) CODEX_INSTALL_LOCK_DIR Lock dir to prevent concurrent installers CARGO_TARGET_DIR Build cache/output directory (default: codex-rs/target) + Set this to a path on another disk when local space is tight. EOF } @@ -68,6 +69,23 @@ case "$target_dir" in esac build_stamp_file="$target_dir/release/.codex-install-build-stamp" +existing_dir_for_path() { + path=$1 + while [ ! -e "$path" ]; do + parent=$(dirname -- "$path") + if [ "$parent" = "$path" ]; then + break + fi + path=$parent + done + + if [ -d "$path" ]; then + printf '%s\n' "$path" + else + dirname -- "$path" + fi +} + collect_source_files() { find "$ROOT_DIR/codex-rs" \ -path "$ROOT_DIR/codex-rs/target" -prune -o \ @@ -271,16 +289,21 @@ fi if [ "$should_build" -eq 1 ]; then required_kb=${CODEX_MIN_FREE_KB:-3145728} - available_kb=$(df -Pk "$ROOT_DIR" | awk 'NR==2 {print $4}') + space_check_dir=$(existing_dir_for_path "$target_dir") + available_kb=$(df -Pk "$space_check_dir" | awk 'NR==2 {print $4}') if [ "$available_kb" -lt "$required_kb" ]; then - echo "Low free disk space detected (${available_kb} KB). Running cargo clean..." >&2 - cargo clean --manifest-path "$ROOT_DIR/codex-rs/Cargo.toml" >/dev/null 2>&1 || true - available_kb=$(df -Pk "$ROOT_DIR" | awk 'NR==2 {print $4}') + echo "Low free disk space detected for build target (${available_kb} KB at $space_check_dir)." >&2 + echo "Running cargo clean for CARGO_TARGET_DIR=$target_dir..." >&2 + CARGO_TARGET_DIR="$target_dir" cargo clean --manifest-path "$ROOT_DIR/codex-rs/Cargo.toml" >/dev/null 2>&1 || true + available_kb=$(df -Pk "$space_check_dir" | awk 'NR==2 {print $4}') fi if [ "$available_kb" -lt "$required_kb" ]; then echo "error: not enough free disk space to build codex release" >&2 echo "available: ${available_kb} KB, required: ${required_kb} KB" >&2 - echo "free space and retry (or lower threshold with CODEX_MIN_FREE_KB)." >&2 + echo "target dir: $target_dir" >&2 + echo "free space and retry, or point CARGO_TARGET_DIR at a larger disk, for example:" >&2 + echo " CARGO_TARGET_DIR=/path/on/larger/disk/codex-target ./install.sh" >&2 + echo "You can also lower the threshold with CODEX_MIN_FREE_KB." >&2 exit 1 fi @@ -297,9 +320,9 @@ if [ "$should_build" -eq 1 ]; then fi if [ -n "$build_reason" ]; then - echo "Building codex release binary (mode=$build_mode jobs=$build_jobs incremental=$resolved_build_incremental reason=$build_reason)..." + echo "Building codex release binary (mode=$build_mode jobs=$build_jobs incremental=$resolved_build_incremental target=$target_dir reason=$build_reason)..." else - echo "Building codex release binary (mode=$build_mode jobs=$build_jobs incremental=$resolved_build_incremental)..." + echo "Building codex release binary (mode=$build_mode jobs=$build_jobs incremental=$resolved_build_incremental target=$target_dir)..." fi if [ -n "$profile_overrides" ]; then env $profile_overrides CARGO_PROFILE_RELEASE_DEBUG=0 CARGO_INCREMENTAL="$resolved_build_incremental" \ diff --git a/scripts/install/test-source-build-install.sh b/scripts/install/test-source-build-install.sh index 42f1dbf50b8c..d45b9597597a 100755 --- a/scripts/install/test-source-build-install.sh +++ b/scripts/install/test-source-build-install.sh @@ -23,6 +23,13 @@ assert_file_exists() { fi } +assert_contains() { + haystack=$1 + needle=$2 + context=$3 + printf '%s' "$haystack" | grep -F -- "$needle" >/dev/null || fail "$context: missing '$needle'" +} + make_fake_workspace() { workspace=$1 @@ -41,11 +48,15 @@ EOF fn main() {} EOF - cat >"$workspace/fake-bin/cargo" <<'EOF' +cat >"$workspace/fake-bin/cargo" <<'EOF' #!/bin/sh set -eu test_log=${TEST_LOG:?} +command=${1:-} +if [ -n "$command" ]; then + shift +fi manifest_path="" while [ "$#" -gt 0 ]; do case "$1" in @@ -66,6 +77,14 @@ fi manifest_dir=$(dirname -- "$manifest_path") target_dir=${CARGO_TARGET_DIR:-"$manifest_dir/target"} +if [ "$command" = "clean" ]; then + rm -rf "$target_dir" + exit 0 +fi +if [ "$command" != "build" ]; then + echo "unexpected cargo invocation: $command" >&2 + exit 1 +fi mkdir -p "$target_dir/release" printf 'incremental=%s\n' "${CARGO_INCREMENTAL:-unset}" >>"$test_log" cat >"$target_dir/release/codex" <<'BIN' @@ -76,6 +95,34 @@ chmod +x "$target_dir/release/codex" EOF chmod +x "$workspace/fake-bin/cargo" + cat >"$workspace/fake-bin/df" <<'EOF' +#!/bin/sh +set -eu + +path="" +for arg in "$@"; do + path=$arg +done + +default_kb=${TEST_DF_DEFAULT_KB:-10485760} +low_path=${TEST_DF_LOW_PATH:-} +low_kb=${TEST_DF_LOW_KB:-1} +high_path=${TEST_DF_HIGH_PATH:-} +high_kb=${TEST_DF_HIGH_KB:-10485760} + +available_kb=$default_kb +if [ -n "$high_path" ] && [ "$path" = "$high_path" ]; then + available_kb=$high_kb +fi +if [ -n "$low_path" ] && [ "$path" = "$low_path" ]; then + available_kb=$low_kb +fi + +printf 'Filesystem 1024-blocks Used Available Capacity Mounted on\n' +printf 'testfs 9999999 0 %s 0%% %s\n' "$available_kb" "$path" +EOF + chmod +x "$workspace/fake-bin/df" + cat >"$workspace/fake-bin/git" <<'EOF' #!/bin/sh set -eu @@ -123,6 +170,7 @@ assert_help_mentions_new_knobs() { help_output=$(sh "$ROOT_DIR/install.sh" --help) printf '%s\n' "$help_output" | grep "CODEX_BUILD_INCREMENTAL" >/dev/null || fail "--help should mention CODEX_BUILD_INCREMENTAL" printf '%s\n' "$help_output" | grep "CARGO_TARGET_DIR" >/dev/null || fail "--help should mention CARGO_TARGET_DIR" + printf '%s\n' "$help_output" | grep "another disk" >/dev/null || fail "--help should explain when to move CARGO_TARGET_DIR" } test_skips_rebuild_when_source_content_is_unchanged() { @@ -174,6 +222,64 @@ test_honors_custom_target_dir() { trap - EXIT INT TERM } +test_uses_custom_target_dir_filesystem_for_space_check() { + workspace=$(mktemp -d) + test_log="$workspace/cargo.log" + target_dir="$workspace/shared-target" + trap 'rm -rf "$workspace"' EXIT INT TERM + + make_fake_workspace "$workspace" + mkdir -p "$target_dir" + + run_install "$workspace" "$test_log" \ + CARGO_TARGET_DIR="$target_dir" \ + CODEX_MIN_FREE_KB=100 \ + TEST_DF_DEFAULT_KB=10 \ + TEST_DF_LOW_PATH="$workspace" \ + TEST_DF_LOW_KB=10 \ + TEST_DF_HIGH_PATH="$target_dir" \ + TEST_DF_HIGH_KB=1000 + assert_eq "1" "$(count_builds "$test_log")" "custom target dir filesystem should satisfy the space check" + assert_file_exists "$target_dir/release/codex" + + rm -rf "$workspace" + trap - EXIT INT TERM +} + +test_low_space_error_mentions_external_target_dir() { + workspace=$(mktemp -d) + test_log="$workspace/cargo.log" + trap 'rm -rf "$workspace"' EXIT INT TERM + + make_fake_workspace "$workspace" + + set +e + output=$( + cd "$workspace" && env PATH="$workspace/fake-bin:$PATH" \ + TEST_LOG="$test_log" \ + CODEX_INSTALL_DIR="$workspace/bin" \ + CODEX_MIN_FREE_KB=100 \ + TEST_DF_DEFAULT_KB=10 \ + TEST_DF_LOW_PATH="$workspace/codex-rs/target" \ + TEST_DF_LOW_KB=10 \ + ./install.sh 2>&1 + ) + status=$? + set -e + + if [ "$status" -eq 0 ]; then + fail "install should fail when the build target filesystem remains full" + fi + + assert_contains "$output" "target dir: $workspace/codex-rs/target" "low-space error should show the target dir" + assert_contains "$output" "CARGO_TARGET_DIR=/path/on/larger/disk/codex-target ./install.sh" "low-space error should suggest using another disk" + + rm -rf "$workspace" + trap - EXIT INT TERM +} + assert_help_mentions_new_knobs test_skips_rebuild_when_source_content_is_unchanged test_honors_custom_target_dir +test_uses_custom_target_dir_filesystem_for_space_check +test_low_space_error_mentions_external_target_dir