Updated the nextcloud update script to correct for not updating issues

This commit is contained in:
Rufus King 2026-08-18 00:40:45 -04:00
parent 70a1a14735
commit 2b1bdb2e5a
2 changed files with 79 additions and 51 deletions

View file

@ -0,0 +1 @@
http://localhost:5005/historical?ticker=SMERY&date=2025-07-31&key=90c2528e9b5221c110f7c2c9cd6c65dc

View file

@ -8,26 +8,16 @@
# Schedule: Daily (recommended 7:00 AM) # Schedule: Daily (recommended 7:00 AM)
# Install: /usr/scripts/omv/nextcloud_update_check.sh # Install: /usr/scripts/omv/nextcloud_update_check.sh
# Log: /var/log/nextcloud-updates/ # Log: /var/log/nextcloud-updates/
#
# 2026-07-24 fix: previously only checked `occ app:update --all
# --showonly`, which reports APP updates only. It never checked
# for a Nextcloud CORE/server version update, so a core release
# (e.g. Nextcloud 34.0.2) was silently missed and the report
# said "up to date" even when it wasn't. Added a separate core
# check via `occ update:check`.
# ============================================================= # =============================================================
# --- Config -------------------------------------------------- # --- Config --------------------------------------------------
CONTAINER="nextcloud" CONTAINER="nextcloud"
OCC="sudo docker exec -u www-data ${CONTAINER} php occ" OCC="sudo docker exec -u www-data ${CONTAINER} php occ"
# OMV Compose project location — NOT ~/docker/nextcloud. Confirmed via:
# docker inspect nextcloud --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'
# Directory is root-only (drwx------ root root), so compose commands
# must run under sudo. OMV names its compose files "<project>.yml" +
# "compose.override.yml" — NOT the default "docker-compose.yml" — so
# both -f flags are required or `docker compose` won't find them.
NEXTCLOUD_COMPOSE_DIR="/kingdezignsnas/Docker/Compose/nextcloud" NEXTCLOUD_COMPOSE_DIR="/kingdezignsnas/Docker/Compose/nextcloud"
NEXTCLOUD_COMPOSE_FILES="-f nextcloud.yml -f compose.override.yml" NEXTCLOUD_COMPOSE_FILES="-f nextcloud.yml -f compose.override.yml"
NEXTCLOUD_FFMPEG_BUILD_DIR="/kingdezignsnas/Docker/Compose/nextcloud-ffmpeg-build"
NEXTCLOUD_FFMPEG_BUILD_FILES="-f nextcloud-ffmpeg-build.yml --env-file nextcloud-ffmpeg-build.env"
NEXTCLOUD_PULL_SERVICES="db onlyoffice autoheal"
SMTP_SERVER="smtppro.zoho.com" SMTP_SERVER="smtppro.zoho.com"
SMTP_PORT="465" SMTP_PORT="465"
SMTP_USER="rufus.king@kingdezigns.com" SMTP_USER="rufus.king@kingdezigns.com"
@ -47,6 +37,30 @@ LOGFILE="$LOG_DIR/update-check-${DATESTAMP}.log"
log() { echo "[$(date +"%H:%M:%S")] $*" | tee -a "$LOGFILE"; } log() { echo "[$(date +"%H:%M:%S")] $*" | tee -a "$LOGFILE"; }
# Returns 0 when $1 is strictly newer than $2 (e.g. 34.0.3 > 34.0.2).
version_gt() {
local ver1=$1 ver2=$2
[[ -n "$ver1" && -n "$ver2" ]] || return 1
local IFS=.
local -a v1=($ver1) v2=($ver2)
local i n1 n2
for ((i=0; i<${#v1[@]} || i<${#v2[@]}; i++)); do
n1=$((10#${v1[i]:-0}))
n2=$((10#${v2[i]:-0}))
if (( n1 > n2 )); then return 0; fi
if (( n1 < n2 )); then return 1; fi
done
return 1
}
# Adjusted to strip any "v" prefix or extra spaces cleanly
fetch_latest_github_release() {
curl -sf --max-time 20 "https://api.github.com/repos/nextcloud/server/releases/latest" \
| grep -oE '"tag_name"[[:space:]]*:[[:space:]]*"v[0-9]+\.[0-9]+\.[0-9]+"' \
| head -1 \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+'
}
# --- Load SMTP password -------------------------------------- # --- Load SMTP password --------------------------------------
if [[ ! -f "$SMTP_PASS_FILE" ]]; then if [[ ! -f "$SMTP_PASS_FILE" ]]; then
log "ERROR: SMTP password file not found at $SMTP_PASS_FILE" log "ERROR: SMTP password file not found at $SMTP_PASS_FILE"
@ -57,7 +71,6 @@ SMTP_PASS=$(cat "$SMTP_PASS_FILE")
# --- Check container is running ------------------------------ # --- Check container is running ------------------------------
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
log "ERROR: Container '${CONTAINER}' is not running." log "ERROR: Container '${CONTAINER}' is not running."
# Send critical alert
STATUS_COLOR="#b91c1c" STATUS_COLOR="#b91c1c"
STATUS_LABEL="CRITICAL" STATUS_LABEL="CRITICAL"
STATUS_MSG="⛔ &nbsp;Nextcloud container is not running" STATUS_MSG="⛔ &nbsp;Nextcloud container is not running"
@ -76,34 +89,54 @@ else
log "Nextcloud version: $NC_VERSION | maintenance: $MAINTENANCE | needsDbUpgrade: $NEEDS_UPGRADE" log "Nextcloud version: $NC_VERSION | maintenance: $MAINTENANCE | needsDbUpgrade: $NEEDS_UPGRADE"
# --- Check for CORE (server) update ------------------------- # --- Check for CORE (server) update -------------------------
# occ app:update only reports app updates, never core/server log "Checking core (server) update via occ update:check..."
# releases. occ update:check is the command that actually
# checks the updater channel for a new Nextcloud version.
log "Checking core (server) update..."
CORE_CHECK_RAW=$($OCC update:check 2>/dev/null | grep -v '^{') CORE_CHECK_RAW=$($OCC update:check 2>/dev/null | grep -v '^{')
log "occ update:check raw output:"
while IFS= read -r line; do
[[ -n "$line" ]] && log " $line"
done <<< "$CORE_CHECK_RAW"
CORE_UPDATE_AVAILABLE=false CORE_UPDATE_AVAILABLE=false
CORE_NEW_VERSION="" CORE_NEW_VERSION=""
if echo "$CORE_CHECK_RAW" | grep -qi "is available"; then CORE_UPDATE_SOURCE=""
if echo "$CORE_CHECK_RAW" | grep -qiE 'Nextcloud [0-9]+\.[0-9]+\.[0-9]+ is available'; then
CORE_UPDATE_AVAILABLE=true CORE_UPDATE_AVAILABLE=true
CORE_NEW_VERSION=$(echo "$CORE_CHECK_RAW" | grep -oE 'Nextcloud [0-9]+\.[0-9]+\.[0-9]+' | head -1 | awk '{print $2}') CORE_NEW_VERSION=$(echo "$CORE_CHECK_RAW" | grep -oiE 'Nextcloud [0-9]+\.[0-9]+\.[0-9]+ is available' | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
log "Core update available: Nextcloud ${CORE_NEW_VERSION}" CORE_UPDATE_SOURCE="occ update:check"
log "Core update available (occ): Nextcloud ${CORE_NEW_VERSION}"
fi
log "Checking core update via GitHub latest release fallback..."
GITHUB_LATEST=$(fetch_latest_github_release)
if [[ -n "$GITHUB_LATEST" ]]; then
log "GitHub latest stable release: ${GITHUB_LATEST} (installed: ${NC_VERSION})"
if version_gt "$GITHUB_LATEST" "$NC_VERSION"; then
if [[ "$CORE_UPDATE_AVAILABLE" != "true" ]] || version_gt "$GITHUB_LATEST" "$CORE_NEW_VERSION"; then
CORE_UPDATE_AVAILABLE=true
CORE_NEW_VERSION="$GITHUB_LATEST"
if [[ -z "$CORE_UPDATE_SOURCE" ]]; then
CORE_UPDATE_SOURCE="GitHub latest release (occ update:check lagged)"
else
CORE_UPDATE_SOURCE="${CORE_UPDATE_SOURCE} + GitHub latest release"
fi
log "Core update available (GitHub fallback): ${NC_VERSION} → ${CORE_NEW_VERSION}"
fi
fi
else else
log "Core is up to date." log "WARN: Could not fetch GitHub latest release for fallback compare."
fi fi
# --- Check for app updates -------------------------------- # --- Check for app updates --------------------------------
log "Checking app updates..." log "Checking app updates..."
# Filter out admin_audit JSON log lines that appear when admin_audit app is enabled
APP_UPDATE_RAW=$($OCC app:update --all --showonly 2>/dev/null | grep -v '^{' ) APP_UPDATE_RAW=$($OCC app:update --all --showonly 2>/dev/null | grep -v '^{' )
# Parse apps with available updates
# Format from occ app:update --showonly: "appname new version available: X.Y.Z"
UPDATABLE_APPS=() UPDATABLE_APPS=()
while IFS= read -r line; do while IFS= read -r line; do
if echo "$line" | grep -q "new version available"; then if echo "$line" | grep -q "new version available"; then
APP_NAME=$(echo "$line" | awk '{print $1}') APP_NAME=$(echo "$line" | awk '{print $1}')
NEW_VER=$(echo "$line" | awk '{print $NF}') NEW_VER=$(echo "$line" | awk '{print $NF}')
UPDATABLE_APPS+=("${APP_NAME}|||${NEW_VER}") UPDATABLE_APPS+=("${APP_NAME}|${NEW_VER}")
log "Update available: $APP_NAME → $NEW_VER" log "Update available: $APP_NAME → $NEW_VER"
fi fi
done <<< "$APP_UPDATE_RAW" done <<< "$APP_UPDATE_RAW"
@ -152,7 +185,6 @@ else
STATUS_MSG="✅ &nbsp;Nextcloud is fully up to date" STATUS_MSG="✅ &nbsp;Nextcloud is fully up to date"
fi fi
# --- Maintenance mode color (was previously unset/unused) --
if [[ "$MAINTENANCE" == "true" ]]; then if [[ "$MAINTENANCE" == "true" ]]; then
MAINTENANCE_COLOR="#b91c1c" MAINTENANCE_COLOR="#b91c1c"
else else
@ -164,7 +196,7 @@ else
if [[ $APP_COUNT -gt 0 ]]; then if [[ $APP_COUNT -gt 0 ]]; then
for entry in "${UPDATABLE_APPS[@]}"; do for entry in "${UPDATABLE_APPS[@]}"; do
APP_NAME=$(echo "$entry" | cut -d'|' -f1) APP_NAME=$(echo "$entry" | cut -d'|' -f1)
NEW_VER=$(echo "$entry" | cut -d'|' -f4) NEW_VER=$(echo "$entry" | cut -d'|' -f2)
APP_ROWS+="<tr> APP_ROWS+="<tr>
<td style='padding:9px 14px;border-bottom:1px solid #e5e7eb;font-size:13px;color:#111827;font-family:monospace;'>${APP_NAME}</td> <td style='padding:9px 14px;border-bottom:1px solid #e5e7eb;font-size:13px;color:#111827;font-family:monospace;'>${APP_NAME}</td>
<td style='padding:9px 14px;border-bottom:1px solid #e5e7eb;font-size:13px;color:#1a7f4b;font-weight:600;'>${NEW_VER}</td> <td style='padding:9px 14px;border-bottom:1px solid #e5e7eb;font-size:13px;color:#1a7f4b;font-weight:600;'>${NEW_VER}</td>
@ -181,25 +213,19 @@ else
done done
# --- Build copy-paste command block ----------------------- # --- Build copy-paste command block -----------------------
# NOTE: must use $'...' (ANSI-C quoting) below, not "...". Plain
# double quotes do NOT interpret \n as a newline in bash — they
# insert the two literal characters \ and n, which is why the
# previous version of this script emailed out commands with
# literal "\n" text instead of real line breaks.
CMD_LINES="" CMD_LINES=""
if [[ "$MAINTENANCE" == "true" ]]; then if [[ "$MAINTENANCE" == "true" ]]; then
CMD_LINES+=$'# Disable maintenance mode\nsudo docker exec -u www-data nextcloud php occ maintenance:mode --off\n\n' CMD_LINES+=$'# Disable maintenance mode\nsudo docker exec -u www-data nextcloud php occ maintenance:mode --off\n\n'
fi fi
if [[ "$CORE_UPDATE_AVAILABLE" == "true" ]]; then if [[ "$CORE_UPDATE_AVAILABLE" == "true" ]]; then
# The nextcloud image's own entrypoint auto-detects the version # Corrected step to target the actual service container for your custom image
# bump and runs the upgrade itself on container start — it does
# NOT need `occ upgrade` run manually. Running `occ upgrade`
# immediately after `up -d` can race the container's own startup
# and falsely report "No upgrade required" if checked too early.
# The 30s sleep gives the entrypoint's internal upgrade time to
# finish before the confirming `occ status` call.
CMD_LINES+="# Update Nextcloud CORE to ${CORE_NEW_VERSION} (do this before app updates)"$'\n' CMD_LINES+="# Update Nextcloud CORE to ${CORE_NEW_VERSION} (do this before app updates)"$'\n'
CMD_LINES+="sudo bash -c \"cd ${NEXTCLOUD_COMPOSE_DIR} && docker compose ${NEXTCLOUD_COMPOSE_FILES} pull && docker compose ${NEXTCLOUD_COMPOSE_FILES} up -d\""$'\n\n' CMD_LINES+="# Step 1: Rebuild nextcloud-ffmpeg from a fresh nextcloud:latest base"$'\n'
CMD_LINES+="sudo bash -c \"cd ${NEXTCLOUD_FFMPEG_BUILD_DIR} && docker compose ${NEXTCLOUD_FFMPEG_BUILD_FILES} build --no-cache --pull\""$'\n\n'
CMD_LINES+="# Step 2: Pull supporting services only (skip nextcloud/cron — local image)"$'\n'
CMD_LINES+="sudo bash -c \"cd ${NEXTCLOUD_COMPOSE_DIR} && docker compose ${NEXTCLOUD_COMPOSE_FILES} pull ${NEXTCLOUD_PULL_SERVICES}\""$'\n\n'
CMD_LINES+="# Step 3: Recreate containers to pick up the rebuilt image"$'\n'
CMD_LINES+="sudo bash -c \"cd ${NEXTCLOUD_COMPOSE_DIR} && docker compose ${NEXTCLOUD_COMPOSE_FILES} up -d\""$'\n\n'
CMD_LINES+="# Give the container's built-in upgrade entrypoint time to finish,"$'\n' CMD_LINES+="# Give the container's built-in upgrade entrypoint time to finish,"$'\n'
CMD_LINES+="# then confirm the new version is active (watch for versionstring: ${CORE_NEW_VERSION})"$'\n' CMD_LINES+="# then confirm the new version is active (watch for versionstring: ${CORE_NEW_VERSION})"$'\n'
CMD_LINES+="sleep 30"$'\n' CMD_LINES+="sleep 30"$'\n'
@ -221,7 +247,6 @@ else
<pre style='margin:0;background:#1e293b;color:#e2e8f0;padding:14px;border-radius:6px;font-size:12px;line-height:1.6;overflow-x:auto;white-space:pre;'>${CMD_LINES}</pre> <pre style='margin:0;background:#1e293b;color:#e2e8f0;padding:14px;border-radius:6px;font-size:12px;line-height:1.6;overflow-x:auto;white-space:pre;'>${CMD_LINES}</pre>
</div>" </div>"
# --- Maintenance/upgrade block ----------------------------
MAINT_BLOCK="" MAINT_BLOCK=""
if [[ -n "$WARN_HTML" ]]; then if [[ -n "$WARN_HTML" ]]; then
MAINT_BLOCK="<div style='margin-top:20px;'> MAINT_BLOCK="<div style='margin-top:20px;'>
@ -230,9 +255,15 @@ else
</div>" </div>"
fi fi
# --- Core update block --------------------------------------
CORE_BLOCK="" CORE_BLOCK=""
if [[ "$CORE_UPDATE_AVAILABLE" == "true" ]]; then if [[ "$CORE_UPDATE_AVAILABLE" == "true" ]]; then
CORE_SOURCE_NOTE=""
if [[ -n "$CORE_UPDATE_SOURCE" ]]; then
CORE_SOURCE_NOTE="<tr>
<td style='font-size:13px;color:#6b7280;padding-top:6px;'>Detected Via</td>
<td style='font-size:13px;color:#374151;padding-top:6px;'>${CORE_UPDATE_SOURCE}</td>
</tr>"
fi
CORE_BLOCK="<div style='margin-top:20px;background:white;border-radius:10px;border:1px solid #e5e7eb;padding:18px 22px;'> CORE_BLOCK="<div style='margin-top:20px;background:white;border-radius:10px;border:1px solid #e5e7eb;padding:18px 22px;'>
<div style='font-size:13px;font-weight:700;color:#374151;margin-bottom:12px;text-transform:uppercase;letter-spacing:.5px;'>🆙 Core Update Available</div> <div style='font-size:13px;font-weight:700;color:#374151;margin-bottom:12px;text-transform:uppercase;letter-spacing:.5px;'>🆙 Core Update Available</div>
<table width='100%' cellspacing='0' cellpadding='0'> <table width='100%' cellspacing='0' cellpadding='0'>
@ -244,11 +275,12 @@ else
<td style='font-size:13px;color:#6b7280;'>New Version</td> <td style='font-size:13px;color:#6b7280;'>New Version</td>
<td style='font-size:13px;color:#b45309;font-weight:700;font-family:monospace;'>${CORE_NEW_VERSION}</td> <td style='font-size:13px;color:#b45309;font-weight:700;font-family:monospace;'>${CORE_NEW_VERSION}</td>
</tr> </tr>
${CORE_SOURCE_NOTE}
</table> </table>
<div style='margin-top:10px;font-size:12px;color:#6b7280;'>This install uses a custom <code style='font-family:monospace;'>nextcloud-ffmpeg:latest</code> image. The emailed commands rebuild that image first — do not run a plain <code style='font-family:monospace;'>docker compose pull</code> in the main project.</div>
</div>" </div>"
fi fi
# --- App update table -------------------------------------
APP_TABLE="" APP_TABLE=""
if [[ $APP_COUNT -gt 0 ]]; then if [[ $APP_COUNT -gt 0 ]]; then
APP_TABLE="<div style='margin-top:20px;background:white;border-radius:10px;border:1px solid #e5e7eb;padding:18px 22px;'> APP_TABLE="<div style='margin-top:20px;background:white;border-radius:10px;border:1px solid #e5e7eb;padding:18px 22px;'>
@ -271,7 +303,6 @@ else
BODY_HTML="${MAINT_BLOCK} BODY_HTML="${MAINT_BLOCK}
<!-- NC Version info -->
<div style='margin-top:20px;padding:12px 14px;background:#f8fafc;border-radius:6px;border:1px solid #e5e7eb;'> <div style='margin-top:20px;padding:12px 14px;background:#f8fafc;border-radius:6px;border:1px solid #e5e7eb;'>
<span style='font-size:12px;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:.5px;'>Nextcloud Version</span> <span style='font-size:12px;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:.5px;'>Nextcloud Version</span>
<span style='margin-left:10px;font-size:13px;color:#374151;font-family:monospace;'>${NC_VERSION}</span> <span style='margin-left:10px;font-size:13px;color:#374151;font-family:monospace;'>${NC_VERSION}</span>
@ -284,7 +315,6 @@ else
${CMD_BLOCK}" ${CMD_BLOCK}"
fi fi
# --- Determine email subject ---------------------------------
if [[ "$NEEDS_ACTION" == "true" ]]; then if [[ "$NEEDS_ACTION" == "true" ]]; then
SUBJECT="[NAS08] Nextcloud — ${STATUS_LABEL} — Action Required" SUBJECT="[NAS08] Nextcloud — ${STATUS_LABEL} — Action Required"
else else
@ -293,7 +323,6 @@ fi
log "Composing email: $SUBJECT" log "Composing email: $SUBJECT"
# --- Build full HTML email -----------------------------------
HTML_EMAIL=$(cat <<HTML HTML_EMAIL=$(cat <<HTML
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -323,12 +352,12 @@ HTML_EMAIL=$(cat <<HTML
</td></tr> </td></tr>
<!-- BODY --> <!-- BODY -->
<tr><td style="background:white;border-radius:0 0 10px 10px;padding:28px 32px;"> <tr><td style="background:white;padding:28px 32px;">
${BODY_HTML} ${BODY_HTML}
</td></tr> </td></tr>
<!-- FOOTER --> <!-- FOOTER -->
<tr><td style="background:#0f172a;border-radius:0 0 10px 10px;padding:16px 32px;text-align:center;margin-top:8px;"> <tr><td style="background:#0f172a;border-radius:0 0 10px 10px;padding:16px 32px;text-align:center;">
<span style="font-size:11px;color:#64748b;">Nextcloud Update Check &nbsp;|&nbsp; ${HOSTNAME_LABEL} &nbsp;|&nbsp; KingDezigns Infrastructure</span> <span style="font-size:11px;color:#64748b;">Nextcloud Update Check &nbsp;|&nbsp; ${HOSTNAME_LABEL} &nbsp;|&nbsp; KingDezigns Infrastructure</span>
</td></tr> </td></tr>
@ -340,7 +369,6 @@ HTML_EMAIL=$(cat <<HTML
HTML HTML
) )
# --- Send email via curl/SMTP --------------------------------
log "Sending email to ${TO_EMAIL}..." log "Sending email to ${TO_EMAIL}..."
SEND_RESULT=$(curl --silent --show-error \ SEND_RESULT=$(curl --silent --show-error \
@ -366,6 +394,5 @@ else
log "ERROR sending email: ${SEND_RESULT}" log "ERROR sending email: ${SEND_RESULT}"
fi fi
# --- Rotate old logs -----------------------------------------
find "$LOG_DIR" -name "update-check-*.log" -mtime +${LOG_RETENTION_DAYS} -delete find "$LOG_DIR" -name "update-check-*.log" -mtime +${LOG_RETENTION_DAYS} -delete
log "Done." log "Done."