#!/usr/bin/env bash set -Eeuo pipefail umask 077 # ============================================================ # Configuration # ============================================================ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SRC="${ROOT}/stacks/nextcloud" LIVE="/srv/nfs/infra/nextcloud" ENV_FILE="${LIVE}/.env" BACKUP_ROOT="/srv/nfs/infra/.deploy-backups/nextcloud" BACKUP_KEEP=3 STAMP="$(date '+%Y%m%d-%H%M%S')" BACKUP_DIR="${BACKUP_ROOT}/${STAMP}" DB_BACKUP_DIR="${BACKUP_DIR}/database" DB_BACKUP_FILE="${DB_BACKUP_DIR}/nextcloud.dump" PREVIOUS_COMPOSE="/tmp/nextcloud-compose-${STAMP}-$$.yml" INIT_TIMEOUT=900 INIT_INTERVAL=5 OCC_TIMEOUT=300 OCC_INTERVAL=5 # ============================================================ # Runtime state # ============================================================ VERSION_CHANGED=false PRODUCTION_CHANGED=false MIGRATION_STARTED=false BACKUP_COMPLETE=false UPGRADE_STARTED="" # ============================================================ # Helpers # ============================================================ log() { echo echo "============================================================" echo "$*" echo "============================================================" } cleanup_temp() { rm -f -- "${PREVIOUS_COMPOSE}" 2>/dev/null || true } restore_previous_compose() { if [[ ! -f "${PREVIOUS_COMPOSE}" ]]; then echo "Previous Compose file is unavailable; automatic restore skipped." return 0 fi echo echo "--- Restore previous docker-compose.yml ---" cp \ "${PREVIOUS_COMPOSE}" \ "${LIVE}/docker-compose.yml" ( cd "${LIVE}" docker compose \ --env-file "${ENV_FILE}" \ up -d \ --remove-orphans ) || true } print_failure_context() { local RC="$1" local FAILED_LINE="$2" local FAILED_COMMAND="$3" echo echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo " Nextcloud deployment FAILED" echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo echo "Exit code:" echo " ${RC}" echo echo "Failed at line:" echo " ${FAILED_LINE}" echo echo "Failed command:" echo " ${FAILED_COMMAND}" if [[ "${MIGRATION_STARTED}" == "true" ]]; then echo echo "Database migration MAY already have started." echo "Automatic rollback will NOT be performed." if [[ "${BACKUP_COMPLETE}" == "true" ]]; then echo echo "Pre-upgrade backup:" echo " ${BACKUP_DIR}" fi echo echo "For rollback restore the PostgreSQL dump first." elif [[ "${PRODUCTION_CHANGED}" == "true" ]]; then echo echo "Database migration was NOT started." echo "Restoring previous Compose configuration." restore_previous_compose else echo echo "Production was NOT modified." echo "Database migration was NOT started." echo "No service rollback is required." fi if [[ -d "${BACKUP_DIR}" ]] && [[ "${BACKUP_COMPLETE}" != "true" ]] then echo echo "Removing incomplete backup directory:" echo " ${BACKUP_DIR}" rm -rf -- "${BACKUP_DIR}" || true fi } on_error() { local RC=$? local FAILED_COMMAND="${BASH_COMMAND}" local FAILED_LINE="${BASH_LINENO[0]:-unknown}" trap - ERR print_failure_context \ "${RC}" \ "${FAILED_LINE}" \ "${FAILED_COMMAND}" exit "${RC}" } fail() { local MESSAGE="$*" echo echo "ERROR: ${MESSAGE}" >&2 return 1 } cleanup_old_backups() { echo echo "--- Backup retention ---" echo "Keeping last ${BACKUP_KEEP} complete pre-upgrade backups." mkdir -p "${BACKUP_ROOT}" mapfile -t BACKUPS < <( find "${BACKUP_ROOT}" \ -mindepth 1 \ -maxdepth 1 \ -type f \ -name '.complete' \ -printf '%h\n' | sed 's#.*/##' | grep -E '^[0-9]{8}-[0-9]{6}$' | sort -r ) if (( ${#BACKUPS[@]} <= BACKUP_KEEP )); then echo "Current complete backup count: ${#BACKUPS[@]}" echo "Nothing to delete." return 0 fi for BACKUP_NAME in "${BACKUPS[@]:${BACKUP_KEEP}}" do OLD_BACKUP="${BACKUP_ROOT}/${BACKUP_NAME}" echo echo "Removing old backup:" echo " ${OLD_BACKUP}" rm -rf -- "${OLD_BACKUP}" done } version_less_than() { local A="$1" local B="$2" [[ "${A}" == "${B}" ]] && return 1 [[ "$( printf '%s\n%s\n' "${A}" "${B}" | sort -V | head -n1 )" == "${A}" ]] } json_string_value() { local KEY="$1" sed -nE \ "s/.*\"${KEY}\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*/\1/p" | head -n1 } get_occ_status_json() { local OUTPUT local RC set +e OUTPUT="$( docker exec \ -u www-data \ nextcloud \ php occ status \ --output=json \ 2>&1 )" RC=$? set -e if [[ "${RC}" -ne 0 ]]; then printf '%s\n' "${OUTPUT}" >&2 return "${RC}" fi printf '%s\n' "${OUTPUT}" } validate_occ_status_json() { local STATUS_JSON="$1" echo "${STATUS_JSON}" | grep -Eq '"installed"[[:space:]]*:[[:space:]]*true' || fail "Nextcloud is not reported as installed" echo "${STATUS_JSON}" | grep -Eq '"needsDbUpgrade"[[:space:]]*:[[:space:]]*false' || fail "Nextcloud requires a database upgrade" echo "${STATUS_JSON}" | grep -Eq '"maintenance"[[:space:]]*:[[:space:]]*false' || fail "Nextcloud is in maintenance mode" } wait_for_occ_normal() { local ELAPSED=0 local STATUS_JSON="" local RC=0 echo echo "Waiting for Nextcloud OCC status..." while (( ELAPSED < OCC_TIMEOUT )) do set +e STATUS_JSON="$(get_occ_status_json 2>&1)" RC=$? set -e if [[ "${RC}" -eq 0 ]] && echo "${STATUS_JSON}" | grep -Eq '"needsDbUpgrade"[[:space:]]*:[[:space:]]*false' && echo "${STATUS_JSON}" | grep -Eq '"maintenance"[[:space:]]*:[[:space:]]*false' then echo "Nextcloud OCC status is normal." return 0 fi sleep "${OCC_INTERVAL}" ELAPSED=$((ELAPSED + OCC_INTERVAL)) echo "Waiting for OCC... ${ELAPSED}s" done echo echo "Last OCC output:" echo "${STATUS_JSON}" fail "Timeout waiting for normal Nextcloud OCC status" } trap on_error ERR trap cleanup_temp EXIT # ============================================================ # Required commands # ============================================================ log "Pre-flight checks" for CMD in \ docker \ psql \ pg_dump \ pg_restore \ sha256sum \ sort \ grep \ sed \ find \ awk \ du \ diff \ seq do command -v "${CMD}" >/dev/null 2>&1 || \ fail "Required command not found: ${CMD}" done docker compose version >/dev/null 2>&1 || \ fail "Docker Compose plugin is not available" # ============================================================ # Required files # ============================================================ [[ -f "${SRC}/docker-compose.yml" ]] || \ fail "${SRC}/docker-compose.yml not found" [[ -f "${LIVE}/docker-compose.yml" ]] || \ fail "${LIVE}/docker-compose.yml not found" [[ -f "${ENV_FILE}" ]] || \ fail "${ENV_FILE} not found" # Preserve production Compose before any change. cp \ "${LIVE}/docker-compose.yml" \ "${PREVIOUS_COMPOSE}" # ============================================================ # Backup directory access # ============================================================ mkdir -p "${BACKUP_ROOT}" [[ -w "${BACKUP_ROOT}" ]] || \ fail "${BACKUP_ROOT} is not writable" # ============================================================ # Load PostgreSQL settings # ============================================================ set -a # shellcheck disable=SC1090 source "${ENV_FILE}" set +a : "${POSTGRES_HOST:?POSTGRES_HOST not defined}" : "${POSTGRES_DB:?POSTGRES_DB not defined}" : "${POSTGRES_USER:?POSTGRES_USER not defined}" : "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD not defined}" POSTGRES_PORT="${POSTGRES_PORT:-5432}" export PGPASSWORD="${POSTGRES_PASSWORD}" export PGCONNECT_TIMEOUT=10 # ============================================================ # Validate requested Compose # ============================================================ log "Validate Docker Compose" docker compose \ --env-file "${ENV_FILE}" \ -f "${SRC}/docker-compose.yml" \ config >/dev/null echo "Docker Compose configuration is valid." # ============================================================ # Show requested Compose changes # ============================================================ log "Docker Compose changes" if diff -u \ "${LIVE}/docker-compose.yml" \ "${SRC}/docker-compose.yml" then echo "No docker-compose.yml text changes detected." fi # ============================================================ # Detect requested Nextcloud image # ============================================================ log "Detect requested Nextcloud image" mapfile -t NEXTCLOUD_IMAGES < <( docker compose \ --env-file "${ENV_FILE}" \ -f "${SRC}/docker-compose.yml" \ config --images | grep '^nextcloud:' | sort -u ) if [[ "${#NEXTCLOUD_IMAGES[@]}" -ne 1 ]]; then echo "Detected Nextcloud images:" if (( ${#NEXTCLOUD_IMAGES[@]} > 0 )); then printf ' %s\n' "${NEXTCLOUD_IMAGES[@]}" else echo " none" fi fail "Services nextcloud and cron must use exactly the same Nextcloud image" fi NEW_IMAGE="${NEXTCLOUD_IMAGES[0]}" echo "Requested image:" echo " ${NEW_IMAGE}" # ============================================================ # Current Nextcloud container # ============================================================ docker inspect nextcloud >/dev/null 2>&1 || \ fail "Current Nextcloud container does not exist" CURRENT_RUNNING="$( docker inspect \ -f '{{.State.Running}}' \ nextcloud )" [[ "${CURRENT_RUNNING}" == "true" ]] || \ fail "Current Nextcloud container is not running" OLD_IMAGE="$( docker inspect \ -f '{{.Config.Image}}' \ nextcloud )" # ============================================================ # Detect current Nextcloud status/version # # IMPORTANT: # Compare internal "version" (e.g. 35.0.1.1) with the target # image's $OC_Version. Do NOT compare "versionstring" (35.0.1) # against $OC_Version, otherwise an unchanged image looks like # a version upgrade. # ============================================================ log "Detect current Nextcloud version" set +e CURRENT_STATUS_JSON="$(get_occ_status_json 2>&1)" CURRENT_STATUS_RC=$? set -e if [[ "${CURRENT_STATUS_RC}" -ne 0 ]]; then echo echo "--- OCC status output ---" echo "${CURRENT_STATUS_JSON}" fail \ "Could not get current Nextcloud status (exit code ${CURRENT_STATUS_RC})" fi CURRENT_NC_VERSION="$( printf '%s\n' "${CURRENT_STATUS_JSON}" | json_string_value version )" CURRENT_NC_VERSIONSTRING="$( printf '%s\n' "${CURRENT_STATUS_JSON}" | json_string_value versionstring )" [[ -n "${CURRENT_NC_VERSION}" ]] || \ fail "Could not determine current internal Nextcloud version" validate_occ_status_json "${CURRENT_STATUS_JSON}" docker exec \ -u www-data \ nextcloud \ php occ status -e echo "Current image:" echo " ${OLD_IMAGE}" echo "Current Nextcloud internal version:" echo " ${CURRENT_NC_VERSION}" if [[ -n "${CURRENT_NC_VERSIONSTRING}" ]]; then echo "Current Nextcloud display version:" echo " ${CURRENT_NC_VERSIONSTRING}" fi # ============================================================ # Pull target Nextcloud image # ============================================================ log "Pull target Nextcloud image" docker pull "${NEW_IMAGE}" # ============================================================ # Determine exact target Nextcloud version # ============================================================ log "Detect target Nextcloud version" TARGET_NC_VERSION="$( docker run \ --rm \ --entrypoint php \ "${NEW_IMAGE}" \ -r ' require "/usr/src/nextcloud/version.php"; echo implode(".", $OC_Version); ' )" TARGET_NC_VERSIONSTRING="$( docker run \ --rm \ --entrypoint php \ "${NEW_IMAGE}" \ -r ' require "/usr/src/nextcloud/version.php"; echo isset($OC_VersionString) ? $OC_VersionString : implode(".", $OC_Version); ' )" [[ -n "${TARGET_NC_VERSION}" ]] || \ fail "Could not determine target Nextcloud version" echo "Target Docker image:" echo " ${NEW_IMAGE}" echo "Target Nextcloud internal version:" echo " ${TARGET_NC_VERSION}" echo "Target Nextcloud display version:" echo " ${TARGET_NC_VERSIONSTRING}" # ============================================================ # Version safety checks # ============================================================ log "Validate version transition" if version_less_than \ "${TARGET_NC_VERSION}" \ "${CURRENT_NC_VERSION}" then fail \ "Nextcloud downgrade is not allowed: ${CURRENT_NC_VERSION} -> ${TARGET_NC_VERSION}" fi CURRENT_MAJOR="${CURRENT_NC_VERSION%%.*}" TARGET_MAJOR="${TARGET_NC_VERSION%%.*}" if (( TARGET_MAJOR > CURRENT_MAJOR + 1 )); then fail \ "Skipping Nextcloud major versions is forbidden: ${CURRENT_MAJOR} -> ${TARGET_MAJOR}" fi if [[ "${CURRENT_NC_VERSION}" != "${TARGET_NC_VERSION}" ]]; then VERSION_CHANGED=true fi echo "Current internal version: ${CURRENT_NC_VERSION}" echo "Target internal version: ${TARGET_NC_VERSION}" echo "Version changed: ${VERSION_CHANGED}" # ============================================================ # PRE-UPGRADE BACKUP # # Only when the actual internal Nextcloud version changes. # ============================================================ if [[ "${VERSION_CHANGED}" == "true" ]]; then log "Prepare Nextcloud pre-upgrade backup" mkdir -p "${DB_BACKUP_DIR}" echo echo "--- Backup current docker-compose.yml ---" cp \ "${LIVE}/docker-compose.yml" \ "${BACKUP_DIR}/docker-compose.yml" printf '%s\n' "${CURRENT_STATUS_JSON}" \ > "${BACKUP_DIR}/nextcloud-status-before.json" cat > "${BACKUP_DIR}/metadata.txt" < "${DB_BACKUP_DIR}/postgresql-version.txt" SERVER_MAJOR="${SERVER_VERSION%%.*}" PG_DUMP_VERSION="$( pg_dump --version | sed -E 's/.* ([0-9]+(\.[0-9]+)?).*/\1/' )" PG_DUMP_MAJOR="${PG_DUMP_VERSION%%.*}" echo "PostgreSQL server:" echo " ${SERVER_VERSION}" echo "pg_dump client:" echo " ${PG_DUMP_VERSION}" if (( PG_DUMP_MAJOR < SERVER_MAJOR )); then fail \ "pg_dump ${PG_DUMP_VERSION} is older than PostgreSQL server ${SERVER_VERSION}" fi echo echo "--- Database size ---" DB_SIZE="$( psql \ -h "${POSTGRES_HOST}" \ -p "${POSTGRES_PORT}" \ -U "${POSTGRES_USER}" \ -d "${POSTGRES_DB}" \ -Atqc \ 'SELECT pg_size_pretty(pg_database_size(current_database()));' )" echo "Database:" echo " ${POSTGRES_DB}" echo "Size:" echo " ${DB_SIZE}" log "Create PostgreSQL backup" pg_dump \ -h "${POSTGRES_HOST}" \ -p "${POSTGRES_PORT}" \ -U "${POSTGRES_USER}" \ -d "${POSTGRES_DB}" \ --format=custom \ --no-owner \ --no-privileges \ --file="${DB_BACKUP_FILE}" [[ -s "${DB_BACKUP_FILE}" ]] || \ fail "PostgreSQL dump does not exist or is empty" echo echo "--- Validate PostgreSQL dump ---" pg_restore \ --list \ "${DB_BACKUP_FILE}" \ > "${DB_BACKUP_DIR}/nextcloud.dump.list" [[ -s "${DB_BACKUP_DIR}/nextcloud.dump.list" ]] || \ fail "pg_restore could not validate PostgreSQL dump" echo echo "--- Generate and verify SHA256 ---" ( cd "${DB_BACKUP_DIR}" sha256sum \ nextcloud.dump \ > nextcloud.dump.sha256 # The checksum contains the relative name "nextcloud.dump", # therefore validation MUST run from the same directory. sha256sum \ -c nextcloud.dump.sha256 ) DB_BACKUP_SIZE="$( du -h "${DB_BACKUP_FILE}" | awk '{print $1}' )" touch "${BACKUP_DIR}/.complete" BACKUP_COMPLETE=true echo echo "PostgreSQL backup successful." echo echo "Backup directory:" echo " ${BACKUP_DIR}" echo echo "Database dump:" echo " ${DB_BACKUP_FILE}" echo echo "Dump size:" echo " ${DB_BACKUP_SIZE}" echo echo "SHA256:" cat "${DB_BACKUP_DIR}/nextcloud.dump.sha256" cleanup_old_backups fi # ============================================================ # Deploy requested Compose # ============================================================ log "Deploy docker-compose.yml" cp \ "${SRC}/docker-compose.yml" \ "${LIVE}/docker-compose.yml" PRODUCTION_CHANGED=true cd "${LIVE}" # ============================================================ # Pull all Compose images # ============================================================ log "Pull Docker images" docker compose \ --env-file "${ENV_FILE}" \ pull # ============================================================ # NEXTCLOUD VERSION UPGRADE # ============================================================ if [[ "${VERSION_CHANGED}" == "true" ]]; then log "Nextcloud upgrade ${CURRENT_NC_VERSION} -> ${TARGET_NC_VERSION}" echo echo "--- Stop Nextcloud cron ---" docker compose \ --env-file "${ENV_FILE}" \ stop cron || true UPGRADE_STARTED="$( date -u '+%Y-%m-%dT%H:%M:%SZ' )" echo echo "--- Recreate Nextcloud container ---" # From this point the new image can modify the DB schema. MIGRATION_STARTED=true docker compose \ --env-file "${ENV_FILE}" \ up -d \ --no-deps \ nextcloud log "Wait for Nextcloud initialization" INITIALIZED=false for _ in $(seq 1 $((INIT_TIMEOUT / INIT_INTERVAL))) do RUNNING="$( docker inspect \ -f '{{.State.Running}}' \ nextcloud 2>/dev/null || echo false )" if [[ "${RUNNING}" != "true" ]]; then echo echo "--- Nextcloud container log ---" docker logs \ --since "${UPGRADE_STARTED}" \ nextcloud || true fail "Nextcloud container stopped during upgrade" fi STARTUP_LOG="$( docker logs \ --since "${UPGRADE_STARTED}" \ nextcloud 2>&1 || true )" if echo "${STARTUP_LOG}" | grep -q 'Update failed' then echo "${STARTUP_LOG}" fail "Nextcloud automatic upgrade failed" fi if echo "${STARTUP_LOG}" | grep -q 'Initializing finished' then INITIALIZED=true break fi sleep "${INIT_INTERVAL}" done if [[ "${INITIALIZED}" != "true" ]]; then echo echo "--- Nextcloud container log ---" docker logs \ --since "${UPGRADE_STARTED}" \ nextcloud || true fail "Timeout waiting for Nextcloud initialization" fi log "Nextcloud startup upgrade output" STARTUP_LOG="$( docker logs \ --since "${UPGRADE_STARTED}" \ nextcloud 2>&1 )" echo "${STARTUP_LOG}" STARTUP_UPDATE_SUCCESS=false if echo "${STARTUP_LOG}" | grep -q 'Update successful' then STARTUP_UPDATE_SUCCESS=true fi echo echo "Startup log contains 'Update successful':" echo " ${STARTUP_UPDATE_SUCCESS}" log "Run explicit occ upgrade" set +e OCC_UPGRADE_OUTPUT="$( docker exec \ -u www-data \ nextcloud \ php occ upgrade 2>&1 )" OCC_UPGRADE_RC=$? set -e echo "${OCC_UPGRADE_OUTPUT}" if [[ "${OCC_UPGRADE_RC}" -ne 0 ]]; then fail \ "Explicit occ upgrade returned exit code ${OCC_UPGRADE_RC}" fi EXPLICIT_UPDATE_SUCCESS=false EXPLICIT_ALREADY_CURRENT=false if echo "${OCC_UPGRADE_OUTPUT}" | grep -q 'Update successful' then EXPLICIT_UPDATE_SUCCESS=true fi if echo "${OCC_UPGRADE_OUTPUT}" | grep -Eqi \ 'already.*(latest|up.?to.?date)|(is|already).*up.?to.?date' then EXPLICIT_ALREADY_CURRENT=true fi echo echo "Explicit occ upgrade contains 'Update successful':" echo " ${EXPLICIT_UPDATE_SUCCESS}" echo "Explicit occ upgrade reports already current:" echo " ${EXPLICIT_ALREADY_CURRENT}" if [[ "${STARTUP_UPDATE_SUCCESS}" != "true" ]] && [[ "${EXPLICIT_UPDATE_SUCCESS}" != "true" ]] then if [[ "${EXPLICIT_ALREADY_CURRENT}" == "true" ]]; then echo echo "occ reports that Nextcloud is already current," echo "but 'Update successful' was not found in either" echo "the startup log or explicit occ upgrade output." fi fail "Could not confirm 'Update successful'" fi log "Validate Nextcloud status after upgrade" wait_for_occ_normal FINAL_STATUS_JSON="$(get_occ_status_json)" echo "${FINAL_STATUS_JSON}" validate_occ_status_json "${FINAL_STATUS_JSON}" docker exec \ -u www-data \ nextcloud \ php occ status -e echo echo "Nextcloud database state validated successfully." log "Start Nextcloud cron" docker compose \ --env-file "${ENV_FILE}" \ up -d \ --no-deps \ cron log "Apply remaining Compose changes" docker compose \ --env-file "${ENV_FILE}" \ up -d \ --remove-orphans # ============================================================ # NO NEXTCLOUD VERSION CHANGE # ============================================================ else log "No Nextcloud version change" echo "Current internal version:" echo " ${CURRENT_NC_VERSION}" # Compose-only change, for example bind mount changes. # No DB backup/occ upgrade is performed. docker compose \ --env-file "${ENV_FILE}" \ up -d \ --remove-orphans wait_for_occ_normal fi # ============================================================ # Final container checks # ============================================================ log "Final container checks" for CONTAINER in \ nextcloud \ nextcloud-cron \ nextcloud-redis do RUNNING="$( docker inspect \ -f '{{.State.Running}}' \ "${CONTAINER}" 2>/dev/null || echo false )" if [[ "${RUNNING}" != "true" ]]; then fail "${CONTAINER} is not running" fi echo "${CONTAINER}: running" done # ============================================================ # Final OCC status/version # ============================================================ log "Final OCC status" wait_for_occ_normal FINAL_STATUS_JSON="$(get_occ_status_json)" echo "${FINAL_STATUS_JSON}" validate_occ_status_json "${FINAL_STATUS_JSON}" docker exec \ -u www-data \ nextcloud \ php occ status -e RUNNING_NC_VERSION="$( printf '%s\n' "${FINAL_STATUS_JSON}" | json_string_value version )" RUNNING_NC_VERSIONSTRING="$( printf '%s\n' "${FINAL_STATUS_JSON}" | json_string_value versionstring )" [[ -n "${RUNNING_NC_VERSION}" ]] || \ fail "Could not determine final Nextcloud version" [[ "${RUNNING_NC_VERSION}" == "${TARGET_NC_VERSION}" ]] || \ fail \ "Running Nextcloud version ${RUNNING_NC_VERSION} does not match target ${TARGET_NC_VERSION}" RUNNING_IMAGE="$( docker inspect \ -f '{{.Config.Image}}' \ nextcloud )" # ============================================================ # Success # ============================================================ log "Nextcloud deployment successful" echo "Previous Nextcloud internal version:" echo " ${CURRENT_NC_VERSION}" echo echo "Running Nextcloud internal version:" echo " ${RUNNING_NC_VERSION}" if [[ -n "${RUNNING_NC_VERSIONSTRING}" ]]; then echo echo "Running Nextcloud display version:" echo " ${RUNNING_NC_VERSIONSTRING}" fi echo echo "Previous Docker image:" echo " ${OLD_IMAGE}" echo echo "Running Docker image:" echo " ${RUNNING_IMAGE}" if [[ "${VERSION_CHANGED}" == "true" ]]; then echo echo "Pre-upgrade backup:" echo " ${BACKUP_DIR}" echo echo "PostgreSQL dump:" echo " ${DB_BACKUP_FILE}" echo echo "Backup retention:" echo " last ${BACKUP_KEEP} complete backups" fi # ============================================================ # Clear credentials # ============================================================ unset PGPASSWORD unset POSTGRES_PASSWORD PRODUCTION_CHANGED=false exit 0