crush-sandbox 0.5.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docker-sandbox-crush +390 -4
- package/package.json +1 -1
package/docker-sandbox-crush
CHANGED
|
@@ -2,10 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
set -e
|
|
4
4
|
|
|
5
|
-
VERSION="0.
|
|
5
|
+
VERSION="0.6.1"
|
|
6
6
|
|
|
7
7
|
# Docker image configuration - can be overridden via DOCKER_SANDBOX_IMAGE environment variable
|
|
8
|
-
DOCKER_IMAGE="${DOCKER_SANDBOX_IMAGE:-node:22-
|
|
8
|
+
DOCKER_IMAGE="${DOCKER_SANDBOX_IMAGE:-node:22-bookworm}"
|
|
9
|
+
|
|
10
|
+
# Color codes (disabled if NO_COLOR is set or not a TTY)
|
|
11
|
+
if [ -t 0 ] && [ -z "$NO_COLOR" ]; then
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
CYAN='\033[0;36m'
|
|
16
|
+
GRAY='\033[0;90m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
else
|
|
19
|
+
GREEN=''
|
|
20
|
+
RED=''
|
|
21
|
+
YELLOW=''
|
|
22
|
+
CYAN=''
|
|
23
|
+
GRAY=''
|
|
24
|
+
NC=''
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
print_success() {
|
|
28
|
+
echo -e "${GREEN}✓${NC} $1"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
print_error() {
|
|
32
|
+
echo -e "${RED}✗${NC} $1" >&2
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
print_warning() {
|
|
36
|
+
echo -e "${YELLOW}!${NC} $1"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
print_info() {
|
|
40
|
+
echo -e "${CYAN}${1}${NC}"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
print_dry_run() {
|
|
44
|
+
echo -e "${YELLOW}[DRY RUN]${NC} $1"
|
|
45
|
+
}
|
|
9
46
|
|
|
10
47
|
print_usage() {
|
|
11
48
|
echo "Usage: crush-sandbox <command> [options]"
|
|
@@ -15,13 +52,15 @@ print_usage() {
|
|
|
15
52
|
echo " clean Remove the sandbox container for current workspace"
|
|
16
53
|
echo " install Install this script to /usr/local/bin"
|
|
17
54
|
echo " update Update to the latest version"
|
|
55
|
+
echo " uninstall Remove all docker-sandbox-crush artifacts"
|
|
18
56
|
echo ""
|
|
19
57
|
echo "Options:"
|
|
20
58
|
echo " --version Show version information"
|
|
21
|
-
echo " --force Skip confirmation
|
|
59
|
+
echo " --force Skip confirmation prompts"
|
|
22
60
|
echo " --shell Start interactive shell instead of Crush CLI (for debugging)"
|
|
23
|
-
echo " --no-host-config
|
|
61
|
+
echo " --no-host-config Skip mounting host Crush config directory"
|
|
24
62
|
echo " --cred-scan Enable credential scanning before starting container"
|
|
63
|
+
echo " --dry-run Preview uninstall actions without executing"
|
|
25
64
|
}
|
|
26
65
|
|
|
27
66
|
validate_docker() {
|
|
@@ -760,6 +799,345 @@ update_command() {
|
|
|
760
799
|
echo "View release notes: https://github.com/wireless25/crush-sandbox/releases"
|
|
761
800
|
}
|
|
762
801
|
|
|
802
|
+
uninstall_command() {
|
|
803
|
+
local dry_run="$1"
|
|
804
|
+
local force="$2"
|
|
805
|
+
|
|
806
|
+
# Track failures for summary
|
|
807
|
+
local script_failed=false
|
|
808
|
+
local alias_failed=false
|
|
809
|
+
local container_failures=()
|
|
810
|
+
local volume_failures=()
|
|
811
|
+
|
|
812
|
+
echo "=== Docker Sandbox Crush Uninstall ==="
|
|
813
|
+
echo ""
|
|
814
|
+
|
|
815
|
+
# Validate Docker is available (even for dry-run, to detect containers)
|
|
816
|
+
if ! validate_docker; then
|
|
817
|
+
echo "Error: Docker is required to uninstall containers and volumes" >&2
|
|
818
|
+
exit 1
|
|
819
|
+
fi
|
|
820
|
+
|
|
821
|
+
# Detect running containers
|
|
822
|
+
local running_containers
|
|
823
|
+
running_containers=$(docker ps --filter "name=^crush-sandbox-" --format '{{.Names}}' 2>/dev/null || echo "")
|
|
824
|
+
|
|
825
|
+
if [ -n "$running_containers" ]; then
|
|
826
|
+
local count
|
|
827
|
+
count=$(echo "$running_containers" | wc -l | tr -d ' ')
|
|
828
|
+
print_warning "$count running container(s) found:"
|
|
829
|
+
echo "$running_containers" | while read -r container; do
|
|
830
|
+
echo " - $container"
|
|
831
|
+
done
|
|
832
|
+
echo ""
|
|
833
|
+
|
|
834
|
+
if [ "$dry_run" = "true" ]; then
|
|
835
|
+
print_dry_run "Would continue with uninstall despite running containers"
|
|
836
|
+
else
|
|
837
|
+
read -p "Continue anyway? [y/N] " confirm
|
|
838
|
+
if [[ ! "$confirm" =~ ^[yY] ]]; then
|
|
839
|
+
echo "Uninstall cancelled"
|
|
840
|
+
exit 2
|
|
841
|
+
fi
|
|
842
|
+
fi
|
|
843
|
+
fi
|
|
844
|
+
|
|
845
|
+
# Step 1: Remove script
|
|
846
|
+
local script_paths=("/usr/local/bin/crush-sandbox" "/usr/local/bin/docker-sandbox-crush")
|
|
847
|
+
local script_to_remove=""
|
|
848
|
+
for script_path in "${script_paths[@]}"; do
|
|
849
|
+
if [ -f "$script_path" ]; then
|
|
850
|
+
script_to_remove="$script_path"
|
|
851
|
+
break
|
|
852
|
+
fi
|
|
853
|
+
done
|
|
854
|
+
|
|
855
|
+
if [ -n "$script_to_remove" ]; then
|
|
856
|
+
print_info "Script: $script_to_remove"
|
|
857
|
+
|
|
858
|
+
if [ "$dry_run" = "true" ]; then
|
|
859
|
+
print_dry_run "Would remove script at $script_to_remove"
|
|
860
|
+
elif [ "$force" = "true" ]; then
|
|
861
|
+
if sudo rm -f "$script_to_remove" 2>/dev/null; then
|
|
862
|
+
print_success "Removed script at $script_to_remove"
|
|
863
|
+
else
|
|
864
|
+
print_error "Failed to remove script at $script_to_remove"
|
|
865
|
+
script_failed=true
|
|
866
|
+
fi
|
|
867
|
+
else
|
|
868
|
+
read -p "Remove script at $script_to_remove? [y/N] " confirm
|
|
869
|
+
if [[ "$confirm" =~ ^[yY] ]]; then
|
|
870
|
+
if sudo rm -f "$script_to_remove" 2>/dev/null; then
|
|
871
|
+
print_success "Removed script at $script_to_remove"
|
|
872
|
+
else
|
|
873
|
+
print_error "Failed to remove script at $script_to_remove"
|
|
874
|
+
script_failed=true
|
|
875
|
+
fi
|
|
876
|
+
else
|
|
877
|
+
echo "Skipped script removal"
|
|
878
|
+
fi
|
|
879
|
+
fi
|
|
880
|
+
else
|
|
881
|
+
echo "No script found (expected at /usr/local/bin/crush-sandbox or /usr/local/bin/docker-sandbox-crush)"
|
|
882
|
+
fi
|
|
883
|
+
|
|
884
|
+
echo ""
|
|
885
|
+
|
|
886
|
+
# Step 2: Remove alias
|
|
887
|
+
local alias_path="/usr/local/bin/crushbox"
|
|
888
|
+
if [ -f "$alias_path" ] || [ -L "$alias_path" ]; then
|
|
889
|
+
print_info "Alias: $alias_path"
|
|
890
|
+
|
|
891
|
+
if [ "$dry_run" = "true" ]; then
|
|
892
|
+
print_dry_run "Would remove alias at $alias_path"
|
|
893
|
+
elif [ "$force" = "true" ]; then
|
|
894
|
+
if sudo rm -f "$alias_path" 2>/dev/null; then
|
|
895
|
+
print_success "Removed alias at $alias_path"
|
|
896
|
+
else
|
|
897
|
+
print_error "Failed to remove alias at $alias_path"
|
|
898
|
+
alias_failed=true
|
|
899
|
+
fi
|
|
900
|
+
else
|
|
901
|
+
read -p "Remove alias at $alias_path? [y/N] " confirm
|
|
902
|
+
if [[ "$confirm" =~ ^[yY] ]]; then
|
|
903
|
+
if sudo rm -f "$alias_path" 2>/dev/null; then
|
|
904
|
+
print_success "Removed alias at $alias_path"
|
|
905
|
+
else
|
|
906
|
+
print_error "Failed to remove alias at $alias_path"
|
|
907
|
+
alias_failed=true
|
|
908
|
+
fi
|
|
909
|
+
else
|
|
910
|
+
echo "Skipped alias removal"
|
|
911
|
+
fi
|
|
912
|
+
fi
|
|
913
|
+
else
|
|
914
|
+
echo "No alias found at $alias_path"
|
|
915
|
+
fi
|
|
916
|
+
|
|
917
|
+
echo ""
|
|
918
|
+
|
|
919
|
+
# Step 3: Remove containers
|
|
920
|
+
local containers
|
|
921
|
+
containers=$(docker ps -a --filter "name=^crush-sandbox-" --format '{{.Names}}' 2>/dev/null || echo "")
|
|
922
|
+
|
|
923
|
+
if [ -n "$containers" ]; then
|
|
924
|
+
local container_count
|
|
925
|
+
container_count=$(echo "$containers" | wc -l | tr -d ' ')
|
|
926
|
+
echo "Found $container_count container(s):"
|
|
927
|
+
|
|
928
|
+
# Use a different approach to avoid subshell issues
|
|
929
|
+
while IFS= read -r container; do
|
|
930
|
+
local status
|
|
931
|
+
status=$(docker inspect --format '{{.State.Status}}' "$container" 2>/dev/null || echo "unknown")
|
|
932
|
+
echo " - $container ($status)"
|
|
933
|
+
done <<< "$containers"
|
|
934
|
+
|
|
935
|
+
if [ "$dry_run" = "true" ]; then
|
|
936
|
+
print_dry_run "Would remove $container_count container(s)"
|
|
937
|
+
echo ""
|
|
938
|
+
elif [ "$force" = "true" ] || [[ "$(read -p "Remove $container_count container(s)? [y/N] " confirm; echo "$confirm")" =~ ^[yY] ]]; then
|
|
939
|
+
if [ "$force" != "true" ]; then
|
|
940
|
+
read -p "Remove $container_count container(s)? [y/N] " confirm
|
|
941
|
+
if [[ ! "$confirm" =~ ^[yY] ]]; then
|
|
942
|
+
echo "Skipped container removal"
|
|
943
|
+
else
|
|
944
|
+
echo ""
|
|
945
|
+
fi
|
|
946
|
+
else
|
|
947
|
+
echo ""
|
|
948
|
+
fi
|
|
949
|
+
|
|
950
|
+
if [ "$force" = "true" ] || [[ "$confirm" =~ ^[yY] ]]; then
|
|
951
|
+
local i=1
|
|
952
|
+
while IFS= read -r container; do
|
|
953
|
+
echo -n "[$i/$container_count] Removing container $container... "
|
|
954
|
+
if docker stop "$container" >/dev/null 2>&1; then
|
|
955
|
+
if docker rm "$container" >/dev/null 2>&1; then
|
|
956
|
+
print_success "Removed $container"
|
|
957
|
+
else
|
|
958
|
+
print_error "Failed to remove $container (stop succeeded)"
|
|
959
|
+
container_failures+=("Failed to remove $container")
|
|
960
|
+
fi
|
|
961
|
+
else
|
|
962
|
+
print_error "Failed to stop $container"
|
|
963
|
+
container_failures+=("Failed to stop $container")
|
|
964
|
+
fi
|
|
965
|
+
i=$((i + 1))
|
|
966
|
+
done <<< "$containers"
|
|
967
|
+
fi
|
|
968
|
+
else
|
|
969
|
+
echo "Skipped container removal"
|
|
970
|
+
fi
|
|
971
|
+
else
|
|
972
|
+
echo "No containers found"
|
|
973
|
+
fi
|
|
974
|
+
|
|
975
|
+
echo ""
|
|
976
|
+
|
|
977
|
+
# Step 4: Remove volumes
|
|
978
|
+
local volumes
|
|
979
|
+
volumes=$(docker volume ls --filter "name=^crush-cache-" --format '{{.Name}}' 2>/dev/null || echo "")
|
|
980
|
+
|
|
981
|
+
if [ -n "$volumes" ]; then
|
|
982
|
+
local volume_count
|
|
983
|
+
volume_count=$(echo "$volumes" | wc -l | tr -d ' ')
|
|
984
|
+
echo "Found $volume_count volume(s):"
|
|
985
|
+
|
|
986
|
+
# Use a different approach to avoid subshell issues
|
|
987
|
+
while IFS= read -r volume; do
|
|
988
|
+
local size
|
|
989
|
+
size=$(docker volume inspect --format '{{.UsageData.Size}}' "$volume" 2>/dev/null || echo "unknown")
|
|
990
|
+
echo " - $volume ($size)"
|
|
991
|
+
done <<< "$volumes"
|
|
992
|
+
|
|
993
|
+
if [ "$dry_run" = "true" ]; then
|
|
994
|
+
print_dry_run "Would remove $volume_count volume(s)"
|
|
995
|
+
echo ""
|
|
996
|
+
elif [ "$force" = "true" ] || [[ "$(read -p "Remove $volume_count volume(s)? [y/N] " confirm; echo "$confirm")" =~ ^[yY] ]]; then
|
|
997
|
+
if [ "$force" != "true" ]; then
|
|
998
|
+
read -p "Remove $volume_count volume(s)? [y/N] " confirm
|
|
999
|
+
if [[ ! "$confirm" =~ ^[yY] ]]; then
|
|
1000
|
+
echo "Skipped volume removal"
|
|
1001
|
+
else
|
|
1002
|
+
echo ""
|
|
1003
|
+
fi
|
|
1004
|
+
else
|
|
1005
|
+
echo ""
|
|
1006
|
+
fi
|
|
1007
|
+
|
|
1008
|
+
if [ "$force" = "true" ] || [[ "$confirm" =~ ^[yY] ]]; then
|
|
1009
|
+
local i=1
|
|
1010
|
+
while IFS= read -r volume; do
|
|
1011
|
+
echo -n "[$i/$volume_count] Removing volume $volume... "
|
|
1012
|
+
if docker volume rm "$volume" >/dev/null 2>&1; then
|
|
1013
|
+
print_success "Removed $volume"
|
|
1014
|
+
else
|
|
1015
|
+
print_error "Failed to remove $volume"
|
|
1016
|
+
volume_failures+=("Failed to remove $volume")
|
|
1017
|
+
fi
|
|
1018
|
+
i=$((i + 1))
|
|
1019
|
+
done <<< "$volumes"
|
|
1020
|
+
fi
|
|
1021
|
+
else
|
|
1022
|
+
echo "Skipped volume removal"
|
|
1023
|
+
fi
|
|
1024
|
+
else
|
|
1025
|
+
echo "No volumes found"
|
|
1026
|
+
fi
|
|
1027
|
+
|
|
1028
|
+
echo ""
|
|
1029
|
+
|
|
1030
|
+
# Final summary
|
|
1031
|
+
echo "=== Uninstall Summary ==="
|
|
1032
|
+
|
|
1033
|
+
# Script status
|
|
1034
|
+
if [ -n "$script_to_remove" ]; then
|
|
1035
|
+
if [ "$dry_run" = "true" ]; then
|
|
1036
|
+
echo -e "Script: ${YELLOW}[DRY RUN]${NC} Would remove $script_to_remove"
|
|
1037
|
+
elif [ "$script_failed" = "true" ]; then
|
|
1038
|
+
echo -e "Script: ${RED}✗ Failed${NC}"
|
|
1039
|
+
else
|
|
1040
|
+
echo -e "Script: ${GREEN}✓ Removed${NC}"
|
|
1041
|
+
fi
|
|
1042
|
+
else
|
|
1043
|
+
echo -e "Script: ${GRAY}N/A${NC} (not installed)"
|
|
1044
|
+
fi
|
|
1045
|
+
|
|
1046
|
+
# Alias status
|
|
1047
|
+
if [ -f "$alias_path" ] || [ -L "$alias_path" ]; then
|
|
1048
|
+
if [ "$dry_run" = "true" ]; then
|
|
1049
|
+
echo -e "Alias: ${YELLOW}[DRY RUN]${NC} Would remove $alias_path"
|
|
1050
|
+
elif [ "$alias_failed" = "true" ]; then
|
|
1051
|
+
echo -e "Alias: ${RED}✗ Failed${NC}"
|
|
1052
|
+
else
|
|
1053
|
+
echo -e "Alias: ${GREEN}✓ Removed${NC}"
|
|
1054
|
+
fi
|
|
1055
|
+
else
|
|
1056
|
+
echo -e "Alias: ${GRAY}N/A${NC} (not installed)"
|
|
1057
|
+
fi
|
|
1058
|
+
|
|
1059
|
+
# Containers status
|
|
1060
|
+
if [ -n "$containers" ]; then
|
|
1061
|
+
local total_containers
|
|
1062
|
+
total_containers=$(echo "$containers" | wc -l | tr -d ' ')
|
|
1063
|
+
local container_fail_count
|
|
1064
|
+
if [ "$dry_run" = "true" ]; then
|
|
1065
|
+
container_fail_count=0
|
|
1066
|
+
echo -e "Containers: ${YELLOW}[DRY RUN]${NC} Would remove $total_containers container(s)"
|
|
1067
|
+
else
|
|
1068
|
+
container_fail_count=${#container_failures[@]}
|
|
1069
|
+
if [ $container_fail_count -eq 0 ]; then
|
|
1070
|
+
echo -e "Containers: ${GREEN}✓ Removed all${NC} ($total_containers)"
|
|
1071
|
+
else
|
|
1072
|
+
echo -e "Containers: ${RED}✗ Partially failed${NC} ($container_fail_count/$total_containers failed)"
|
|
1073
|
+
fi
|
|
1074
|
+
fi
|
|
1075
|
+
else
|
|
1076
|
+
echo -e "Containers: ${GRAY}N/A${NC} (none found)"
|
|
1077
|
+
fi
|
|
1078
|
+
|
|
1079
|
+
# Volumes status
|
|
1080
|
+
if [ -n "$volumes" ]; then
|
|
1081
|
+
local total_volumes
|
|
1082
|
+
total_volumes=$(echo "$volumes" | wc -l | tr -d ' ')
|
|
1083
|
+
local volume_fail_count
|
|
1084
|
+
if [ "$dry_run" = "true" ]; then
|
|
1085
|
+
volume_fail_count=0
|
|
1086
|
+
echo -e "Volumes: ${YELLOW}[DRY RUN]${NC} Would remove $total_volumes volume(s)"
|
|
1087
|
+
else
|
|
1088
|
+
volume_fail_count=${#volume_failures[@]}
|
|
1089
|
+
if [ $volume_fail_count -eq 0 ]; then
|
|
1090
|
+
echo -e "Volumes: ${GREEN}✓ Removed all${NC} ($total_volumes)"
|
|
1091
|
+
else
|
|
1092
|
+
echo -e "Volumes: ${RED}✗ Partially failed${NC} ($volume_fail_count/$total_volumes failed)"
|
|
1093
|
+
fi
|
|
1094
|
+
fi
|
|
1095
|
+
else
|
|
1096
|
+
echo -e "Volumes: ${GRAY}N/A${NC} (none found)"
|
|
1097
|
+
fi
|
|
1098
|
+
|
|
1099
|
+
# Error details
|
|
1100
|
+
if [ "$dry_run" != "true" ]; then
|
|
1101
|
+
local total_errors=0
|
|
1102
|
+
[ "$script_failed" = "true" ] && total_errors=$((total_errors + 1))
|
|
1103
|
+
[ "$alias_failed" = "true" ] && total_errors=$((total_errors + 1))
|
|
1104
|
+
total_errors=$((total_errors + ${#container_failures[@]} + ${#volume_failures[@]}))
|
|
1105
|
+
|
|
1106
|
+
if [ $total_errors -gt 0 ]; then
|
|
1107
|
+
echo ""
|
|
1108
|
+
echo "=== Errors ==="
|
|
1109
|
+
|
|
1110
|
+
if [ "$script_failed" = "true" ]; then
|
|
1111
|
+
echo -e " ${RED}✗${NC} Script: Failed to remove"
|
|
1112
|
+
fi
|
|
1113
|
+
|
|
1114
|
+
if [ "$alias_failed" = "true" ]; then
|
|
1115
|
+
echo -e " ${RED}✗${NC} Alias: Failed to remove"
|
|
1116
|
+
fi
|
|
1117
|
+
|
|
1118
|
+
for failure in "${container_failures[@]}"; do
|
|
1119
|
+
echo -e " ${RED}✗${NC} Container: $failure"
|
|
1120
|
+
done
|
|
1121
|
+
|
|
1122
|
+
for failure in "${volume_failures[@]}"; do
|
|
1123
|
+
echo -e " ${RED}✗${NC} Volume: $failure"
|
|
1124
|
+
done
|
|
1125
|
+
|
|
1126
|
+
echo ""
|
|
1127
|
+
echo -e "${RED}Uninstall complete. $total_errors error(s) occurred.${NC}"
|
|
1128
|
+
exit 1
|
|
1129
|
+
else
|
|
1130
|
+
echo ""
|
|
1131
|
+
echo -e "${GREEN}Uninstall complete. All components removed successfully.${NC}"
|
|
1132
|
+
exit 0
|
|
1133
|
+
fi
|
|
1134
|
+
else
|
|
1135
|
+
echo ""
|
|
1136
|
+
echo -e "${YELLOW}[DRY RUN] Uninstall preview complete. No changes made.${NC}"
|
|
1137
|
+
exit 0
|
|
1138
|
+
fi
|
|
1139
|
+
}
|
|
1140
|
+
|
|
763
1141
|
# Main script logic
|
|
764
1142
|
if [ $# -lt 1 ]; then
|
|
765
1143
|
print_usage >&2
|
|
@@ -773,6 +1151,7 @@ SHELL_MODE="false"
|
|
|
773
1151
|
SHOW_VERSION="false"
|
|
774
1152
|
NO_HOST_CONFIG="false"
|
|
775
1153
|
CRED_SCAN="false"
|
|
1154
|
+
DRY_RUN="false"
|
|
776
1155
|
|
|
777
1156
|
while [[ $# -gt 0 ]]; do
|
|
778
1157
|
case "$1" in
|
|
@@ -796,6 +1175,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
796
1175
|
CRED_SCAN="true"
|
|
797
1176
|
shift
|
|
798
1177
|
;;
|
|
1178
|
+
--dry-run)
|
|
1179
|
+
DRY_RUN="true"
|
|
1180
|
+
shift
|
|
1181
|
+
;;
|
|
799
1182
|
-*)
|
|
800
1183
|
echo "Error: Unknown option: $1" >&2
|
|
801
1184
|
print_usage
|
|
@@ -838,6 +1221,9 @@ case "$COMMAND" in
|
|
|
838
1221
|
update)
|
|
839
1222
|
update_command
|
|
840
1223
|
;;
|
|
1224
|
+
uninstall)
|
|
1225
|
+
uninstall_command "$DRY_RUN" "$FORCE"
|
|
1226
|
+
;;
|
|
841
1227
|
*)
|
|
842
1228
|
echo "Error: Unknown command: $COMMAND" >&2
|
|
843
1229
|
print_usage >&2
|