#!/usr/bin/env bash # scripts/update-nextcloud-hub.sh # Update Nextcloud Hub and supporting containers, run upgrade tasks, # and execute post-update validation. set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" COMPOSE_FILE="${REPO_DIR}/docker-compose.yml" APP_CONTAINER="${NEXTCLOUD_APP_CONTAINER:-nextcloud-app}" RUN_APP_UPDATES="${RUN_APP_UPDATES:-1}" compose() { if docker compose version >/dev/null 2>&1; then docker compose "$@" else docker-compose "$@" fi } require_command() { local cmd="$1" if ! command -v "${cmd}" >/dev/null 2>&1; then echo "ERROR: '${cmd}' is required but not installed." exit 1 fi } wait_for_occ() { local max_attempts=45 local attempt=1 while [ "${attempt}" -le "${max_attempts}" ]; do if docker exec --user www-data "${APP_CONTAINER}" php occ status >/dev/null 2>&1; then return 0 fi echo "Waiting for Nextcloud app container to become ready (${attempt}/${max_attempts})..." sleep 2 attempt=$((attempt + 1)) done return 1 } require_command docker require_command curl if ! docker ps >/dev/null 2>&1; then echo "ERROR: Docker daemon is not reachable." exit 1 fi if ! compose -f "${COMPOSE_FILE}" ps >/dev/null 2>&1; then echo "ERROR: Unable to access compose project using ${COMPOSE_FILE}." exit 1 fi echo "==> Pulling latest images for db/app/web" compose -f "${COMPOSE_FILE}" pull db app web echo "==> Recreating services" compose -f "${COMPOSE_FILE}" up -d db app web if ! wait_for_occ; then echo "ERROR: Nextcloud OCC did not become ready in time." exit 1 fi maintenance_enabled=0 cleanup() { if [ "${maintenance_enabled}" -eq 1 ]; then docker exec --user www-data "${APP_CONTAINER}" php occ maintenance:mode --off >/dev/null 2>&1 || true fi } trap cleanup EXIT echo "==> Enabling maintenance mode" docker exec --user www-data "${APP_CONTAINER}" php occ maintenance:mode --on maintenance_enabled=1 echo "==> Running Nextcloud upgrade" docker exec --user www-data "${APP_CONTAINER}" php occ upgrade if [ "${RUN_APP_UPDATES}" = "1" ]; then echo "==> Updating installed apps" docker exec --user www-data "${APP_CONTAINER}" php occ app:update --all else echo "==> Skipping app:update --all (RUN_APP_UPDATES=${RUN_APP_UPDATES})" fi echo "==> Running database/schema remediation commands" docker exec --user www-data "${APP_CONTAINER}" php occ db:add-missing-columns || true docker exec --user www-data "${APP_CONTAINER}" php occ db:add-missing-indices || true docker exec --user www-data "${APP_CONTAINER}" php occ db:add-missing-primary-keys || true echo "==> Running maintenance repair" docker exec --user www-data "${APP_CONTAINER}" php occ maintenance:repair echo "==> Disabling maintenance mode" docker exec --user www-data "${APP_CONTAINER}" php occ maintenance:mode --off maintenance_enabled=0 echo "==> Running post-update checks" docker exec --user www-data "${APP_CONTAINER}" php occ status docker exec --user www-data "${APP_CONTAINER}" php occ setupchecks || true if [ -x "${REPO_DIR}/scripts/test.sh" ]; then echo "==> Running endpoint smoke tests" STRICT_TLS="${STRICT_TLS:-0}" "${REPO_DIR}/scripts/test.sh" else echo "WARN: scripts/test.sh is not executable; skipping smoke tests." fi echo "==> Nextcloud Hub update workflow completed successfully"