-
Notifications
You must be signed in to change notification settings - Fork 159
Expand file tree
/
Copy pathstart.sh
More file actions
executable file
·1343 lines (1153 loc) · 47.1 KB
/
start.sh
File metadata and controls
executable file
·1343 lines (1153 loc) · 47.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# Start script for running DBDiff tests with different PHP/MySQL version combinations
# Usage: ./start.sh [php_version] [mysql_version]
# Example: ./start.sh 8.3 8.0
# ./start.sh all all (runs all combinations)
# ./start.sh (interactive mode)
# Load environment configuration
if [ -f .env ]; then
# Parse .env file and export variables
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ $key =~ ^[[:space:]]*# ]] && continue
[[ -z $key ]] && continue
# Remove quotes and export
value=$(echo "$value" | sed 's/^["'\'']//' | sed 's/["'\'']$//')
export "$key"="$value"
done < .env
fi
# Compose command: prefer COMPOSE_CMD env var, then docker-compose, then podman-compose
if [ -z "${COMPOSE_CMD:-}" ]; then
if command -v docker-compose &>/dev/null; then
COMPOSE_CMD="docker-compose"
elif command -v podman-compose &>/dev/null; then
COMPOSE_CMD="podman-compose"
else
echo "❌ Neither docker-compose nor podman-compose found. Please install one."
exit 1
fi
fi
# Set defaults for environment variables if not set (or if .env is missing/partial)
# PHP Versions
export PHP_VERSION_81=${PHP_VERSION_81:-8.1}
export PHP_VERSION_82=${PHP_VERSION_82:-8.2}
export PHP_VERSION_83=${PHP_VERSION_83:-8.3}
export PHP_VERSION_84=${PHP_VERSION_84:-8.4}
export PHP_VERSION_85=${PHP_VERSION_85:-8.5}
# MySQL Versions
export MYSQL_VERSION_80=${MYSQL_VERSION_80:-8.0}
export MYSQL_VERSION_84=${MYSQL_VERSION_84:-8.4}
export MYSQL_VERSION_93=${MYSQL_VERSION_93:-9.3}
export MYSQL_VERSION_96=${MYSQL_VERSION_96:-9.6}
# Database Ports
export DB_PORT_MYSQL80=${DB_PORT_MYSQL80:-3306}
export DB_PORT_MYSQL84=${DB_PORT_MYSQL84:-3307}
export DB_PORT_MYSQL93=${DB_PORT_MYSQL93:-3308}
export DB_PORT_MYSQL96=${DB_PORT_MYSQL96:-3309}
# PHPMyAdmin Ports
export PHPMYADMIN_PORT_MYSQL80=${PHPMYADMIN_PORT_MYSQL80:-8080}
export PHPMYADMIN_PORT_MYSQL84=${PHPMYADMIN_PORT_MYSQL84:-8081}
export PHPMYADMIN_PORT_MYSQL93=${PHPMYADMIN_PORT_MYSQL93:-8082}
export PHPMYADMIN_PORT_MYSQL96=${PHPMYADMIN_PORT_MYSQL96:-8083}
# Set defaults if not loaded from .env
export COMPOSE_BAKE=${COMPOSE_BAKE:-true}
export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-dbdiff}
# Set up signal handling for cleanup
trap cleanup_on_exit EXIT
trap cleanup_on_interrupt INT TERM
cleanup_on_exit() {
local exit_code=$?
stop_watchdog
if [ "$NO_TEARDOWN" = "true" ]; then
echo ""
echo "🔄 No teardown mode - containers and resources left running"
echo "💡 Use 'docker-compose down / podman-compose down' or run stop.sh to cleanup manually"
return 0
fi
if [ $exit_code -ne 0 ]; then
echo ""
echo "🧹 Script exited with error code $exit_code. Cleaning up..."
else
echo ""
echo "🧹 Script completed. Cleaning up..."
fi
cleanup_docker
}
cleanup_on_interrupt() {
echo ""
echo "🛑 Interrupted!"
stop_watchdog
if [ "$NO_TEARDOWN" = "true" ]; then
echo "🔄 No teardown mode - keeping containers and resources running"
echo "💡 Use 'docker-compose down / podman-compose down' or run stop.sh to cleanup manually"
else
echo "🧹 Force cleaning up..."
cleanup_docker
echo "Cleanup completed."
fi
echo "Exiting..."
exit 130
}
# Configuration variables - derive arrays and mappings from individual variables
# Build PHP versions array from individual variables
PHP_VERSIONS=()
for var in PHP_VERSION_81 PHP_VERSION_82 PHP_VERSION_83 PHP_VERSION_84 PHP_VERSION_85; do
if [ -n "${!var}" ]; then
PHP_VERSIONS+=("${!var}")
fi
done
# Build MySQL version mapping from individual variables
MYSQL_VERSION_MAPPING=()
if [ -n "$MYSQL_VERSION_80" ]; then
MYSQL_VERSION_MAPPING+=("$MYSQL_VERSION_80:mysql80")
fi
if [ -n "$MYSQL_VERSION_84" ]; then
MYSQL_VERSION_MAPPING+=("$MYSQL_VERSION_84:mysql84")
fi
if [ -n "$MYSQL_VERSION_93" ]; then
MYSQL_VERSION_MAPPING+=("$MYSQL_VERSION_93:mysql93")
fi
if [ -n "$MYSQL_VERSION_96" ]; then
MYSQL_VERSION_MAPPING+=("$MYSQL_VERSION_96:mysql96")
fi
# Static MySQL service names
MYSQL_VERSIONS=("mysql80" "mysql84" "mysql93" "mysql96")
# Fallback to defaults if individual variables not found
if [ ${#PHP_VERSIONS[@]} -eq 0 ]; then
PHP_VERSIONS=("8.1" "8.2" "8.3" "8.4" "8.5")
fi
if [ ${#MYSQL_VERSION_MAPPING[@]} -eq 0 ]; then
MYSQL_VERSION_MAPPING=("8.0:mysql80" "8.4:mysql84" "9.3:mysql93" "9.6:mysql96")
fi
# Port configuration - derive arrays from individual variables
DB_PORTS=(${DB_PORT_MYSQL80:-3306} ${DB_PORT_MYSQL84:-3307} ${DB_PORT_MYSQL93:-3308} ${DB_PORT_MYSQL96:-3309})
PHPMYADMIN_PORTS=(${PHPMYADMIN_PORT_MYSQL80:-18080} ${PHPMYADMIN_PORT_MYSQL84:-18081} ${PHPMYADMIN_PORT_MYSQL93:-18082} ${PHPMYADMIN_PORT_MYSQL96:-18083})
# Database connection details
DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-"rootpass"}
DB_NAME=${DB_NAME:-"diff1"}
DB_USER=${DB_USER:-"dbdiff"}
DB_PASSWORD=${DB_PASSWORD:-"dbdiff"}
# Timeout configuration (in seconds)
DATABASE_STARTUP_TIMEOUT=${DATABASE_STARTUP_TIMEOUT:-180}
DATABASE_HEALTH_TIMEOUT=${DATABASE_HEALTH_TIMEOUT:-120}
CLI_BUILD_TIMEOUT=${CLI_BUILD_TIMEOUT:-300}
PHP_VERSION_CHECK_TIMEOUT=${PHP_VERSION_CHECK_TIMEOUT:-30}
PHPUNIT_TEST_TIMEOUT=${PHPUNIT_TEST_TIMEOUT:-300}
# Function to show usage
show_usage() {
local mysql_display=($(get_mysql_version_display))
echo "Usage: $0 [php_version] [mysql_version] [--watch] [--no-teardown]"
echo ""
echo "Available PHP versions: ${PHP_VERSIONS[@]}"
echo "Available MySQL versions: ${mysql_display[@]}"
echo ""
echo "Examples:"
echo " $0 8.3 8.0 # Test specific combination"
echo " $0 all all # Test all combinations"
echo " $0 8.3 8.0 --watch # Watch mode - run tests once then keep active"
echo " $0 8.3 8.0 --no-teardown # Single run without cleanup"
echo " $0 8.3 8.0 --watch --no-teardown # Watch mode with no cleanup on exit"
echo " $0 8.3 8.0 --fast # Fast restart mode - less aggressive cleanup"
echo " $0 all all --parallel # Run all combinations in parallel by MySQL version"
echo " $0 all all --record # Record expected outputs for all combinations"
echo " $0 # Interactive mode"
echo ""
echo "Modes:"
echo " Single run: Tests run once, then cleanup (unless --no-teardown)"
echo " Watch mode: Tests run once, services stay active until Ctrl+C"
echo " No teardown: Services remain active after script completion"
echo ""
echo "All option behavior:"
echo " Single mode: Each combination runs and cleans up sequentially"
echo " Watch mode: Each combination runs in sequence, user must Ctrl+C between each"
echo " Fast mode: Skips heavy cleanup (images/cache) for much faster restarts"
echo ""
}
# Function to check for port conflicts
check_port_conflicts() {
local ports=(
"${DB_PORT_MYSQL80:-3306}"
"${DB_PORT_MYSQL84:-3307}"
"${DB_PORT_MYSQL93:-3308}"
"${DB_PORT_MYSQL96:-3309}"
"${PHPMYADMIN_PORT_MYSQL80:-18080}"
"${PHPMYADMIN_PORT_MYSQL84:-18081}"
"${PHPMYADMIN_PORT_MYSQL93:-18082}"
"${PHPMYADMIN_PORT_MYSQL96:-18083}"
)
local conflicts=()
echo "=== Checking for port conflicts ==="
for port in "${ports[@]}"; do
if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then
conflicts+=($port)
echo "⚠️ Port $port is already in use"
fi
done
if [ ${#conflicts[@]} -gt 0 ]; then
echo ""
echo "❌ Port conflicts detected on: ${conflicts[*]}"
echo "Please stop services using these ports or use different ports."
echo "You can check what's using a port with: lsof -i :PORT_NUMBER"
echo ""
return 1
else
echo "✅ No port conflicts detected"
echo ""
return 0
fi
}
# Function to check if a container is responsive
check_container_responsive() {
local service_name=$1
local container_name="dbdiff-${service_name}-1"
echo "🔍 Checking if container $container_name is responsive..."
# Check if container exists and is running
if ! docker ps --format "table {{.Names}}" | grep -q "^${container_name}$"; then
echo "❌ Container $container_name is not running"
return 1
fi
# Try a simple command to test responsiveness
if timeout 10 docker exec $container_name echo "Container is responsive" >/dev/null 2>&1; then
echo "✅ Container $container_name is responsive"
return 0
else
echo "❌ Container $container_name is not responsive"
return 1
fi
}
# Function to check if Docker is available
is_docker_available() {
# Check both Docker daemon and socket availability
docker info >/dev/null 2>&1 && [ -S /var/run/docker.sock ] 2>/dev/null
}
# Function to cleanup docker resources
cleanup_docker() {
echo "=== Cleaning up Docker resources ==="
# Check if Docker is available before attempting cleanup
if ! is_docker_available; then
echo "⚠️ Docker daemon not available - skipping cleanup"
return 0
fi
# Stop and remove containers
echo "Stopping containers..."
if $COMPOSE_CMD down --remove-orphans --volumes 2>/dev/null; then
echo "✅ Containers stopped"
else
echo "⚠️ Failed to stop containers"
fi
# Remove project containers
local containers=$(docker container ls -a --filter "name=dbdiff" --format "{{.ID}}" 2>/dev/null)
if [ -n "$containers" ]; then
echo "Removing project containers..."
echo "$containers" | xargs -r docker rm -f 2>/dev/null && echo "✅ Project containers removed"
fi
if [ "$FAST_MODE" = "true" ]; then
echo "🏃 Fast mode: Skipping image, prune, and cache cleanup"
echo "✅ Cleanup completed"
echo ""
return 0
fi
# Remove project images (including PHPMyAdmin)
local images=$(docker images --filter "reference=dbdiff*" --format "{{.ID}}" 2>/dev/null)
local phpmyadmin_images=$(docker images --filter "reference=phpmyadmin/phpmyadmin*" --format "{{.ID}}" 2>/dev/null)
if [ -n "$images" ] || [ -n "$phpmyadmin_images" ]; then
echo "Removing project images..."
if [ -n "$images" ]; then
echo "$images" | xargs -r docker rmi -f 2>/dev/null
fi
if [ -n "$phpmyadmin_images" ]; then
echo "$phpmyadmin_images" | xargs -r docker rmi -f 2>/dev/null
fi
echo "✅ Project images removed"
fi
# Clean unused resources
echo "Cleaning unused resources..."
docker system prune -a -f --volumes >/dev/null 2>&1 && echo "✅ Unused resources cleaned"
# Clean build cache
echo "Cleaning build cache..."
docker builder prune -a -f >/dev/null 2>&1 && echo "✅ Build cache cleaned"
echo "✅ Cleanup completed"
echo ""
}
# Function to show Docker disk usage
show_docker_disk_usage() {
if is_docker_available; then
echo "📊 Docker disk usage:"
docker system df 2>/dev/null || echo "Could not get Docker disk usage"
else
echo "📊 Docker unavailable - cannot show disk usage"
fi
echo ""
}
# Function to run $COMPOSE_CMD with timeout and progress monitoring
run_docker_compose_with_timeout() {
local timeout=$1
local description=$2
shift 2
local cmd=("$@")
# Check Docker availability before running command
if ! is_docker_available; then
echo "❌ Docker daemon not available"
return 1
fi
# Create a temporary file to track the process
local temp_file=$(mktemp)
local start_time=$(date +%s)
# Start the command in background and capture its PID
(
trap 'echo "Subprocess interrupted"; exit 130' INT TERM
$COMPOSE_CMD "${cmd[@]}" 2>/dev/null
echo $? > "$temp_file"
) &
local pid=$!
# Monitor progress
local elapsed=0
local check_interval=5
while kill -0 $pid 2>/dev/null; do
sleep $check_interval
elapsed=$((elapsed + check_interval))
if [ $elapsed -ge $timeout ]; then
echo "❌ Command timed out after ${timeout}s"
kill -TERM $pid 2>/dev/null || true
sleep 2
kill -9 $pid 2>/dev/null || true
rm -f "$temp_file"
return 124
fi
done
# Get the exit code
wait $pid 2>/dev/null
local exit_code=$?
if [ -f "$temp_file" ]; then
local file_exit_code=$(cat "$temp_file" 2>/dev/null)
if [ -n "$file_exit_code" ]; then
exit_code=$file_exit_code
fi
rm -f "$temp_file"
fi
local end_time=$(date +%s)
local duration=$((end_time - start_time))
if [ $exit_code -eq 0 ]; then
echo "✅ $description completed (${duration}s)"
else
echo "❌ $description failed (exit code: $exit_code, ${duration}s)"
fi
return $exit_code
}
# Function to show periodic status updates
show_progress() {
local message="$1"
local pid="$2"
local interval="${3:-10}"
local elapsed=0
while kill -0 "$pid" 2>/dev/null; do
sleep $interval
elapsed=$((elapsed + interval))
echo "⏳ $message... (${elapsed}s elapsed)"
# Show docker status every 30 seconds
if [ $((elapsed % 30)) -eq 0 ]; then
echo "📊 Current Docker status:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | head -5
fi
done
}
# Function to get appropriate $COMPOSE_CMD run flags
get_docker_run_flags() {
local flags="--rm"
# Always use non-TTY mode for better script compatibility
# Check which flag is supported by testing $COMPOSE_CMD version
if $COMPOSE_CMD run --help | grep -q "\-\-no-TTY"; then
flags="$flags --no-TTY"
elif $COMPOSE_CMD run --help | grep -q "\-T"; then
flags="$flags -T"
fi
echo "$flags"
}
# Function to test a specific PHP/MySQL combination
test_combination() {
local php_version=$1
local mysql_version=$2
local service_name="cli-php${php_version//.}-${mysql_version}"
local db_service="db-${mysql_version}"
echo "=== Testing $service_name ==="
echo "📊 PHP: $php_version | MySQL: $(convert_service_to_mysql_version "$mysql_version")"
echo "🐳 Service: $service_name | DB: $db_service"
echo ""
# Start database service first with timeout monitoring
echo "🗄️ Starting database service: $db_service"
if ! run_docker_compose_with_timeout $DATABASE_STARTUP_TIMEOUT "Database startup" up -d --build $db_service; then
echo "❌ Failed to start database service $db_service"
return 1
fi
echo ""
echo "⏳ Waiting for database to become healthy..."
local timeout=$DATABASE_HEALTH_TIMEOUT
local elapsed=0
local interval=5
while [ $elapsed -lt $timeout ]; do
if $COMPOSE_CMD ps --format json $db_service | grep -q '"Health":"healthy"'; then
echo "✅ Database $db_service is healthy!"
break
elif $COMPOSE_CMD ps --format json $db_service | grep -q '"Health":"unhealthy"'; then
echo "❌ Database $db_service is unhealthy. Checking logs..."
$COMPOSE_CMD logs --tail=20 $db_service
return 1
fi
sleep $interval
elapsed=$((elapsed + interval))
done
if [ $elapsed -ge $timeout ]; then
echo "❌ Database $db_service failed to become healthy within ${timeout}s"
$COMPOSE_CMD logs --tail=30 $db_service
return 1
fi
# Start PHPMyAdmin service for this database
local phpmyadmin_service="phpmyadmin-${mysql_version}"
echo ""
echo "🌐 Starting PHPMyAdmin service: $phpmyadmin_service"
if ! run_docker_compose_with_timeout 120 "PHPMyAdmin service startup" up -d $phpmyadmin_service; then
echo "⚠️ Failed to start PHPMyAdmin service $phpmyadmin_service (non-critical, continuing with tests)"
else
echo "✅ PHPMyAdmin started successfully"
fi
# Build CLI service if needed
echo ""
echo "🔨 Building CLI service: $service_name"
if ! run_docker_compose_with_timeout $CLI_BUILD_TIMEOUT "CLI service build" build $service_name; then
echo "❌ Failed to build CLI service $service_name"
return 1
fi
# Test PHP version info
echo ""
echo "📋 Getting PHP version..."
local run_flags=$(get_docker_run_flags)
if run_docker_compose_with_timeout $PHP_VERSION_CHECK_TIMEOUT "PHP version check" run $run_flags --entrypoint="" $service_name php --version; then
echo "✅ PHP version check successful"
else
echo "❌ Failed to get PHP version for $service_name"
return 1
fi
echo ""
echo "🧪 Running PHPUnit Tests..."
# Run PHPUnit with timeout and progress monitoring
local test_start_time=$(date +%s)
local phpunit_cmd="./scripts/run-tests.sh"
# Run in record mode if flag is set
local env_vars=""
if [ "$RECORD_MODE" = "true" ]; then
env_vars="-e DBDIFF_RECORD_MODE=true"
fi
if run_docker_compose_with_timeout $PHPUNIT_TEST_TIMEOUT "PHPUnit test execution" run $run_flags $env_vars --entrypoint="" $service_name $phpunit_cmd; then
local test_end_time=$(date +%s)
local test_duration=$((test_end_time - test_start_time))
echo "✅ All tests PASSED for $service_name (${test_duration}s)"
else
local exit_code=$?
local test_end_time=$(date +%s)
local test_duration=$((test_end_time - test_start_time))
if [ $exit_code -eq 124 ]; then
echo "❌ PHPUnit tests TIMED OUT for $service_name after ${test_duration}s (${PHPUNIT_TEST_TIMEOUT} second limit)"
else
echo "❌ Some tests FAILED for $service_name after ${test_duration}s (exit code: $exit_code)"
fi
# Show container logs for debugging
echo "--- Database Logs ---"
$COMPOSE_CMD logs --tail=20 $db_service
echo "--- End Logs ---"
return 1
fi
echo ""
echo "✅ Test completed successfully for $service_name"
# Only cleanup if not in watch mode and not in no-teardown mode
if [ "$WATCH_MODE" != "true" ] && [ "$NO_TEARDOWN" != "true" ]; then
echo "🧹 Cleaning up after test..."
if is_docker_available; then
# Stop all containers
$COMPOSE_CMD down --remove-orphans --volumes >/dev/null 2>&1
# Remove project containers
docker container ls -a --filter "name=dbdiff" --format "{{.ID}}" | xargs -r docker rm -f >/dev/null 2>&1
# Remove project images
docker images --filter "reference=dbdiff*" --format "{{.ID}}" | xargs -r docker rmi -f >/dev/null 2>&1
# Clean unused resources
docker system prune -a -f --volumes >/dev/null 2>&1
# Clean build cache
docker builder prune -a -f >/dev/null 2>&1
echo "✅ Cleanup completed"
else
echo "⚠️ Docker unavailable - skipping cleanup"
fi
elif [ "$NO_TEARDOWN" = "true" ]; then
echo "🔄 No teardown mode: Keeping containers running..."
else
echo "🔄 Watch mode: Keeping containers running for next test..."
fi
echo "--------------------------------------"
echo ""
}
# Function to force clean everything before starting
force_cleanup_before_start() {
echo "🧹 Performing initial cleanup to ensure clean state..."
if [ "$FAST_MODE" = "true" ]; then
echo "🏃 Fast mode: Skipping heavy Docker cleanup before start"
return 0
fi
# Check if Docker is available before attempting cleanup
if ! is_docker_available; then
echo "⚠️ Docker is not available - skipping Docker cleanup, only killing processes"
pkill -9 -f "docker-compose" 2>/dev/null || true
pkill -9 -f "docker.*build" 2>/dev/null || true
echo "✅ Process cleanup completed (Docker unavailable)"
return 0
fi
# Kill any existing $COMPOSE_CMD processes
echo "Killing existing $COMPOSE_CMD processes..."
pkill -9 -f "docker-compose" 2>/dev/null || true
pkill -9 -f "docker.*build" 2>/dev/null || true
# Stop all containers related to this project
echo "Stopping project containers..."
$COMPOSE_CMD down --remove-orphans --volumes 2>/dev/null || true
# Remove any hanging containers
echo "Removing hanging containers..."
docker container ls -a --filter "name=dbdiff" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
# Remove all project images to start fresh
echo "Removing project images..."
docker images --filter "reference=dbdiff*" --format "{{.ID}}" | xargs -r docker rmi -f 2>/dev/null || true
docker images --filter "reference=phpmyadmin/phpmyadmin*" --format "{{.ID}}" | xargs -r docker rmi -f 2>/dev/null || true
# Clean up all unused resources aggressively
echo "Cleaning all unused Docker resources..."
docker system prune -a -f --volumes 2>/dev/null || true
# Clean up build cache to avoid issues
echo "Cleaning build cache..."
docker builder prune -a -f 2>/dev/null || true
# Show disk usage after cleanup
echo "💾 Disk usage after cleanup:"
show_docker_disk_usage
echo "✅ Initial cleanup completed"
echo ""
}
# Function to run tests in watch mode
run_watch_mode() {
local php_arg=$1
local mysql_arg=$2
# Check if this is a multiple combination scenario
if [ "$php_arg" = "all" ] || [ "$mysql_arg" = "all" ]; then
run_watch_mode_all $php_arg $mysql_arg
return
fi
# Single combination watch mode
echo "🔄 Watch mode enabled - running tests once, then keeping containers active"
echo "Press Ctrl+C to exit and cleanup"
echo ""
echo "🔄 Running tests - $(date)"
echo "======================================"
# Test the specific combination
test_combination $php_arg $mysql_arg
echo "✅ Initial test run completed"
echo ""
# Start additional services for manual use
start_additional_services_for_watch $php_arg $mysql_arg
echo ""
echo "🐳 Containers and resources are now active and ready for manual use"
echo ""
# Show running services and their URLs
show_running_services
echo "⏳ Waiting for Ctrl+C to cleanup and exit..."
echo "💡 Tip: Keep this terminal open to see service URLs, use another terminal for manual testing"
echo ""
# Wait indefinitely until interrupt
while true; do
sleep 60
done
}
# Function to run multiple combinations in watch mode (one at a time)
run_watch_mode_all() {
local php_arg=$1
local mysql_arg=$2
local combinations=()
# Build list of combinations to test
if [ "$php_arg" = "all" ] && [ "$mysql_arg" = "all" ]; then
# All combinations
for php_version in "${PHP_VERSIONS[@]}"; do
for mysql_version in "${MYSQL_VERSIONS[@]}"; do
combinations+=("$php_version:$mysql_version")
done
done
elif [ "$php_arg" = "all" ]; then
# All PHP versions with specific MySQL
for php_version in "${PHP_VERSIONS[@]}"; do
combinations+=("$php_version:$mysql_arg")
done
elif [ "$mysql_arg" = "all" ]; then
# Specific PHP with all MySQL versions
for mysql_version in "${MYSQL_VERSIONS[@]}"; do
combinations+=("$php_arg:$mysql_version")
done
fi
echo "🔄 Watch mode with multiple combinations enabled"
echo "📋 Will test ${#combinations[@]} combinations sequentially"
echo "⏭️ Press Ctrl+C to move to next combination or exit if on last one"
echo ""
for i in "${!combinations[@]}"; do
local combination="${combinations[$i]}"
local php_version="${combination%:*}"
local mysql_version="${combination#*:}"
local current=$((i + 1))
local total=${#combinations[@]}
echo "🔄 Combination $current/$total: PHP $php_version + MySQL $mysql_version"
echo "==============================================================="
# Run the test combination
test_combination $php_version $mysql_version
if [ $? -ne 0 ]; then
echo "❌ Test failed for combination $current/$total"
read -p "Continue to next combination? (y/N): " continue_choice
if [[ ! "$continue_choice" =~ ^[Yy]$ ]]; then
echo "Stopping at user request"
break
fi
fi
# Start additional services for manual use
start_additional_services_for_watch $php_version $mysql_version
echo ""
echo "🐳 Combination $current/$total is now active and ready for manual use"
echo ""
# Show running services and their URLs
show_running_services
if [ $current -lt $total ]; then
echo "⏭️ Press Ctrl+C to move to next combination: PHP ${combinations[$((i+1))]%:*} + MySQL ${combinations[$((i+1))]#*:}"
else
echo "🏁 This is the last combination. Press Ctrl+C to exit and cleanup."
fi
echo "💡 Tip: Use another terminal for manual testing"
echo ""
# Wait for interrupt to move to next combination
local interrupted=false
trap 'interrupted=true' INT
while [ "$interrupted" = "false" ]; do
sleep 1
done
trap cleanup_on_interrupt INT
echo ""
echo "🧹 Moving to next combination - cleaning up current services..."
# Clean up current combination unless it's the last one and NO_TEARDOWN is set
if [ $current -lt $total ] || [ "$NO_TEARDOWN" != "true" ]; then
cleanup_docker
fi
echo ""
# Break if this was the last combination
if [ $current -ge $total ]; then
echo "🏁 All combinations completed!"
break
fi
done
}
# Function to start additional services for watch mode (CLI and PHPMyAdmin)
start_additional_services_for_watch() {
local php_version=$1
local mysql_version=$2
local cli_service="cli-php${php_version//.}-${mysql_version}"
local phpmyadmin_service="phpmyadmin-${mysql_version}"
echo "🚀 Starting additional services for manual use..."
# Start CLI service
echo "Starting CLI service: $cli_service"
if ! run_docker_compose_with_timeout 120 "CLI service startup" up -d $cli_service; then
echo "⚠️ Failed to start CLI service $cli_service (non-critical)"
fi
# Start PHPMyAdmin service
echo "Starting PHPMyAdmin service: $phpmyadmin_service"
if ! run_docker_compose_with_timeout 120 "PHPMyAdmin service startup" up -d $phpmyadmin_service; then
echo "⚠️ Failed to start PHPMyAdmin service $phpmyadmin_service (non-critical)"
fi
echo "✅ Additional services started"
}
# Function to show running services with their localhost URLs
show_running_services() {
echo "🌐 Running Services & Access URLs:"
echo "================================="
# Check if any containers are running
local running_containers=$($COMPOSE_CMD ps --format table 2>/dev/null | grep -v "Name\|----" | grep -E "(Up|running)" || true)
if [ -z "$running_containers" ]; then
echo "ℹ️ No containers are currently running"
return
fi
# Get list of running service names
local running_services=$($COMPOSE_CMD ps --services --filter status=running 2>/dev/null || true)
# Database services
echo "📊 Database Services:"
if echo "$running_services" | grep -q "db-mysql80"; then
echo " • MySQL ${MYSQL_VERSION_80:-8.0}: mysql://$DB_USER:$DB_PASSWORD@localhost:${DB_PORT_MYSQL80:-3306}/$DB_NAME"
echo " Root access: mysql://root:$DB_ROOT_PASSWORD@localhost:${DB_PORT_MYSQL80:-3306}/$DB_NAME"
fi
if echo "$running_services" | grep -q "db-mysql84"; then
echo " • MySQL ${MYSQL_VERSION_84:-8.4}: mysql://$DB_USER:$DB_PASSWORD@localhost:${DB_PORT_MYSQL84:-3307}/$DB_NAME"
echo " Root access: mysql://root:$DB_ROOT_PASSWORD@localhost:${DB_PORT_MYSQL84:-3307}/$DB_NAME"
fi
if echo "$running_services" | grep -q "db-mysql93"; then
echo " • MySQL ${MYSQL_VERSION_93:-9.3}: mysql://$DB_USER:$DB_PASSWORD@localhost:${DB_PORT_MYSQL93:-3308}/$DB_NAME"
echo " Root access: mysql://root:$DB_ROOT_PASSWORD@localhost:${DB_PORT_MYSQL93:-3308}/$DB_NAME"
fi
if echo "$running_services" | grep -q "db-mysql96"; then
echo " • MySQL ${MYSQL_VERSION_96:-9.6}: mysql://$DB_USER:$DB_PASSWORD@localhost:${DB_PORT_MYSQL96:-3309}/$DB_NAME"
echo " Root access: mysql://root:$DB_ROOT_PASSWORD@localhost:${DB_PORT_MYSQL96:-3309}/$DB_NAME"
fi
# Only show database section if we have any DB services running
if echo "$running_services" | grep -q "db-mysql"; then
echo ""
fi
# PHPMyAdmin services
local has_phpmyadmin=false
echo "🔧 PHPMyAdmin Web Interfaces:"
if echo "$running_services" | grep -q "phpmyadmin-mysql80"; then
echo " • MySQL ${MYSQL_VERSION_80:-8.0}: http://localhost:${PHPMYADMIN_PORT_MYSQL80:-18080}"
has_phpmyadmin=true
fi
if echo "$running_services" | grep -q "phpmyadmin-mysql84"; then
echo " • MySQL ${MYSQL_VERSION_84:-8.4}: http://localhost:${PHPMYADMIN_PORT_MYSQL84:-18081}"
has_phpmyadmin=true
fi
if echo "$running_services" | grep -q "phpmyadmin-mysql93"; then
echo " • MySQL ${MYSQL_VERSION_93:-9.3}: http://localhost:${PHPMYADMIN_PORT_MYSQL93:-18082}"
has_phpmyadmin=true
fi
if [ "$has_phpmyadmin" = "false" ]; then
echo " (No PHPMyAdmin services currently running)"
fi
echo ""
# CLI services
local has_cli=false
echo "💻 PHP CLI Services (use $COMPOSE_CMD exec):"
if echo "$running_services" | grep -q "cli-php81-mysql80"; then
echo " • php81-mysql80: $COMPOSE_CMD exec cli-php81-mysql80 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php81-mysql84"; then
echo " • php81-mysql84: $COMPOSE_CMD exec cli-php81-mysql84 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php81-mysql93"; then
echo " • php81-mysql93: $COMPOSE_CMD exec cli-php81-mysql93 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php81-mysql96"; then
echo " • php81-mysql96: $COMPOSE_CMD exec cli-php81-mysql96 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php82-mysql80"; then
echo " • php82-mysql80: $COMPOSE_CMD exec cli-php82-mysql80 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php82-mysql84"; then
echo " • php82-mysql84: $COMPOSE_CMD exec cli-php82-mysql84 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php82-mysql93"; then
echo " • php82-mysql93: $COMPOSE_CMD exec cli-php82-mysql93 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php82-mysql96"; then
echo " • php82-mysql96: $COMPOSE_CMD exec cli-php82-mysql96 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php83-mysql80"; then
echo " • php83-mysql80: $COMPOSE_CMD exec cli-php83-mysql80 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php83-mysql84"; then
echo " • php83-mysql84: $COMPOSE_CMD exec cli-php83-mysql84 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php83-mysql93"; then
echo " • php83-mysql93: $COMPOSE_CMD exec cli-php83-mysql93 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php83-mysql96"; then
echo " • php83-mysql96: $COMPOSE_CMD exec cli-php83-mysql96 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php84-mysql80"; then
echo " • php84-mysql80: $COMPOSE_CMD exec cli-php84-mysql80 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php84-mysql84"; then
echo " • php84-mysql84: $COMPOSE_CMD exec cli-php84-mysql84 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php84-mysql93"; then
echo " • php84-mysql93: $COMPOSE_CMD exec cli-php84-mysql93 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php84-mysql96"; then
echo " • php84-mysql96: $COMPOSE_CMD exec cli-php84-mysql96 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php85-mysql80"; then
echo " • php85-mysql80: $COMPOSE_CMD exec cli-php85-mysql80 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php85-mysql84"; then
echo " • php85-mysql84: $COMPOSE_CMD exec cli-php85-mysql84 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php85-mysql93"; then
echo " • php85-mysql93: $COMPOSE_CMD exec cli-php85-mysql93 bash"
has_cli=true
fi
if echo "$running_services" | grep -q "cli-php85-mysql96"; then
echo " • php85-mysql96: $COMPOSE_CMD exec cli-php85-mysql96 bash"
has_cli=true
fi
if [ "$has_cli" = "false" ]; then
echo " (No CLI services currently running)"
fi
echo ""
# Show actual running containers
echo "🐳 Currently Running Containers:"
$COMPOSE_CMD ps --format table 2>/dev/null | grep -E "(Name|Up|running|----)" || echo "Unable to fetch container status"
echo ""
}
# Function to test signal handling (for debugging)
test_signal_handling() {
echo "🧪 Testing signal handling..."
echo "Press Ctrl+C within 10 seconds to test interrupt handling..."
for i in {1..10}; do
echo "Waiting... $i/10"
sleep 1
done
echo "✅ Signal handling test completed"
}
# Function to start a watchdog that helps with stuck processes
start_watchdog() {
(
sleep 60 # Wait 60 seconds
while true; do
# Check if any $COMPOSE_CMD run commands have been running too long
local long_running=$(ps aux | grep "$COMPOSE_CMD.*run" | grep -v grep | awk '$10 > 120 {print $2}')
if [ -n "$long_running" ]; then
echo ""
echo "⚠️ WATCHDOG: Detected long-running $COMPOSE_CMD processes"
echo "PIDs: $long_running"
echo "💡 If the script seems stuck, press Ctrl+C or run: kill -9 $long_running"
echo ""
fi
sleep 30
done
) &
echo $! > /tmp/test_runner_watchdog.pid
}
# Function to stop the watchdog
stop_watchdog() {
if [ -f /tmp/test_runner_watchdog.pid ]; then
local watchdog_pid=$(cat /tmp/test_runner_watchdog.pid)
kill $watchdog_pid 2>/dev/null || true
rm -f /tmp/test_runner_watchdog.pid
fi
}
# Function to debug $COMPOSE_CMD run issues
debug_docker_run() {
local service_name=$1
echo "🔍 Debugging $COMPOSE_CMD run for service: $service_name"
echo "Docker-compose version:"
$COMPOSE_CMD --version
echo ""
echo "Available $COMPOSE_CMD run flags:"
$COMPOSE_CMD run --help | grep -E "(-T|--no-TTY|--no-tty)" || echo "No TTY flags found"