From 405975bda8b31f4edd6e2f348bdd692dd8c2716c Mon Sep 17 00:00:00 2001 From: Alexander Zubarev Date: Wed, 30 Sep 2026 14:27:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20stacks/nextcloud/docker-compose.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stacks/nextcloud/docker-compose.yml | 1240 ++------------------------- 1 file changed, 64 insertions(+), 1176 deletions(-) diff --git a/stacks/nextcloud/docker-compose.yml b/stacks/nextcloud/docker-compose.yml index d90bf2a..43e9d18 100644 --- a/stacks/nextcloud/docker-compose.yml +++ b/stacks/nextcloud/docker-compose.yml @@ -1,1203 +1,91 @@ -#!/usr/bin/env bash +services: -set -Eeuo pipefail + nextcloud: + image: nextcloud:35.0.1 + container_name: nextcloud + restart: unless-stopped -umask 077 + env_file: + - /srv/nfs/infra/nextcloud/.env + environment: + OVERWRITEHOST: "nc.zk-tech.info" + OVERWRITEPROTOCOL: "https" -# ============================================================ -# Configuration -# ============================================================ + TRUSTED_PROXIES: "127.0.0.1,172.19.0.0/16,172.19.0.15" + NEXTCLOUD_TRUSTED_DOMAINS: "nc.zk-tech.info" -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -SRC="${ROOT}/stacks/nextcloud" + UPLOAD_MAX_FILESIZE: "16G" + POST_MAX_FILE_SIZE: "16G" -LIVE="/srv/nfs/infra/nextcloud" -ENV_FILE="${LIVE}/.env" + PHP_MEMORY_LIMIT: "4G" + PHP_UPLOAD_LIMIT: "16G" -BACKUP_ROOT="/srv/nfs/infra/.deploy-backups/nextcloud" -BACKUP_KEEP=3 + REDIS_HOST: "redis" -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" + volumes: + - /srv/nfs/infra/nextcloud/config:/var/www/html/config + - /srv/nfs/infra/nextcloud/data:/var/www/html/data + - /srv/nfs/infra/nextcloud/apps:/var/www/html/custom_apps + - /srv/nfs/infra/nextcloud/themes:/var/www/html/themes + - /srv/nfs/infra/nextcloud/config/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro -PREVIOUS_COMPOSE="/tmp/nextcloud-compose-${STAMP}-$$.yml" + depends_on: + - redis -INIT_TIMEOUT=900 -INIT_INTERVAL=5 -OCC_TIMEOUT=300 -OCC_INTERVAL=5 + networks: + - default + - interdock -# ============================================================ -# Runtime state -# ============================================================ + cron: + image: nextcloud:35.0.1 + container_name: nextcloud-cron + restart: unless-stopped -VERSION_CHANGED=false -PRODUCTION_CHANGED=false -MIGRATION_STARTED=false -BACKUP_COMPLETE=false -UPGRADE_STARTED="" + entrypoint: /cron.sh + env_file: + - /srv/nfs/infra/nextcloud/.env -# ============================================================ -# Helpers -# ============================================================ + environment: + REDIS_HOST: "redis" -log() -{ - echo - echo "============================================================" - echo "$*" - echo "============================================================" -} + volumes: + - /srv/nfs/infra/nextcloud/config:/var/www/html/config + - /srv/nfs/infra/nextcloud/data:/var/www/html/data + - /srv/nfs/infra/nextcloud/apps:/var/www/html/custom_apps + - /srv/nfs/infra/nextcloud/themes:/var/www/html/themes + depends_on: + - redis -cleanup_temp() -{ - rm -f -- "${PREVIOUS_COMPOSE}" 2>/dev/null || true -} + networks: + - default -restore_previous_compose() -{ - if [[ ! -f "${PREVIOUS_COMPOSE}" ]]; then - echo "Previous Compose file is unavailable; automatic restore skipped." - return 0 - fi + redis: + image: redis:rc-32bit-buster + container_name: nextcloud-redis + restart: unless-stopped - echo - echo "--- Restore previous docker-compose.yml ---" + command: + - redis-server + - --appendonly + - "yes" - cp \ - "${PREVIOUS_COMPOSE}" \ - "${LIVE}/docker-compose.yml" + volumes: + - redis-data:/data - ( - cd "${LIVE}" + networks: + - default - docker compose \ - --env-file "${ENV_FILE}" \ - up -d \ - --remove-orphans - ) || true -} +volumes: + redis-data: + driver: local -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 +networks: + interdock: + external: true