Skip to content

Commit ecb3a28

Browse files
Merge pull request #170 from bugman-007/feat/preset-diff-command
Feat/preset diff command
2 parents 4ad616a + 64ccb50 commit ecb3a28

2 files changed

Lines changed: 282 additions & 1 deletion

File tree

dream-server/dream-cli

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,8 +831,146 @@ META
831831
cd "$INSTALL_DIR"
832832
;;
833833

834+
diff|d)
835+
local preset1="$2"
836+
local preset2="$3"
837+
838+
# Validate arguments
839+
if [[ -z "$preset1" ]] || [[ -z "$preset2" ]]; then
840+
error "Usage: dream preset diff <preset1> <preset2>"
841+
fi
842+
843+
local dir1="$PRESETS_DIR/$preset1"
844+
local dir2="$PRESETS_DIR/$preset2"
845+
846+
# Check both presets exist
847+
[[ -d "$dir1" ]] || error "Preset '$preset1' not found"
848+
[[ -d "$dir2" ]] || error "Preset '$preset2' not found"
849+
850+
log "Comparing presets: ${CYAN}$preset1${NC} vs ${CYAN}$preset2${NC}"
851+
echo ""
852+
853+
# Compare metadata
854+
echo -e "${CYAN}━━━ Metadata ━━━${NC}"
855+
if [[ -f "$dir1/meta.txt" ]] && [[ -f "$dir2/meta.txt" ]]; then
856+
diff -u "$dir1/meta.txt" "$dir2/meta.txt" | tail -n +4 | sed "s/^-/$(printf "${RED}-${NC}")/" | sed "s/^+/$(printf "${GREEN}+${NC}")/" || true
857+
fi
858+
echo ""
859+
860+
# Compare extensions
861+
echo -e "${CYAN}━━━ Service State ━━━${NC}"
862+
if [[ -f "$dir1/extensions.list" ]] && [[ -f "$dir2/extensions.list" ]]; then
863+
# Parse both files into associative arrays
864+
declare -A ext1 ext2
865+
while IFS=: read -r state sid; do
866+
[[ -n "$sid" ]] && ext1["$sid"]="$state"
867+
done < "$dir1/extensions.list"
868+
while IFS=: read -r state sid; do
869+
[[ -n "$sid" ]] && ext2["$sid"]="$state"
870+
done < "$dir2/extensions.list"
871+
872+
# Find all unique service IDs
873+
local all_sids=()
874+
for sid in "${!ext1[@]}"; do all_sids+=("$sid"); done
875+
for sid in "${!ext2[@]}"; do
876+
[[ -z "${ext1[$sid]}" ]] && all_sids+=("$sid")
877+
done
878+
879+
# Sort and compare
880+
local has_diff=false
881+
for sid in $(printf '%s\n' "${all_sids[@]}" | sort); do
882+
local state1="${ext1[$sid]:-missing}"
883+
local state2="${ext2[$sid]:-missing}"
884+
885+
if [[ "$state1" != "$state2" ]]; then
886+
has_diff=true
887+
if [[ "$state1" == "missing" ]]; then
888+
echo -e " ${GREEN}+${NC} $sid: $state2"
889+
elif [[ "$state2" == "missing" ]]; then
890+
echo -e " ${RED}-${NC} $sid: $state1"
891+
else
892+
echo -e " ${YELLOW}~${NC} $sid: $state1$state2"
893+
fi
894+
fi
895+
done
896+
897+
[[ "$has_diff" == "false" ]] && echo " (no differences)"
898+
fi
899+
echo ""
900+
901+
# Compare environment variables
902+
echo -e "${CYAN}━━━ Environment Variables ━━━${NC}"
903+
if [[ -f "$dir1/env" ]] && [[ -f "$dir2/env" ]]; then
904+
# Parse both env files
905+
declare -A env1 env2
906+
while IFS='=' read -r key value; do
907+
[[ "$key" =~ ^[[:space:]]*# ]] && continue
908+
[[ -z "$key" ]] && continue
909+
env1["$key"]="$value"
910+
done < "$dir1/env"
911+
while IFS='=' read -r key value; do
912+
[[ "$key" =~ ^[[:space:]]*# ]] && continue
913+
[[ -z "$key" ]] && continue
914+
env2["$key"]="$value"
915+
done < "$dir2/env"
916+
917+
# Find all unique keys
918+
local all_keys=()
919+
for key in "${!env1[@]}"; do all_keys+=("$key"); done
920+
for key in "${!env2[@]}"; do
921+
[[ -z "${env1[$key]}" ]] && all_keys+=("$key")
922+
done
923+
924+
# Sort and compare (mask secrets)
925+
local has_diff=false
926+
for key in $(printf '%s\n' "${all_keys[@]}" | sort); do
927+
local val1="${env1[$key]}"
928+
local val2="${env2[$key]}"
929+
930+
# Check if values differ BEFORE masking
931+
if [[ "$val1" != "$val2" ]]; then
932+
has_diff=true
933+
934+
# Mask sensitive values for display
935+
if [[ "$key" =~ (PASSWORD|SECRET|KEY|TOKEN|API) ]]; then
936+
[[ -n "$val1" ]] && val1="***"
937+
[[ -n "$val2" ]] && val2="***"
938+
fi
939+
940+
if [[ -z "$val1" ]]; then
941+
echo -e " ${GREEN}+${NC} $key=$val2"
942+
elif [[ -z "$val2" ]]; then
943+
echo -e " ${RED}-${NC} $key=$val1"
944+
else
945+
echo -e " ${YELLOW}~${NC} $key: $val1$val2"
946+
fi
947+
fi
948+
done
949+
950+
[[ "$has_diff" == "false" ]] && echo " (no differences)"
951+
fi
952+
echo ""
953+
;;
954+
834955
*)
835-
log "Usage: dream preset <save|load|list|delete|export|import> [name]"
956+
cat <<EOF
957+
Usage: dream preset <command> [args]
958+
959+
Commands:
960+
save <name> Save current configuration as a preset
961+
load <name> Load a preset (restores .env and service state)
962+
list List all saved presets
963+
delete <name> Delete a preset
964+
export <name> <file> Export preset to .tar.gz archive
965+
import <file> Import preset from .tar.gz archive
966+
diff <preset1> <preset2> Compare two presets and show differences
967+
968+
Examples:
969+
dream preset save gaming
970+
dream preset load gaming
971+
dream preset diff gaming production
972+
dream preset export gaming ~/gaming-preset.tar.gz
973+
EOF
836974
;;
837975
esac
838976
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/bin/bash
2+
# test-preset-diff.sh
3+
# Tests preset diff command
4+
5+
set -euo pipefail
6+
7+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+
DREAM_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
9+
DREAM_CLI="$DREAM_DIR/dream-cli"
10+
11+
# Test counters
12+
PASS=0
13+
FAIL=0
14+
15+
# Test helper functions
16+
pass() { echo "$1"; PASS=$((PASS + 1)); }
17+
fail() { echo "$1"; FAIL=$((FAIL + 1)); }
18+
19+
# Setup test environment
20+
setup() {
21+
export DREAM_HOME="$DREAM_DIR"
22+
export INSTALL_DIR="$DREAM_DIR"
23+
export SCRIPT_DIR="$DREAM_DIR"
24+
# PRESETS_DIR is set by dream-cli to ${INSTALL_DIR}/presets
25+
mkdir -p "$DREAM_DIR/presets"
26+
touch "$DREAM_DIR/docker-compose.base.yml" 2>/dev/null || true
27+
}
28+
29+
# Cleanup
30+
cleanup() {
31+
rm -rf "$DREAM_DIR/presets/test-preset-"* 2>/dev/null || true
32+
# Don't remove docker-compose.base.yml if it existed before tests
33+
}
34+
35+
# Test 1: Diff command requires two arguments
36+
test_diff_requires_args() {
37+
local output
38+
output=$("$DREAM_CLI" preset diff 2>&1 || true)
39+
if echo "$output" | grep -q "Usage:"; then
40+
pass "Diff requires two arguments"
41+
else
42+
fail "Diff should require two arguments"
43+
fi
44+
}
45+
46+
# Test 2: Diff fails if preset doesn't exist
47+
test_diff_nonexistent_preset() {
48+
local output
49+
output=$("$DREAM_CLI" preset diff nonexistent1 nonexistent2 2>&1 || true)
50+
if echo "$output" | grep -q "not found"; then
51+
pass "Diff fails for nonexistent presets"
52+
else
53+
fail "Diff should fail for nonexistent presets"
54+
fi
55+
}
56+
57+
# Test 3: Diff shows no differences for identical presets
58+
test_diff_identical() {
59+
local presets_dir="$DREAM_DIR/presets"
60+
mkdir -p "$presets_dir/test-preset-a" "$presets_dir/test-preset-b"
61+
echo "TIER=3" > "$presets_dir/test-preset-a/env"
62+
echo "TIER=3" > "$presets_dir/test-preset-b/env"
63+
echo "enabled:llama-server" > "$presets_dir/test-preset-a/extensions.list"
64+
echo "enabled:llama-server" > "$presets_dir/test-preset-b/extensions.list"
65+
66+
local output
67+
output=$("$DREAM_CLI" preset diff test-preset-a test-preset-b 2>&1 || true)
68+
if echo "$output" | grep -q "no differences"; then
69+
pass "Diff shows no differences for identical presets"
70+
else
71+
fail "Diff should show no differences for identical presets"
72+
fi
73+
}
74+
75+
# Test 4: Diff detects environment variable changes
76+
test_diff_env_changes() {
77+
local presets_dir="$DREAM_DIR/presets"
78+
mkdir -p "$presets_dir/test-preset-c" "$presets_dir/test-preset-d"
79+
echo "TIER=3" > "$presets_dir/test-preset-c/env"
80+
echo "TIER=4" > "$presets_dir/test-preset-d/env"
81+
82+
local output
83+
output=$("$DREAM_CLI" preset diff test-preset-c test-preset-d 2>&1 || true)
84+
if echo "$output" | grep -q "TIER"; then
85+
pass "Diff detects environment variable changes"
86+
else
87+
fail "Diff should detect environment variable changes"
88+
fi
89+
}
90+
91+
# Test 5: Diff detects service state changes
92+
test_diff_service_changes() {
93+
local presets_dir="$DREAM_DIR/presets"
94+
mkdir -p "$presets_dir/test-preset-e" "$presets_dir/test-preset-f"
95+
echo "enabled:llama-server" > "$presets_dir/test-preset-e/extensions.list"
96+
echo "disabled:llama-server" > "$presets_dir/test-preset-f/extensions.list"
97+
98+
local output
99+
output=$("$DREAM_CLI" preset diff test-preset-e test-preset-f 2>&1 || true)
100+
if echo "$output" | grep -q "llama-server"; then
101+
pass "Diff detects service state changes"
102+
else
103+
fail "Diff should detect service state changes"
104+
fi
105+
}
106+
107+
# Test 6: Diff masks sensitive values
108+
test_diff_masks_secrets() {
109+
local presets_dir="$DREAM_DIR/presets"
110+
mkdir -p "$presets_dir/test-preset-g" "$presets_dir/test-preset-h"
111+
echo "API_KEY=secret123" > "$presets_dir/test-preset-g/env"
112+
echo "API_KEY=secret456" > "$presets_dir/test-preset-h/env"
113+
echo "enabled:llama-server" > "$presets_dir/test-preset-g/extensions.list"
114+
echo "enabled:llama-server" > "$presets_dir/test-preset-h/extensions.list"
115+
116+
local output
117+
output=$("$DREAM_CLI" preset diff test-preset-g test-preset-h 2>&1 || true)
118+
if echo "$output" | grep -q "API_KEY" && echo "$output" | grep -q "\*\*\*"; then
119+
pass "Diff masks sensitive values"
120+
else
121+
fail "Diff should mask sensitive values"
122+
fi
123+
}
124+
125+
# Run tests
126+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
127+
echo "Preset Diff Tests"
128+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
129+
130+
setup
131+
test_diff_requires_args
132+
test_diff_nonexistent_preset
133+
test_diff_identical
134+
test_diff_env_changes
135+
test_diff_service_changes
136+
test_diff_masks_secrets
137+
cleanup
138+
139+
# Summary
140+
echo ""
141+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
142+
echo "Test Results: $PASS passed, $FAIL failed"
143+
[[ $FAIL -eq 0 ]] && exit 0 || exit 1

0 commit comments

Comments
 (0)