feat(update): detect a stale root footprint on git and local installs
footprint_update_needed only ever came from comparing the installed marker against a channel manifest, so it could not fire on a git or local install — they have no channel to ask. Those are exactly the installs whose code tree is synced by hand, i.e. the ones most able to drift, and the drift was silent: the helpers in /usr/local/lib/libreportal could sit behind the code that calls them with nothing reporting it. That is how this box ended up running a crowdsec helper with no bouncer-traefik-rotate action while the tool that needs it shipped. init.sh is what bakes the marker, so the install tree's own init.sh is authoritative for every mode. lpInstallTreeFootprintVersion reads it and lpFootprintStale compares. Wired into both non-release branches of the WebUI status generator, and into the local branch of the interactive update check, which is where a local operator actually looks. Fails safe: a tree older than the marker, or a missing init.sh, reports current rather than warning — verified alongside the real stale case. Also gives webuiSystemUpdateCheck the self-reload guard webuiGenerateAppsToolsConfig already documents. The WebUI task service sources these once at startup, so without it an edited generator keeps writing the old JSON from memory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
552f102517
commit
bd10a9ab55
@ -68,6 +68,27 @@ lpVerifyMinisig() {
|
||||
# latter is greater, a plain manager-run update can't apply it (it can't rewrite
|
||||
# the root-owned helpers/wrapper/unit) — a root re-install is required.
|
||||
lpInstalledFootprintVersion() { local v; v=$(cat /usr/local/lib/libreportal/.footprint_version 2>/dev/null | tr -d ' \t\n\r'); echo "${v:-0}"; }
|
||||
|
||||
# The footprint version the CODE IN THE INSTALL TREE would bake if init.sh were
|
||||
# re-run. This is the only comparison available to a git or local install: those
|
||||
# have no channel to ask, so lpReleaseLatestFootprint cannot answer and the
|
||||
# staleness never surfaced — the root helpers silently stayed behind the code
|
||||
# that expects them. Reading init.sh directly works for every mode, because
|
||||
# init.sh is what bakes the marker in the first place.
|
||||
lpInstallTreeFootprintVersion() {
|
||||
local f="${script_dir%/}/init.sh" v=""
|
||||
[[ -f "$f" ]] && v=$(grep -m1 -oE '^footprint_version=[0-9]+' "$f" 2>/dev/null | cut -d= -f2)
|
||||
echo "${v:-0}"
|
||||
}
|
||||
|
||||
# True when what is baked into /usr/local/lib/libreportal is older than what the
|
||||
# install tree carries, i.e. a root re-install (`sudo <install>/init.sh`) is owed.
|
||||
lpFootprintStale() {
|
||||
local installed tree
|
||||
installed=$(lpInstalledFootprintVersion)
|
||||
tree=$(lpInstallTreeFootprintVersion)
|
||||
[[ "${tree:-0}" -gt "${installed:-0}" ]]
|
||||
}
|
||||
lpReleaseLatestFootprint() {
|
||||
local m; m="$(_lpDownload "$(lpReleaseBaseUrl)/$(lpReleaseChannel)/latest.json" - 2>/dev/null)" || return 1
|
||||
local f; f=$(_lpJsonNum "$m" footprint_version); echo "${f:-0}"
|
||||
|
||||
@ -658,9 +658,11 @@ declare -gA LP_FN_MAP=(
|
||||
[lpFetchRelease]="source/fetch.sh"
|
||||
[lpFetchSource]="source/fetch.sh"
|
||||
[_lpFetchTool]="source/fetch.sh"
|
||||
[lpFootprintStale]="source/fetch.sh"
|
||||
[lpIndexArtifactIds]="source/artifacts.sh"
|
||||
[lpIndexTop]="source/artifacts.sh"
|
||||
[lpInstalledFootprintVersion]="source/fetch.sh"
|
||||
[lpInstallTreeFootprintVersion]="source/fetch.sh"
|
||||
[_lpJsonEsc]="source/fetch.sh"
|
||||
[_lpJsonNum]="source/fetch.sh"
|
||||
[_lpJsonStr]="source/fetch.sh"
|
||||
@ -1803,9 +1805,11 @@ declare -gA LP_FN_ROOT=(
|
||||
[lpFetchRelease]="scripts"
|
||||
[lpFetchSource]="scripts"
|
||||
[_lpFetchTool]="scripts"
|
||||
[lpFootprintStale]="scripts"
|
||||
[lpIndexArtifactIds]="scripts"
|
||||
[lpIndexTop]="scripts"
|
||||
[lpInstalledFootprintVersion]="scripts"
|
||||
[lpInstallTreeFootprintVersion]="scripts"
|
||||
[_lpJsonEsc]="scripts"
|
||||
[_lpJsonNum]="scripts"
|
||||
[_lpJsonStr]="scripts"
|
||||
@ -2984,9 +2988,11 @@ lpFetchIndexInto() { unset -f lpFetchIndexInto; __lpAutoload "${install_scripts_
|
||||
lpFetchRelease() { unset -f lpFetchRelease; __lpAutoload "${install_scripts_dir}source/fetch.sh"; lpFetchRelease "$@"; }
|
||||
lpFetchSource() { unset -f lpFetchSource; __lpAutoload "${install_scripts_dir}source/fetch.sh"; lpFetchSource "$@"; }
|
||||
_lpFetchTool() { unset -f _lpFetchTool; __lpAutoload "${install_scripts_dir}source/fetch.sh"; _lpFetchTool "$@"; }
|
||||
lpFootprintStale() { unset -f lpFootprintStale; __lpAutoload "${install_scripts_dir}source/fetch.sh"; lpFootprintStale "$@"; }
|
||||
lpIndexArtifactIds() { unset -f lpIndexArtifactIds; __lpAutoload "${install_scripts_dir}source/artifacts.sh"; lpIndexArtifactIds "$@"; }
|
||||
lpIndexTop() { unset -f lpIndexTop; __lpAutoload "${install_scripts_dir}source/artifacts.sh"; lpIndexTop "$@"; }
|
||||
lpInstalledFootprintVersion() { unset -f lpInstalledFootprintVersion; __lpAutoload "${install_scripts_dir}source/fetch.sh"; lpInstalledFootprintVersion "$@"; }
|
||||
lpInstallTreeFootprintVersion() { unset -f lpInstallTreeFootprintVersion; __lpAutoload "${install_scripts_dir}source/fetch.sh"; lpInstallTreeFootprintVersion "$@"; }
|
||||
_lpJsonEsc() { unset -f _lpJsonEsc; __lpAutoload "${install_scripts_dir}source/fetch.sh"; _lpJsonEsc "$@"; }
|
||||
_lpJsonNum() { unset -f _lpJsonNum; __lpAutoload "${install_scripts_dir}source/fetch.sh"; _lpJsonNum "$@"; }
|
||||
_lpJsonStr() { unset -f _lpJsonStr; __lpAutoload "${install_scripts_dir}source/fetch.sh"; _lpJsonStr "$@"; }
|
||||
|
||||
@ -22,6 +22,14 @@ webuiRunUpdate()
|
||||
|
||||
if [[ "$install_mode" == "local" ]]; then
|
||||
isError "This is a local installation — updates are managed manually, nothing to pull."
|
||||
# The code tree is synced by hand here, so the root-owned footprint can
|
||||
# fall behind it without anything saying so — there is no channel
|
||||
# manifest to trigger the usual footprint_update_needed path.
|
||||
if declare -f lpFootprintStale >/dev/null 2>&1 && lpFootprintStale; then
|
||||
isNotice "Root footprint is stale: installed v$(lpInstalledFootprintVersion), install tree carries v$(lpInstallTreeFootprintVersion)."
|
||||
isNotice "The helpers in /usr/local/lib/libreportal are older than the code that calls them. Re-bake as root:"
|
||||
isNotice " sudo ${script_dir%/}/init.sh"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
if [[ "$git_updates" != "true" ]]; then
|
||||
@ -139,6 +147,15 @@ checkUpdates()
|
||||
# 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."
|
||||
# A local install syncs its code tree by hand, so the root-owned
|
||||
# footprint can fall behind that code with nothing to say so — there is
|
||||
# no channel manifest here to drive the usual footprint_update_needed
|
||||
# path. This is the one place a local operator reliably looks.
|
||||
if declare -f lpFootprintStale >/dev/null 2>&1 && lpFootprintStale; then
|
||||
isNotice "Root footprint is stale: installed v$(lpInstalledFootprintVersion), install tree carries v$(lpInstallTreeFootprintVersion)."
|
||||
isNotice "The helpers in /usr/local/lib/libreportal are older than the code calling them. Re-bake as root:"
|
||||
isNotice " sudo ${script_dir%/}/init.sh"
|
||||
fi
|
||||
if [[ $init_run_flag == "true" ]]; then
|
||||
startLoad;
|
||||
fi
|
||||
|
||||
@ -34,6 +34,21 @@ webuiSystemUpdate() {
|
||||
# Pass "force" as $1 to bypass the fetch throttle (used by the manual
|
||||
# "Check for updates" action: `libreportal update check`).
|
||||
webuiSystemUpdateCheck() {
|
||||
# Same hazard webuiGenerateAppsToolsConfig documents: the WebUI task service
|
||||
# is a long-lived bash process that sources this file once at startup, so an
|
||||
# updated function on disk is ignored until the service restarts. That is
|
||||
# not cosmetic here — it silently kept writing the OLD update_status.json,
|
||||
# which is how the footprint check below appeared to do nothing after it was
|
||||
# added. Re-source ourselves and dispatch to the fresh copy; the guard stops
|
||||
# the recursion, and a missing file (briefly, mid-update) falls through to
|
||||
# the in-memory version rather than failing.
|
||||
if [[ -z "$_WEBUI_SYSUPDATE_RELOADED" && -f "${BASH_SOURCE[0]}" ]]; then
|
||||
local _WEBUI_SYSUPDATE_RELOADED=1
|
||||
source "${BASH_SOURCE[0]}"
|
||||
webuiSystemUpdateCheck "$@"
|
||||
return $?
|
||||
fi
|
||||
|
||||
local force_flag="$1"
|
||||
|
||||
local repo_dir="${script_dir}"
|
||||
@ -153,9 +168,14 @@ EOF
|
||||
# Not a git working copy, or a deliberately local install: we can't compare
|
||||
# against an upstream, so report "managed manually" rather than an error.
|
||||
if [[ ! -d "$repo_dir/.git" || "$install_mode" == "local" ]]; then
|
||||
# There is no channel to compare against, but the install tree's own
|
||||
# init.sh still says what the root footprint should be — so a stale
|
||||
# footprint is detectable here, and used to go unreported entirely.
|
||||
local _fp_local="false"
|
||||
declare -f lpFootprintStale >/dev/null 2>&1 && lpFootprintStale && _fp_local="true"
|
||||
_webuiWriteUpdateStatus "false" "false" \
|
||||
"$current_version" "$current_version" \
|
||||
"" "" "0" "0" "" "local" ""
|
||||
"" "" "0" "0" "" "local" "" "$_fp_local"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@ -219,10 +239,16 @@ EOF
|
||||
local can_update="false"
|
||||
[[ "$install_mode" == "git" && "$git_updates" == "true" ]] && can_update="true"
|
||||
|
||||
# Same for a git install: no channel manifest, but init.sh in the working
|
||||
# copy is authoritative about what the footprint should be.
|
||||
local _fp_git="false"
|
||||
declare -f lpFootprintStale >/dev/null 2>&1 && lpFootprintStale && _fp_git="true"
|
||||
[[ "$_fp_git" == "true" ]] && can_update="false"
|
||||
|
||||
_webuiWriteUpdateStatus "$update_available" "$can_update" \
|
||||
"$current_version" "$latest_version" \
|
||||
"$current_commit" "$latest_commit" \
|
||||
"$behind" "$ahead" "$branch" "git" "$fetch_error"
|
||||
"$behind" "$ahead" "$branch" "git" "$fetch_error" "$_fp_git"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user