From 50efe515650a0202e4bcfed8a3c7f059fe290af6 Mon Sep 17 00:00:00 2001 From: tw93 Date: Sat, 7 Mar 2026 23:07:38 +0800 Subject: [PATCH] fix(clean): guard empty Xcode DeviceSupport arrays --- lib/clean/dev.sh | 72 +++++++++++++++++++++-------------------- tests/dev_extended.bats | 18 +++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/lib/clean/dev.sh b/lib/clean/dev.sh index 342ea0f..fb8eefd 100644 --- a/lib/clean/dev.sh +++ b/lib/clean/dev.sh @@ -359,47 +359,49 @@ clean_xcode_device_support() { version_dirs+=("$entry") done < <(command find "$ds_dir" -mindepth 1 -maxdepth 1 -print0 2> /dev/null) - # Sort by modification time (most recent first) - local -a sorted_dirs=() - while IFS= read -r line; do - sorted_dirs+=("${line#* }") - done < <( - for entry in "${version_dirs[@]}"; do - printf '%s %s\n' "$(stat -f%m "$entry" 2> /dev/null || echo 0)" "$entry" - done | sort -rn - ) + if [[ ${#version_dirs[@]} -gt 0 ]]; then + # Sort by modification time (most recent first) + local -a sorted_dirs=() + while IFS= read -r line; do + sorted_dirs+=("${line#* }") + done < <( + for entry in "${version_dirs[@]}"; do + printf '%s %s\n' "$(stat -f%m "$entry" 2> /dev/null || echo 0)" "$entry" + done | sort -rn + ) - # Get stale versions (everything after keep_count) - local -a stale_dirs=("${sorted_dirs[@]:$keep_count}") + # Get stale versions (everything after keep_count) + local -a stale_dirs=("${sorted_dirs[@]:$keep_count}") - if [[ ${#stale_dirs[@]} -gt 0 ]]; then - # Calculate total size of stale versions - local stale_size_kb=0 entry_size_kb - for stale_entry in "${stale_dirs[@]}"; do - entry_size_kb=$(get_path_size_kb "$stale_entry" 2> /dev/null || echo 0) - stale_size_kb=$((stale_size_kb + entry_size_kb)) - done - local stale_size_human - stale_size_human=$(bytes_to_human "$((stale_size_kb * 1024))") - - if [[ "$DRY_RUN" == "true" ]]; then - echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} ${display_name} · would remove ${#stale_dirs[@]} old versions (${stale_size_human}), keeping ${keep_count} most recent" - note_activity - else - # Remove old versions - local removed_count=0 + if [[ ${#stale_dirs[@]} -gt 0 ]]; then + # Calculate total size of stale versions + local stale_size_kb=0 entry_size_kb for stale_entry in "${stale_dirs[@]}"; do - if should_protect_path "$stale_entry" || is_path_whitelisted "$stale_entry"; then - continue - fi - if safe_remove "$stale_entry"; then - removed_count=$((removed_count + 1)) - fi + entry_size_kb=$(get_path_size_kb "$stale_entry" 2> /dev/null || echo 0) + stale_size_kb=$((stale_size_kb + entry_size_kb)) done + local stale_size_human + stale_size_human=$(bytes_to_human "$((stale_size_kb * 1024))") - if [[ $removed_count -gt 0 ]]; then - echo -e " ${GREEN}${ICON_SUCCESS}${NC} ${display_name} · removed ${removed_count} old versions, ${stale_size_human}" + if [[ "$DRY_RUN" == "true" ]]; then + echo -e " ${YELLOW}${ICON_DRY_RUN}${NC} ${display_name} · would remove ${#stale_dirs[@]} old versions (${stale_size_human}), keeping ${keep_count} most recent" note_activity + else + # Remove old versions + local removed_count=0 + for stale_entry in "${stale_dirs[@]}"; do + if should_protect_path "$stale_entry" || is_path_whitelisted "$stale_entry"; then + continue + fi + if safe_remove "$stale_entry"; then + removed_count=$((removed_count + 1)) + fi + done + + if [[ $removed_count -gt 0 ]]; then + echo -e " ${GREEN}${ICON_SUCCESS}${NC} ${display_name} · removed ${removed_count} old versions, ${stale_size_human}" + note_activity + fi fi fi fi diff --git a/tests/dev_extended.bats b/tests/dev_extended.bats index f50133a..ec7515e 100644 --- a/tests/dev_extended.bats +++ b/tests/dev_extended.bats @@ -136,6 +136,24 @@ EOF [[ "$output" != *"NDK versions"* ]] } +@test "clean_xcode_device_support handles empty directories under nounset" { + local ds_dir="$HOME/EmptyDeviceSupport" + mkdir -p "$ds_dir" + + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/clean/dev.sh" +note_activity() { :; } +safe_clean() { :; } +clean_xcode_device_support "$HOME/EmptyDeviceSupport" "iOS DeviceSupport" +echo "survived" +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"survived"* ]] +} + @test "clean_xcode_documentation_cache keeps newest DeveloperDocumentation index" { local doc_root="$HOME/DocumentationCache" mkdir -p "$doc_root"