Compare commits
2 Commits
95e7267e3e
...
9e5b336d1e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e5b336d1e | ||
|
|
96b04392dc |
@ -2,6 +2,7 @@
|
|||||||
# Terminal - System utilities and advanced settings **ADVANCED**
|
# Terminal - System utilities and advanced settings **ADVANCED**
|
||||||
# ================================================================================
|
# ================================================================================
|
||||||
CFG_UPDATER_CHECK=60 # Update Check Interval - Hours between system update checks
|
CFG_UPDATER_CHECK=60 # Update Check Interval - Hours between system update checks
|
||||||
|
CFG_HOTFIX_AUTO=security-breakage # Hotfix Auto-Apply - Which signed hotfix severities apply automatically on the update check [security-breakage|all|off]
|
||||||
CFG_SWAPFILE_SIZE=2G # Swap File Size - Size of swap file for memory management
|
CFG_SWAPFILE_SIZE=2G # Swap File Size - Size of swap file for memory management
|
||||||
CFG_GENERATED_PASS_LENGTH=14 # Password Length - Length for auto generated passwords
|
CFG_GENERATED_PASS_LENGTH=14 # Password Length - Length for auto generated passwords
|
||||||
CFG_GENERATED_USER_LENGTH=8 # Username Length - Length for auto generated usernames
|
CFG_GENERATED_USER_LENGTH=8 # Username Length - Length for auto generated usernames
|
||||||
|
|||||||
@ -521,3 +521,45 @@ artifactRevert() {
|
|||||||
isError "Revert of $id was incomplete -- the applied-record is kept so you can retry: libreportal artifact revert $id (MANUAL recovery may be needed)."
|
isError "Revert of $id was incomplete -- the applied-record is kept so you can retry: libreportal artifact revert $id (MANUAL recovery may be needed)."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# artifactApplyAuto -- enqueue apply tasks for the auto-eligible hotfixes, gated
|
||||||
|
# by CFG_HOTFIX_AUTO (security-breakage|all|off). Called from `updater check`.
|
||||||
|
# Only acts on a VERIFIED-signed index; only artifacts with auto==true, in the
|
||||||
|
# severity policy, applicable, and not already applied. Each apply is enqueued as
|
||||||
|
# its own task (visible in the task log + History) -- never applied inline here.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
artifactApplyAuto() {
|
||||||
|
_artifactNeedJq || return 0
|
||||||
|
local policy="${CFG_HOTFIX_AUTO:-security-breakage}"
|
||||||
|
[[ "$policy" == "off" ]] && { isNotice "Hotfix auto-apply is off (CFG_HOTFIX_AUTO=off)."; return 0; }
|
||||||
|
|
||||||
|
local index; index="$(lpFetchIndex)" || { isNotice "artifact apply-auto: no index available."; return 0; }
|
||||||
|
if [[ "$LP_INDEX_SIGSTATE" != "verified" ]]; then
|
||||||
|
isNotice "artifact apply-auto: index is unsigned (signing not activated) — not auto-applying."; return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ids id art sev app installed enqueued=0
|
||||||
|
ids="$(printf '%s' "$index" | jq -r '.artifacts[]? | select(.auto==true and (.type=="hotfix")) | .id' 2>/dev/null)"
|
||||||
|
while IFS= read -r id; do
|
||||||
|
[[ -z "$id" ]] && continue
|
||||||
|
art="$(printf '%s' "$index" | jq -ce --arg id "$id" '.artifacts[]?|select(.id==$id)' 2>/dev/null)"
|
||||||
|
[[ -n "$art" ]] || continue
|
||||||
|
sev="$(jq -r '.severity // "tweak"' <<<"$art")"
|
||||||
|
case "$policy" in
|
||||||
|
all) : ;;
|
||||||
|
*) [[ "$sev" == "security" || "$sev" == "breakage" ]] || continue ;; # security-breakage (default)
|
||||||
|
esac
|
||||||
|
# skip if already applied
|
||||||
|
[[ -f "$(_artifactRecordFile "$id")" ]] && continue
|
||||||
|
# skip if app-scoped but the app isn't installed (applicable gate; full
|
||||||
|
# gates re-checked at apply time)
|
||||||
|
app="$(jq -r '.applies_when.app // empty' <<<"$art")"
|
||||||
|
[[ -n "$app" && ! -d "${containers_dir%/}/$app" ]] && continue
|
||||||
|
cliTaskRun "libreportal artifact apply $id" "artifact_apply" "$id" ""
|
||||||
|
enqueued=$((enqueued + 1))
|
||||||
|
done <<< "$ids"
|
||||||
|
|
||||||
|
if (( enqueued > 0 )); then isSuccessful "Queued $enqueued auto-hotfix(es) for apply (policy: $policy)."
|
||||||
|
else isNotice "No new auto-hotfixes to apply (policy: $policy)."; fi
|
||||||
|
}
|
||||||
|
|||||||
@ -59,6 +59,10 @@ cliHandleArtifactCommands()
|
|||||||
cliTaskRun "libreportal artifact revert $id" "artifact_revert" "$id" ""
|
cliTaskRun "libreportal artifact revert $id" "artifact_revert" "$id" ""
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
"apply-auto")
|
||||||
|
# Decide + enqueue (gated by CFG_HOTFIX_AUTO); each apply is its own task.
|
||||||
|
artifactApplyAuto
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
cliShowArtifactHelp
|
cliShowArtifactHelp
|
||||||
;;
|
;;
|
||||||
|
|||||||
@ -30,6 +30,16 @@ cliHandleUpdaterCommands()
|
|||||||
source "$install_scripts_dir/webui/data/generators/updater/webui_updater_scan.sh" 2>/dev/null
|
source "$install_scripts_dir/webui/data/generators/updater/webui_updater_scan.sh" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
webuiUpdaterScan
|
webuiUpdaterScan
|
||||||
|
# Hotfix channel: refresh the signed artifact index for the WebUI, then
|
||||||
|
# auto-apply the eligible signed hotfixes (gated by CFG_HOTFIX_AUTO).
|
||||||
|
if ! declare -F webuiArtifactScan >/dev/null 2>&1; then
|
||||||
|
source "$install_scripts_dir/webui/data/generators/updater/webui_artifact_scan.sh" 2>/dev/null
|
||||||
|
fi
|
||||||
|
declare -F webuiArtifactScan >/dev/null 2>&1 && webuiArtifactScan
|
||||||
|
if ! declare -F artifactApplyAuto >/dev/null 2>&1; then
|
||||||
|
source "$install_scripts_dir/cli/commands/artifact/cli_artifact_apply.sh" 2>/dev/null
|
||||||
|
fi
|
||||||
|
declare -F artifactApplyAuto >/dev/null 2>&1 && artifactApplyAuto
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"apply"|"now")
|
"apply"|"now")
|
||||||
|
|||||||
@ -33,6 +33,7 @@ webui_scripts=(
|
|||||||
"webui/data/generators/system/webui_system_memory.sh"
|
"webui/data/generators/system/webui_system_memory.sh"
|
||||||
"webui/data/generators/system/webui_system_metrics.sh"
|
"webui/data/generators/system/webui_system_metrics.sh"
|
||||||
"webui/data/generators/system/webui_system_update.sh"
|
"webui/data/generators/system/webui_system_update.sh"
|
||||||
|
"webui/data/generators/updater/webui_artifact_scan.sh"
|
||||||
"webui/data/generators/updater/webui_updater_scan.sh"
|
"webui/data/generators/updater/webui_updater_scan.sh"
|
||||||
"webui/data/lock/webui_check_update_lock.sh"
|
"webui/data/lock/webui_check_update_lock.sh"
|
||||||
"webui/data/lock/webui_create_update_lock.sh"
|
"webui/data/lock/webui_create_update_lock.sh"
|
||||||
|
|||||||
@ -103,6 +103,7 @@ declare -gA LP_FN_MAP=(
|
|||||||
[appWebuiRefresh_gluetun]="gluetun/scripts/gluetun_providers.sh"
|
[appWebuiRefresh_gluetun]="gluetun/scripts/gluetun_providers.sh"
|
||||||
[_artifactAppliedDir]="cli/commands/artifact/cli_artifact_apply.sh"
|
[_artifactAppliedDir]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
[artifactApply]="cli/commands/artifact/cli_artifact_apply.sh"
|
[artifactApply]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
|
[artifactApplyAuto]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
[_artifactComposeImage]="cli/commands/artifact/cli_artifact_apply.sh"
|
[_artifactComposeImage]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
[_artifactFetchPayload]="cli/commands/artifact/cli_artifact_apply.sh"
|
[_artifactFetchPayload]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
[_artifactGenDir]="cli/commands/artifact/cli_artifact_apply.sh"
|
[_artifactGenDir]="cli/commands/artifact/cli_artifact_apply.sh"
|
||||||
@ -889,6 +890,7 @@ declare -gA LP_FN_MAP=(
|
|||||||
[viewLibrePortalConfigs]="config/core/config_manage_menu.sh"
|
[viewLibrePortalConfigs]="config/core/config_manage_menu.sh"
|
||||||
[viewLogs]="logs/installed_apps.sh"
|
[viewLogs]="logs/installed_apps.sh"
|
||||||
[viewLogsAppMenu]="logs/app_log_menu.sh"
|
[viewLogsAppMenu]="logs/app_log_menu.sh"
|
||||||
|
[webuiArtifactScan]="webui/data/generators/updater/webui_artifact_scan.sh"
|
||||||
[webuiCheckUpdateLock]="webui/data/lock/webui_check_update_lock.sh"
|
[webuiCheckUpdateLock]="webui/data/lock/webui_check_update_lock.sh"
|
||||||
[webuiContainerSetup]="webui/data/utils/webui_container_setup.sh"
|
[webuiContainerSetup]="webui/data/utils/webui_container_setup.sh"
|
||||||
[webuiCreateAppFieldMappings]="webui/data/generators/categories/webui_create_app_field_mappings.sh"
|
[webuiCreateAppFieldMappings]="webui/data/generators/categories/webui_create_app_field_mappings.sh"
|
||||||
@ -1043,6 +1045,7 @@ declare -gA LP_FN_ROOT=(
|
|||||||
[appWebuiRefresh_gluetun]="containers"
|
[appWebuiRefresh_gluetun]="containers"
|
||||||
[_artifactAppliedDir]="scripts"
|
[_artifactAppliedDir]="scripts"
|
||||||
[artifactApply]="scripts"
|
[artifactApply]="scripts"
|
||||||
|
[artifactApplyAuto]="scripts"
|
||||||
[_artifactComposeImage]="scripts"
|
[_artifactComposeImage]="scripts"
|
||||||
[_artifactFetchPayload]="scripts"
|
[_artifactFetchPayload]="scripts"
|
||||||
[_artifactGenDir]="scripts"
|
[_artifactGenDir]="scripts"
|
||||||
@ -1829,6 +1832,7 @@ declare -gA LP_FN_ROOT=(
|
|||||||
[viewLibrePortalConfigs]="scripts"
|
[viewLibrePortalConfigs]="scripts"
|
||||||
[viewLogs]="scripts"
|
[viewLogs]="scripts"
|
||||||
[viewLogsAppMenu]="scripts"
|
[viewLogsAppMenu]="scripts"
|
||||||
|
[webuiArtifactScan]="scripts"
|
||||||
[webuiCheckUpdateLock]="scripts"
|
[webuiCheckUpdateLock]="scripts"
|
||||||
[webuiContainerSetup]="scripts"
|
[webuiContainerSetup]="scripts"
|
||||||
[webuiCreateAppFieldMappings]="scripts"
|
[webuiCreateAppFieldMappings]="scripts"
|
||||||
@ -2004,6 +2008,7 @@ appUpdateSpecifics_pihole() { source "${install_containers_dir}pihole/scripts/pi
|
|||||||
appWebuiRefresh_gluetun() { source "${install_containers_dir}gluetun/scripts/gluetun_providers.sh"; appWebuiRefresh_gluetun "$@"; }
|
appWebuiRefresh_gluetun() { source "${install_containers_dir}gluetun/scripts/gluetun_providers.sh"; appWebuiRefresh_gluetun "$@"; }
|
||||||
_artifactAppliedDir() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactAppliedDir "$@"; }
|
_artifactAppliedDir() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactAppliedDir "$@"; }
|
||||||
artifactApply() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; artifactApply "$@"; }
|
artifactApply() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; artifactApply "$@"; }
|
||||||
|
artifactApplyAuto() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; artifactApplyAuto "$@"; }
|
||||||
_artifactComposeImage() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactComposeImage "$@"; }
|
_artifactComposeImage() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactComposeImage "$@"; }
|
||||||
_artifactFetchPayload() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactFetchPayload "$@"; }
|
_artifactFetchPayload() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactFetchPayload "$@"; }
|
||||||
_artifactGenDir() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactGenDir "$@"; }
|
_artifactGenDir() { source "${install_scripts_dir}cli/commands/artifact/cli_artifact_apply.sh"; _artifactGenDir "$@"; }
|
||||||
@ -2790,6 +2795,7 @@ viewConfigs() { source "${install_scripts_dir}config/core/config_main_menu.sh";
|
|||||||
viewLibrePortalConfigs() { source "${install_scripts_dir}config/core/config_manage_menu.sh"; viewLibrePortalConfigs "$@"; }
|
viewLibrePortalConfigs() { source "${install_scripts_dir}config/core/config_manage_menu.sh"; viewLibrePortalConfigs "$@"; }
|
||||||
viewLogs() { source "${install_scripts_dir}logs/installed_apps.sh"; viewLogs "$@"; }
|
viewLogs() { source "${install_scripts_dir}logs/installed_apps.sh"; viewLogs "$@"; }
|
||||||
viewLogsAppMenu() { source "${install_scripts_dir}logs/app_log_menu.sh"; viewLogsAppMenu "$@"; }
|
viewLogsAppMenu() { source "${install_scripts_dir}logs/app_log_menu.sh"; viewLogsAppMenu "$@"; }
|
||||||
|
webuiArtifactScan() { source "${install_scripts_dir}webui/data/generators/updater/webui_artifact_scan.sh"; webuiArtifactScan "$@"; }
|
||||||
webuiCheckUpdateLock() { source "${install_scripts_dir}webui/data/lock/webui_check_update_lock.sh"; webuiCheckUpdateLock "$@"; }
|
webuiCheckUpdateLock() { source "${install_scripts_dir}webui/data/lock/webui_check_update_lock.sh"; webuiCheckUpdateLock "$@"; }
|
||||||
webuiContainerSetup() { source "${install_scripts_dir}webui/data/utils/webui_container_setup.sh"; webuiContainerSetup "$@"; }
|
webuiContainerSetup() { source "${install_scripts_dir}webui/data/utils/webui_container_setup.sh"; webuiContainerSetup "$@"; }
|
||||||
webuiCreateAppFieldMappings() { source "${install_scripts_dir}webui/data/generators/categories/webui_create_app_field_mappings.sh"; webuiCreateAppFieldMappings "$@"; }
|
webuiCreateAppFieldMappings() { source "${install_scripts_dir}webui/data/generators/categories/webui_create_app_field_mappings.sh"; webuiCreateAppFieldMappings "$@"; }
|
||||||
|
|||||||
79
scripts/webui/data/generators/updater/webui_artifact_scan.sh
Normal file
79
scripts/webui/data/generators/updater/webui_artifact_scan.sh
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# WebUI artifact-index data generator
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Writes the read-only JSON the updater "Improvements" (hotfix) stream reads:
|
||||||
|
# frontend/data/updater/generated/artifacts_available.json
|
||||||
|
#
|
||||||
|
# It fetches + verifies the signed artifact index (lpFetchIndex), annotates each
|
||||||
|
# artifact with `applied` (a per-id applied-record exists) and `applicable` (the
|
||||||
|
# target app is installed / system-scope), and writes ATOMICALLY: build into a
|
||||||
|
# temp, validate it, then one runFileWrite. On ANY fetch/verify failure it KEEPS
|
||||||
|
# the prior file — it must never emit broken JSON, and the WebUI degrades
|
||||||
|
# gracefully when the file is absent. Run by `libreportal updater check`.
|
||||||
|
|
||||||
|
webuiArtifactScan() {
|
||||||
|
local out_dir="${containers_dir%/}/libreportal/frontend/data/updater/generated"
|
||||||
|
local out="$out_dir/artifacts_available.json"
|
||||||
|
local applied_dir="$out_dir/applied"
|
||||||
|
runFileOp mkdir -p "$out_dir" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Lazy-loader gap: ensure the read primitives are present.
|
||||||
|
if ! declare -F lpFetchIndex >/dev/null 2>&1; then
|
||||||
|
source "$install_scripts_dir/source/fetch.sh" 2>/dev/null
|
||||||
|
source "$install_scripts_dir/source/artifacts.sh" 2>/dev/null
|
||||||
|
fi
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
isNotice "webuiArtifactScan: jq not available — keeping the prior artifacts_available.json."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local index
|
||||||
|
if ! index="$(lpFetchIndex)"; then
|
||||||
|
isNotice "webuiArtifactScan: no verified index available — keeping the prior file."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local signed="false"; [[ "$LP_INDEX_SIGSTATE" == "verified" ]] && signed="true"
|
||||||
|
local serial now
|
||||||
|
serial="$(_lpJsonNum "$index" index_serial)"
|
||||||
|
now="$(date -Iseconds 2>/dev/null || date)"
|
||||||
|
|
||||||
|
# Applied ids = basenames of applied/*.json, as a JSON array.
|
||||||
|
local applied_ids="[]" f
|
||||||
|
if compgen -G "$applied_dir/*.json" >/dev/null 2>&1; then
|
||||||
|
applied_ids="$(for f in "$applied_dir"/*.json; do basename "$f" .json; done | jq -R . | jq -cs .)"
|
||||||
|
fi
|
||||||
|
# Installed apps = dir names under containers_dir, for the applicable check.
|
||||||
|
local installed="[]" d
|
||||||
|
if [[ -d "$containers_dir" ]]; then
|
||||||
|
installed="$(for d in "$containers_dir"/*/; do [[ -d "$d" ]] && basename "$d"; done | jq -R . | jq -cs .)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmp; tmp="$(mktemp)"
|
||||||
|
printf '%s' "$index" | jq \
|
||||||
|
--arg now "$now" --arg signed "$signed" --arg serial "${serial:-0}" \
|
||||||
|
--argjson applied "$applied_ids" --argjson installed "$installed" '
|
||||||
|
{ generated_at: $now,
|
||||||
|
signed: ($signed=="true"),
|
||||||
|
serial: ($serial|tonumber? // 0),
|
||||||
|
artifacts: [ .artifacts[]? | {
|
||||||
|
id, type,
|
||||||
|
severity: (.severity // "tweak"),
|
||||||
|
auto: (.auto // false),
|
||||||
|
reversible: (.reversible // true),
|
||||||
|
title: (.title // ""),
|
||||||
|
why: (.why // ""),
|
||||||
|
app: (.applies_when.app // null),
|
||||||
|
applied: (.id as $i | ($applied | index($i)) != null),
|
||||||
|
applicable: ((.applies_when.app // null) as $a | if $a == null then true else ($installed | index($a)) != null end)
|
||||||
|
} ] }' > "$tmp" 2>/dev/null
|
||||||
|
|
||||||
|
if ! jq empty "$tmp" 2>/dev/null; then
|
||||||
|
isNotice "webuiArtifactScan: generated JSON was invalid — keeping the prior file."
|
||||||
|
rm -f "$tmp"; return 1
|
||||||
|
fi
|
||||||
|
runFileWrite "$out" < "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
runFileOp chown "$docker_install_user":"$docker_install_user" "$out" 2>/dev/null || true
|
||||||
|
isSuccessful "Artifact index refreshed (serial=${serial:-?}, signed=$signed)."
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user