diff --git a/src/common/etc/entrypoint.d/0-container-info.sh b/src/common/etc/entrypoint.d/0-container-info.sh index a71de57c..b92ee2bd 100644 --- a/src/common/etc/entrypoint.d/0-container-info.sh +++ b/src/common/etc/entrypoint.d/0-container-info.sh @@ -55,7 +55,8 @@ Brought to you by serversideup.net â€ĸ Upload Limit: '"$UPLOAD_LIMIT"' 🔄 Runtime -â€ĸ Docker CMD: '"$DOCKER_CMD"' +â€ĸ Automations: '"$AUTORUN_ENABLED"' +â€ĸ Docker CMD: '"$DOCKER_CMD"' ' if [ "$PHP_OPCACHE_STATUS" = "0" ]; then diff --git a/src/common/etc/entrypoint.d/50-laravel-automations.sh b/src/common/etc/entrypoint.d/50-laravel-automations.sh index 1e86e034..3634c0a1 100644 --- a/src/common/etc/entrypoint.d/50-laravel-automations.sh +++ b/src/common/etc/entrypoint.d/50-laravel-automations.sh @@ -53,10 +53,6 @@ fi ############################################################################ artisan_migrate() { - migrate_flags="" - - debug_log "Starting migrations (isolation: $AUTORUN_LARAVEL_MIGRATION_ISOLATION)" - echo "🚀 Clearing Laravel cache before attempting migrations..." php "$APP_BASE_DIR/artisan" config:clear @@ -73,7 +69,8 @@ artisan_migrate() { ;; esac - # Build migration flags (used for all databases) + # Determine if isolation is intended to be used + isolation_enabled="false" if [ "$AUTORUN_LARAVEL_MIGRATION_ISOLATION" = "true" ]; then # Isolation only works in default mode if [ "$AUTORUN_LARAVEL_MIGRATION_MODE" != "default" ]; then @@ -82,14 +79,18 @@ artisan_migrate() { fi # Isolation requires Laravel 9.38.0+ - if ! laravel_version_is_at_least "9.38.0"; then - echo "❌ $script_name: Isolated migrations require Laravel v9.38.0 or above. Detected version: $(get_laravel_version)" - return 1 + if laravel_version_is_at_least "9.38.0"; then + isolation_enabled="true" + debug_log "Isolation mode enabled (Laravel version check passed)" + else + echo "âš ī¸ $script_name: Isolated migrations require Laravel v9.38.0 or above. Detected version: $(get_laravel_version)" + echo " Continuing without isolation mode..." fi - - migrate_flags="$migrate_flags --isolated" fi + # Start assembling migration flags + migrate_flags="" + if [ "$AUTORUN_LARAVEL_MIGRATION_FORCE" = "true" ]; then migrate_flags="$migrate_flags --force" fi @@ -98,30 +99,55 @@ artisan_migrate() { migrate_flags="$migrate_flags --seed" fi - # Determine if multiple databases are specified + # Helper function to run migrations for a specific database + run_migration_for_db() { + db_name="${1:-}" + + # Build display name and database flag for messages/commands + if [ -n "$db_name" ]; then + db_display_name="'$db_name'" + db_flag="--database=$db_name" + else + db_display_name="default database" + db_flag="" + fi + + # Wait for database connection + if ! wait_for_database_connection $db_name; then + echo "❌ $script_name: Failed to connect to $db_display_name" + return 1 + fi + + # Determine if --isolated can be used for this database + db_migrate_flags="$migrate_flags" + if [ "$isolation_enabled" = "true" ]; then + if db_has_migrations_table $db_name; then + db_migrate_flags="$db_migrate_flags --isolated" + debug_log "Using --isolated flag for $db_display_name" + else + echo "â„šī¸ Skipping --isolated flag for $db_display_name: migrations table not ready (normal for first deployment)" + echo " The --isolated flag will be used on subsequent deployments." + fi + fi + + echo "🚀 Running migrations for $db_display_name" + php "$APP_BASE_DIR/artisan" $migration_command $db_flag $db_migrate_flags + } + + # Run migrations for specified database(s) if [ -n "$AUTORUN_LARAVEL_MIGRATION_DATABASE" ]; then databases=$(convert_comma_delimited_to_space_separated "$AUTORUN_LARAVEL_MIGRATION_DATABASE") database_list=$(echo "$databases" | tr ',' ' ') for db in $database_list; do - # Wait for this specific database to be ready - if ! wait_for_database_connection "$db"; then - echo "❌ $script_name: Failed to connect to database: $db" + if ! run_migration_for_db "$db"; then return 1 fi - - echo "🚀 Running migrations for database: $db" - php "$APP_BASE_DIR/artisan" $migration_command --database=$db $migrate_flags done else - # Wait for default database connection - if ! wait_for_database_connection; then - echo "❌ $script_name: Failed to connect to default database" + if ! run_migration_for_db; then return 1 fi - - # Run migration with default database connection - php "$APP_BASE_DIR/artisan" $migration_command $migrate_flags fi } @@ -241,17 +267,16 @@ get_laravel_version() { fi debug_log "Detecting Laravel version..." - # Use 2>/dev/null to handle potential PHP warnings - artisan_version_output=$(php "$APP_BASE_DIR/artisan" --version 2>/dev/null) - # Check if command was successful - if [ $? -ne 0 ]; then + # Capture artisan output + if ! artisan_version_output=$(php "$APP_BASE_DIR/artisan" --version 2>/dev/null); then echo "❌ $script_name: Failed to execute artisan command" >&2 return 1 fi + debug_log "Raw artisan output: $artisan_version_output" + # Extract version number using sed (POSIX compliant) - # Using a more strict pattern that matches "Laravel Framework X.Y.Z" laravel_version=$(echo "$artisan_version_output" | sed -e 's/^Laravel Framework \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/') # Validate that we got a version number (POSIX compliant regex) @@ -261,7 +286,7 @@ get_laravel_version() { echo "$laravel_version" return 0 else - echo "❌ $script_name: Failed to determine Laravel version" >&2 + echo "❌ $script_name: Failed to determine Laravel version from: $artisan_version_output" >&2 return 1 fi } @@ -286,33 +311,55 @@ laravel_version_is_at_least() { return 1 fi - # Validate required version format - if ! echo "$required_version" | grep -Eq '^[0-9]+\.[0-9]+(\.[0-9]+)?$'; then - echo "❌ $script_name - Invalid version requirement format: $required_version" >&2 - return 1 - fi - current_version=$(get_laravel_version) if [ $? -ne 0 ]; then echo "❌ $script_name: Failed to get Laravel version" >&2 return 1 fi - # normalize_version() takes a version string and ensures it has 3 parts - normalize_version() { - echo "$1" | awk -F. '{ print $1"."$2"."(NF>2?$3:0) }' - } + # Extract version components using cut (POSIX compliant) + cur_major=$(echo "$current_version" | cut -d. -f1) + cur_minor=$(echo "$current_version" | cut -d. -f2) + cur_patch=$(echo "$current_version" | cut -d. -f3) + + req_major=$(echo "$required_version" | cut -d. -f1) + req_minor=$(echo "$required_version" | cut -d. -f2) + req_patch=$(echo "$required_version" | cut -d. -f3) - normalized_current=$(normalize_version "$current_version") - normalized_required=$(normalize_version "$required_version") + # Default patch to 0 if not specified + : "${cur_patch:=0}" + : "${req_patch:=0}" - # Use sort -V to get the lower version, then compare it with required version - # This works in BusyBox because we only need to check the first line of output - lowest_version=$(printf '%s\n%s\n' "$normalized_required" "$normalized_current" | sort -V | head -n1) - if [ "$lowest_version" = "$normalized_required" ]; then - return 0 # Success: current version is >= required version + # Numeric comparison (POSIX arithmetic expansion handles this correctly) + # Compare major version + if [ "$cur_major" -gt "$req_major" ]; then + return 0 + elif [ "$cur_major" -lt "$req_major" ]; then + return 1 + fi + + # Major versions equal, compare minor + if [ "$cur_minor" -gt "$req_minor" ]; then + return 0 + elif [ "$cur_minor" -lt "$req_minor" ]; then + return 1 + fi + + # Minor versions equal, compare patch + if [ "$cur_patch" -ge "$req_patch" ]; then + return 0 + fi + + return 1 +} + +db_has_migrations_table() { + database_arg="${1:-}" + + if [ -n "$database_arg" ]; then + php "$APP_BASE_DIR/artisan" migrate:status --database="$database_arg" > /dev/null 2>&1 else - return 1 # Failure: current version is < required version + php "$APP_BASE_DIR/artisan" migrate:status > /dev/null 2>&1 fi } @@ -324,9 +371,9 @@ test_db_connection() { # Pass database connection name only if specified (not empty) database_arg="${1:-}" if [ -n "$database_arg" ]; then - php "$AUTORUN_LIB_DIR/laravel/test-db-connection.php" "$APP_BASE_DIR" "$AUTORUN_LARAVEL_MIGRATION_MODE" "$AUTORUN_LARAVEL_MIGRATION_ISOLATION" "$database_arg" + php "$AUTORUN_LIB_DIR/laravel/test-db-connection.php" "$APP_BASE_DIR" "$AUTORUN_LARAVEL_MIGRATION_MODE" "$database_arg" else - php "$AUTORUN_LIB_DIR/laravel/test-db-connection.php" "$APP_BASE_DIR" "$AUTORUN_LARAVEL_MIGRATION_MODE" "$AUTORUN_LARAVEL_MIGRATION_ISOLATION" + php "$AUTORUN_LIB_DIR/laravel/test-db-connection.php" "$APP_BASE_DIR" "$AUTORUN_LARAVEL_MIGRATION_MODE" fi } diff --git a/src/common/etc/entrypoint.d/lib/laravel/test-db-connection.php b/src/common/etc/entrypoint.d/lib/laravel/test-db-connection.php index e4817fd3..0c0b5225 100644 --- a/src/common/etc/entrypoint.d/lib/laravel/test-db-connection.php +++ b/src/common/etc/entrypoint.d/lib/laravel/test-db-connection.php @@ -5,12 +5,11 @@ * This script tests if the Laravel application can connect to its configured database. * It's designed to be called from shell scripts during container initialization. * - * Usage: php test-db-connection.php /path/to/app/base/dir [migration_mode] [migration_isolation] [database_connection] + * Usage: php test-db-connection.php /path/to/app/base/dir [migration_mode] [database_connection] * * Arguments: - * app_base_dir - Path to Laravel application root - * migration_mode - Migration mode: 'default', 'fresh', or 'refresh' (optional, defaults to 'default') - * migration_isolation - Whether to run migrations in isolation (optional, defaults to 'false') + * app_base_dir - Path to Laravel application root + * migration_mode - Migration mode: 'default', 'fresh', or 'refresh' (optional, defaults to 'default') * database_connection - Name of the database connection to test (optional, defaults to 'default') * * Exit codes: @@ -21,15 +20,14 @@ */ // Validate arguments -if ($argc < 2 || $argc > 5) { - fwrite(STDERR, "Usage: php test-db-connection.php /path/to/app/base/dir [migration_mode] [migration_isolation] [database_connection]\n"); +if ($argc < 2 || $argc > 4) { + fwrite(STDERR, "Usage: php test-db-connection.php /path/to/app/base/dir [migration_mode] [database_connection]\n"); exit(1); } $appBaseDir = $argv[1]; $migrationMode = $argc >= 3 ? $argv[2] : 'default'; -$migrationIsolation = $argc >= 4 ? $argv[3] : 'false'; -$databaseConnection = $argc >= 5 ? $argv[4] : null; +$databaseConnection = $argc >= 4 ? $argv[3] : null; // Validate migration mode $validModes = ['default', 'fresh', 'refresh']; @@ -38,13 +36,6 @@ exit(1); } -// Validate migration isolation -$validIsolations = ['true', 'false']; -if (!in_array($migrationIsolation, $validIsolations)) { - fwrite(STDERR, "Error: Invalid migration isolation '{$migrationIsolation}'. Must be one of: " . implode(', ', $validIsolations) . "\n"); - exit(1); -} - // Validate that the app base directory exists if (!is_dir($appBaseDir)) { fwrite(STDERR, "Error: App base directory does not exist: {$appBaseDir}\n"); @@ -126,17 +117,7 @@ exit(1); } - // For isolated migrations, the database file must exist (even in default mode) - if ($migrationIsolation === 'true') { - fwrite(STDERR, "SQLite database file does not exist: {$dbPath}\n"); - fwrite(STDERR, "Isolated migrations require the database file to exist before running.\n"); - fwrite(STDERR, "Either:\n"); - fwrite(STDERR, " 1. Create the database (ensure it has read and write permissions for your user): touch {$dbPath}\n"); - fwrite(STDERR, " 2. Set AUTORUN_LARAVEL_MIGRATION_ISOLATION=false to let migrations create it\n"); - exit(1); - } - - // Directory exists and is writable - migrations can create the database file (default mode only) + // Directory exists and is writable - migrations can create the database file fwrite(STDOUT, "SQLite database directory is ready - migrations will create database\n"); exit(0); }