LibrePortal/scripts/update/check_update.sh
librelad acfe7d6bfa feat(update): release-aware update detection + apply (phase D)
Make the WebUI updater work off release versions, not git commits, in release mode
(git/local paths untouched):

- webui_system_update.sh: a release branch resolves latest_version from the channel
  manifest (lpReleaseLatestVersion), computes update_available via lpVersionGt vs
  the local VERSION, reuses the same throttle + the same update_status.json schema
  (source="release"); reuses last-known latest when throttled so the badge
  doesn't flicker.
- check_update.sh webuiRunUpdate: a release branch version-compares and, if newer,
  lpFetchRelease (download + checksum-verify) the new tarball + dockerInstallApp
  redeploy + regen. No config-backup dance — lpFetchRelease replaces only the
  install tree; configs/logs are in the separate system tree.

Verified against a local server: latest-version read + the no-update / update-
available decision (0.2.0==0.2.0 no; 0.3.0>0.2.0 yes). Remaining: route the
reset/reinstall recovery paths through the release fetch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-25 18:13:09 +01:00

235 lines
8.1 KiB
Bash
Executable File

#!/bin/bash
# webuiRunUpdate — non-interactive LibrePortal update for the WebUI.
#
# Invoked as a task via `libreportal update apply` (see cli_update_commands.sh),
# so it runs under the task processor with stdin closed and
# LIBREPORTAL_NONINTERACTIVE=1. It must therefore never block on a prompt:
# every decision is resolved up front from config, and it bails out cleanly
# (non-zero) instead of asking a question.
#
# Flow: guard -> forced update check -> if behind, run the proven file update
# (gitPerformUpdate) -> redeploy the portal so the new WebUI is live ->
# regenerate WebUI data and refresh the out-of-date status.
webuiRunUpdate()
{
isHeader "LibrePortal Update"
sourceCheckFiles;
local install_mode="${CFG_INSTALL_MODE:-git}"
local git_updates="${CFG_GIT_UPDATES:-true}"
if [[ "$install_mode" == "local" ]]; then
isError "This is a local installation — updates are managed manually, nothing to pull."
return 1
fi
if [[ "$git_updates" != "true" ]]; then
isError "Updates are disabled (CFG_GIT_UPDATES). Enable them to update from the WebUI."
return 1
fi
# Release mode: version-compare the channel's latest against the local VERSION
# and, if newer, fetch + verify the new tarball and redeploy. No config backup
# dance — lpFetchRelease replaces only the install tree; configs/logs live in
# the separate system tree.
if [[ "$install_mode" == "release" ]]; then
webuiSystemUpdateCheck "force"
local cur lat
cur=$(tr -d ' \t\n\r' < "$script_dir/VERSION" 2>/dev/null)
lat=$(lpReleaseLatestVersion 2>/dev/null)
if [[ -z "$lat" ]]; then
isError "Could not reach the release server ($(lpReleaseBaseUrl))."
return 1
fi
if ! lpVersionGt "$lat" "$cur"; then
isSuccessful "LibrePortal is already up to date (v${cur})."
return 0
fi
isNotice "Update found — v${cur} → v${lat}. Fetching the verified release..."
lpFetchRelease "$lat" || { isError "Release fetch failed — install unchanged."; return 1; }
isNotice "Redeploying LibrePortal with the new version..."
dockerInstallApp "libreportal"
WEBUI_UPDATER_FORCE=1 webuiLibrePortalUpdate
webuiSystemUpdateCheck "force"
isSuccessful "LibrePortal has been updated to v${lat}."
return 0
fi
# Credential guard — gitReset()/gitCheckGitDetails() would otherwise prompt
# for missing git details and hang the task forever. Fail fast with a clear
# message instead.
if [[ "$install_mode" == "git" ]]; then
if [[ -z "$CFG_GIT_USER" || "$CFG_GIT_USER" == "changeme" ]]; then
isError "Git credentials are not configured. Run the 'libreportal' command once on the host, then retry."
return 1
fi
if [[ "$CFG_GIT_USER" != "empty" && ( -z "$CFG_GIT_KEY" || "$CFG_GIT_KEY" == "changeme" ) ]]; then
isError "Git access token is not configured. Run the 'libreportal' command once on the host, then retry."
return 1
fi
fi
cd "$script_dir" || { isError "Cannot access the install directory ($script_dir)."; return 1; }
runAsManager git config core.fileMode false
# Force a fresh fetch + status write so the decision below (and the badge)
# reflect reality right now, not a stale throttled snapshot.
webuiSystemUpdateCheck "force"
local branch behind
branch=$(runAsManager git -C "$script_dir" rev-parse --abbrev-ref HEAD 2>/dev/null)
[[ -z "$branch" || "$branch" == "HEAD" ]] && branch="main"
behind=$(runAsManager git -C "$script_dir" rev-list --count "HEAD..refs/remotes/origin/$branch" 2>/dev/null)
[[ -z "$behind" ]] && behind=0
if [[ "$behind" -eq 0 ]]; then
isSuccessful "LibrePortal is already up to date."
return 0
fi
isNotice "Update found — $behind commit(s) behind origin/$branch. Updating now..."
# Proven file-level update: back up configs/logs, re-clone, restore.
gitPerformUpdate;
# Redeploy the portal from the freshly pulled source so the new WebUI goes
# live. This is exactly what `libreportal app install libreportal` does, so
# it's already safe to run non-interactively. It restarts the container; the
# task processor runs on the host, so this task survives the restart.
isNotice "Redeploying LibrePortal with the new version..."
dockerInstallApp "libreportal"
# Regenerate WebUI data (new version, configs, etc.) and clear the
# out-of-date flag.
WEBUI_UPDATER_FORCE=1 webuiLibrePortalUpdate
webuiSystemUpdateCheck "force"
isSuccessful "LibrePortal has been updated."
}
checkUpdates()
{
local param1="$1"
sourceCheckFiles;
# Skip Git updates if installation mode is local or Git updates are disabled
if [[ $CFG_INSTALL_MODE == "local" ]]; then
isNotice "Local installation detected - Git updates are disabled."
if [[ $init_run_flag == "true" ]]; then
startLoad;
fi
return
fi
if [[ $CFG_GIT_UPDATES == "true" ]]; then
isHeader "Checking for Updates"
# Ask user if they want to check for updates
while true; do
if [[ $CFG_GIT_UPDATES == "false" ]]; then
isQuestion "Would you like to check for updates? (y/n): "
read -p "" check_updates_choice
elif [[ $CFG_GIT_UPDATES == "true" ]]; then
check_updates_choice=y
fi
case $check_updates_choice in
[yY])
# DNS Query Test with Quad9
isNotice "Testing internet DNS, please wait..."
if command -v dig >/dev/null 2>&1; then
dns_check_cmd="dig +short +time=3 +tries=1 @9.9.9.9 quad9.net"
elif command -v getent >/dev/null 2>&1; then
dns_check_cmd="getent hosts quad9.net"
else
dns_check_cmd="ping -c 1 -W 3 9.9.9.9"
fi
if $dns_check_cmd >/dev/null 2>&1; then
isSuccessful "Internet DNS is working."
else
isError "Internet DNS is not working."
exit 1
fi
cd "$script_dir" || { isError " Cannot navigate to the repository directory"; exit 1; }
# Update Git to ignore changes in file permissions
runAsManager git config core.fileMode false
# Update Git with email address
runAsManager git config --global user.name "$CFG_INSTALL_NAME"
runAsManager git config --global user.email "noreply@${CFG_INSTALL_NAME,,}.libreportal.local"
# Check if there are edited (modified) files
if git status --porcelain | grep -q "^ M"; then
isNotice "There are uncommitted changes in the repository."
while true; do
isQuestion "Do you want to discard these changes and update the repository? (y/n): "
read -p "" customupdatesfound
case $customupdatesfound in
[yY])
remove_changes=true
gitCheckForUpdate;
gitCheckConfigs;
fixPermissionsBeforeStart "" "update";
sourceCheckFiles;
if [[ $init_run_flag == "true" ]]; then
isSuccessful "Starting/Restarting LibrePortal"
startLoad;
fi
;;
[nN])
isNotice "Custom changes will be kept, continuing..."
remove_changes=false
gitCheckForUpdate;
gitCheckConfigs;
fixPermissionsBeforeStart "" "update";
sourceCheckFiles;
if [[ $init_run_flag == "true" ]]; then
startLoad;
fi
;;
*)
isNotice "Please provide a valid input (y or n)."
;;
esac
done
fi
# Make sure an update happens after custom code check
if [[ $update_done != "true" ]]; then
gitCheckForUpdate;
gitCheckConfigs;
fixPermissionsBeforeStart "" "update";
sourceCheckFiles;
if [[ $init_run_flag == "true" ]]; then
isSuccessful "Starting/Restarting LibrePortal"
startLoad;
fi
fi
;;
[nN])
echo ""
isNotice "Skipping update check. Starting application..."
if [[ $init_run_flag == "true" ]]; then
startLoad;
fi
;;
*)
isNotice "Please enter y or n."
;;
esac
done
else
if [[ $init_run_flag == "true" ]]; then
startLoad;
fi
fi
}