From bc165f197653d4b981001b280ec9aba3d307f6b7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:54:23 +0200 Subject: [PATCH] fix(postgres): preserve Coolify image tag during upgrade Keep the running Coolify image tag when restarting the stack after PostgreSQL upgrade or rollback so compose does not fall back to latest. --- other/nightly/upgrade-postgres.sh | 30 ++++++++- scripts/upgrade-postgres.sh | 30 ++++++++- tests/Feature/PostgresUpgradeScriptTest.php | 71 +++++++++++++++++++++ 3 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 tests/Feature/PostgresUpgradeScriptTest.php diff --git a/other/nightly/upgrade-postgres.sh b/other/nightly/upgrade-postgres.sh index e9e12a5fe..09065875e 100755 --- a/other/nightly/upgrade-postgres.sh +++ b/other/nightly/upgrade-postgres.sh @@ -136,6 +136,20 @@ current_postgres_image() { docker inspect coolify-db --format '{{.Config.Image}}' 2>/dev/null } +current_coolify_image_tag() { + local image + local image_without_digest + local last_segment + + image=$(docker inspect coolify --format '{{.Config.Image}}' 2>/dev/null || true) + image_without_digest="${image%@*}" + last_segment="${image_without_digest##*/}" + + if [[ "$last_segment" == *:* ]]; then + printf '%s' "${last_segment##*:}" + fi +} + write_override_file() { local image="$1" local volume="$2" @@ -175,11 +189,12 @@ EOF } start_stack() { + local coolify_image_tag="${1:-${LATEST_IMAGE:-latest}}" local files files=$(compose_files) # shellcheck disable=SC2086 - LATEST_IMAGE="${LATEST_IMAGE:-latest}" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120 + LATEST_IMAGE="$coolify_image_tag" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120 } print_rollback_instructions() { @@ -220,10 +235,13 @@ rollback_postgres() { [ -n "${PREVIOUS_MOUNT_PATH:-}" ] || fail "Rollback metadata is missing PREVIOUS_MOUNT_PATH." [ -n "${PREVIOUS_OVERRIDE_PRESENT:-}" ] || fail "Rollback metadata is missing PREVIOUS_OVERRIDE_PRESENT." + CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag) + log "Rolling back Coolify internal PostgreSQL." log "Previous image: ${PREVIOUS_IMAGE}" log "Previous volume: ${PREVIOUS_VOLUME}" log "Previous mount path: ${PREVIOUS_MOUNT_PATH}" + log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}" docker volume inspect "$PREVIOUS_VOLUME" >/dev/null 2>&1 || fail "Previous volume '${PREVIOUS_VOLUME}' does not exist." @@ -242,7 +260,7 @@ rollback_postgres() { fi log "Starting Coolify stack with rollback database volume." - start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}." + start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}." log "Rollback completed successfully." cat <>"$LOGFILE" 2>&1 || true log "Starting Coolify stack with PostgreSQL ${TARGET_MAJOR}." - start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}." + start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}." log "Coolify internal PostgreSQL upgrade completed successfully." print_rollback_instructions } +if [ "${COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY:-false}" = "true" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then + return 0 +fi + case "$COMMAND" in rollback) rollback_postgres diff --git a/scripts/upgrade-postgres.sh b/scripts/upgrade-postgres.sh index e9e12a5fe..09065875e 100755 --- a/scripts/upgrade-postgres.sh +++ b/scripts/upgrade-postgres.sh @@ -136,6 +136,20 @@ current_postgres_image() { docker inspect coolify-db --format '{{.Config.Image}}' 2>/dev/null } +current_coolify_image_tag() { + local image + local image_without_digest + local last_segment + + image=$(docker inspect coolify --format '{{.Config.Image}}' 2>/dev/null || true) + image_without_digest="${image%@*}" + last_segment="${image_without_digest##*/}" + + if [[ "$last_segment" == *:* ]]; then + printf '%s' "${last_segment##*:}" + fi +} + write_override_file() { local image="$1" local volume="$2" @@ -175,11 +189,12 @@ EOF } start_stack() { + local coolify_image_tag="${1:-${LATEST_IMAGE:-latest}}" local files files=$(compose_files) # shellcheck disable=SC2086 - LATEST_IMAGE="${LATEST_IMAGE:-latest}" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120 + LATEST_IMAGE="$coolify_image_tag" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120 } print_rollback_instructions() { @@ -220,10 +235,13 @@ rollback_postgres() { [ -n "${PREVIOUS_MOUNT_PATH:-}" ] || fail "Rollback metadata is missing PREVIOUS_MOUNT_PATH." [ -n "${PREVIOUS_OVERRIDE_PRESENT:-}" ] || fail "Rollback metadata is missing PREVIOUS_OVERRIDE_PRESENT." + CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag) + log "Rolling back Coolify internal PostgreSQL." log "Previous image: ${PREVIOUS_IMAGE}" log "Previous volume: ${PREVIOUS_VOLUME}" log "Previous mount path: ${PREVIOUS_MOUNT_PATH}" + log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}" docker volume inspect "$PREVIOUS_VOLUME" >/dev/null 2>&1 || fail "Previous volume '${PREVIOUS_VOLUME}' does not exist." @@ -242,7 +260,7 @@ rollback_postgres() { fi log "Starting Coolify stack with rollback database volume." - start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}." + start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}." log "Rollback completed successfully." cat <>"$LOGFILE" 2>&1 || true log "Starting Coolify stack with PostgreSQL ${TARGET_MAJOR}." - start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}." + start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}." log "Coolify internal PostgreSQL upgrade completed successfully." print_rollback_instructions } +if [ "${COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY:-false}" = "true" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then + return 0 +fi + case "$COMMAND" in rollback) rollback_postgres diff --git a/tests/Feature/PostgresUpgradeScriptTest.php b/tests/Feature/PostgresUpgradeScriptTest.php new file mode 100644 index 000000000..bdb6f7c4e --- /dev/null +++ b/tests/Feature/PostgresUpgradeScriptTest.php @@ -0,0 +1,71 @@ + "${MOCK_LATEST_IMAGE_OUTPUT}" + exit 0 +fi + +exit 1 +BASH); + chmod($binDirectory.'/docker', 0755); + + $outputFile = $workingDirectory.'/latest-image.txt'; + $command = sprintf( + 'PATH=%s:$PATH MOCK_LATEST_IMAGE_OUTPUT=%s COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY=true bash -c %s 2>&1', + escapeshellarg($binDirectory), + escapeshellarg($outputFile), + escapeshellarg('cd '.escapeshellarg($root).'; '.$script), + ); + + exec($command, $output, $exitCode); + + return [$exitCode, implode("\n", $output), is_file($outputFile) ? trim(file_get_contents($outputFile)) : null]; +} + +it('detects the current Coolify image tag from the running container image', function (string $scriptPath, string $image, string $expectedTag) { + [$exitCode, $output] = runPostgresUpgradeScriptCommand(sprintf( + 'MOCK_DOCKER_IMAGE=%s; export MOCK_DOCKER_IMAGE; source %s; current_coolify_image_tag', + escapeshellarg($image), + escapeshellarg($scriptPath), + )); + + expect($exitCode)->toBe(0) + ->and($output)->toBe($expectedTag); +})->with([ + 'nightly standard registry image' => ['other/nightly/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.0-beta.420', '4.0.0-beta.420'], + 'nightly registry with port' => ['other/nightly/upgrade-postgres.sh', 'registry.example.com:5000/coollabsio/coolify:4.0.1', '4.0.1'], + 'nightly digest suffix' => ['other/nightly/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.2@sha256:abcdef', '4.0.2'], + 'scripts standard registry image' => ['scripts/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.0-beta.420', '4.0.0-beta.420'], + 'scripts registry with port' => ['scripts/upgrade-postgres.sh', 'registry.example.com:5000/coollabsio/coolify:4.0.1', '4.0.1'], + 'scripts digest suffix' => ['scripts/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.2@sha256:abcdef', '4.0.2'], +]); + +it('passes the preserved Coolify image tag to docker compose when starting the stack', function (string $scriptPath) { + [$exitCode, $output, $latestImage] = runPostgresUpgradeScriptCommand( + sprintf('source %s; start_stack 4.0.0-beta.420', escapeshellarg($scriptPath)), + ); + + expect($exitCode)->toBe(0) + ->and($output)->toBe('') + ->and($latestImage)->toBe('4.0.0-beta.420'); +})->with([ + 'nightly script' => 'other/nightly/upgrade-postgres.sh', + 'release script' => 'scripts/upgrade-postgres.sh', +]);