#!/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}" STRICT_TLS="${STRICT_TLS:-0}" ENABLE_WEB_UPDATER="${ENABLE_WEB_UPDATER:-1}" UPDATER_RELEASE_CHANNEL="${UPDATER_RELEASE_CHANNEL:-stable}" HUB_ENV_FILE="${HUB_ENV_FILE:-${REPO_DIR}/.hub-update.env}" if [ -f "${HUB_ENV_FILE}" ]; then # shellcheck disable=SC1090 set -a source "${HUB_ENV_FILE}" set +a fi 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 } occ_cmd() { docker exec --user www-data "${APP_CONTAINER}" php occ "$@" } wait_for_occ() { local max_attempts=45 local attempt=1 while [ "${attempt}" -le "${max_attempts}" ]; do if occ_cmd 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 } enable_web_updater() { echo "==> Enabling Nextcloud web updater" docker exec "${APP_CONTAINER}" sh -lc "cat > /var/www/html/config/upgrade-disable-web.config.php <<'PHP' false, ); PHP chown www-data:www-data /var/www/html/config/upgrade-disable-web.config.php chmod 640 /var/www/html/config/upgrade-disable-web.config.php" occ_cmd config:system:set updater.release.channel --value="${UPDATER_RELEASE_CHANNEL}" } 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 echo "==> Restarting web container to refresh nginx upstream resolution" compose -f "${COMPOSE_FILE}" restart web if ! wait_for_occ; then echo "ERROR: Nextcloud OCC did not become ready in time." exit 1 fi if [ "${ENABLE_WEB_UPDATER}" = "1" ]; then enable_web_updater else echo "==> Skipping web-updater enablement (ENABLE_WEB_UPDATER=${ENABLE_WEB_UPDATER})" fi echo "==> Ensuring maintenance mode is off before upgrade" occ_cmd maintenance:mode --off >/dev/null 2>&1 || true echo "==> Running Nextcloud upgrade" occ_cmd upgrade if [ "${RUN_APP_UPDATES}" = "1" ]; then echo "==> Updating installed apps" occ_cmd app:update --all else echo "==> Skipping app:update --all (RUN_APP_UPDATES=${RUN_APP_UPDATES})" fi echo "==> Running database/schema remediation commands" occ_cmd db:add-missing-columns || true occ_cmd db:add-missing-indices || true occ_cmd db:add-missing-primary-keys || true echo "==> Running maintenance repair" occ_cmd maintenance:repair echo "==> Running post-update checks" occ_cmd status occ_cmd setupchecks || true if [ -x "${REPO_DIR}/scripts/test.sh" ]; then echo "==> Running endpoint smoke tests" STRICT_TLS="${STRICT_TLS}" "${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"