LibrePortal/scripts/webui/webui_regen.sh
librelad 899e04bcd3 feat(regen): unified regeneration front door + self-heal poll
Add `lpRegen` (scripts/webui/webui_regen.sh) — one entry point that rebuilds the
file-derived artifacts whose sources changed, so callers don't have to know which
generator owns what. Self-heal is a cheap `find -newer` mtime compare (no watcher
/ daemon): a stage runs only when a source is newer than its artifact, or --force.

- `libreportal regen [all|webui|arrays] [--force]` CLI command (new category).
- Task processor idle tick runs a throttled `regen webui` poll, so an app dropped
  in out-of-band (drag-drop / marketplace) appears on its own — no manual command,
  no inotify (works on the relocatable/external-drive roots where inotify can't).
- make_release.sh guards against shipping stale source arrays (regenerate; abort
  if the committed tree was out of date), killing the "forgot generate_arrays" bug
  class at the build boundary.
- Document the front door in DEVELOPMENT.md.

webui scope rebuilds from containers/<app>/{*.config,tools/*.tools.json}; arrays
scope from scripts/** (a dev/build concern — a no-op on a normal install). Gate
logic verified in a sandbox (clean/config-newer/tools-newer/force/missing).

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

83 lines
3.2 KiB
Bash

#!/bin/bash
# Unified regeneration front door.
#
# One entry point that rebuilds the file-derived artifacts whose SOURCES changed,
# so callers (the CLI, the task-processor poll, a deploy) don't have to know which
# generator owns what. Runs as the manager and writes only into the data dirs — no
# privilege needed.
#
# lpRegen [scope] [--force]
# scope: all (default) | webui | arrays
#
# Self-heal: a stage runs only when a source file is newer than its artifact (or
# the artifact is missing), unless --force. The check is a cheap `find -newer`
# mtime compare — there is no watcher/daemon. The natural triggers (install,
# config change, deploy, the periodic task-processor poll) call this; it no-ops
# when nothing is stale, so it is safe to call often.
#
# webui — apps.json / apps-tools.json etc. from containers/<app>/{*.config,tools/*.tools.json}
# arrays — the static files_*.sh source arrays from scripts/** (a dev/build concern;
# a normal install never adds core scripts, so this is a no-op there)
# Is $artifact stale relative to the given find expression? Returns 0 (stale) when
# the artifact is missing or any matched source file is newer than it.
_lpRegenStale() {
local artifact="$1"; shift
[[ -f "$artifact" ]] || return 0
find "$@" -newer "$artifact" -print -quit 2>/dev/null | grep -q .
}
lpRegenWebui() {
local force="$1"
local gen="${containers_dir}libreportal/frontend/data/apps/generated"
local apps_json="$gen/apps.json"
local tools_json="$gen/apps-tools.json"
if [[ "$force" == "force" ]] \
|| _lpRegenStale "$apps_json" "$install_containers_dir" -maxdepth 2 -name '*.config' \
|| _lpRegenStale "$tools_json" "$install_containers_dir" -maxdepth 3 -path '*/tools/*.tools.json'; then
# Sources changed (e.g. an app folder was dropped in) — do the full,
# debounced refresh so the new app appears everywhere. Force past the
# updater's own debounce: we have already established there is real work.
WEBUI_UPDATER_FORCE=1 webuiLibrePortalUpdate
return $?
fi
return 0
}
lpRegenArrays() {
local force="$1"
local arrays_dir="${install_scripts_dir}source/files/arrays"
local gen_script="${install_scripts_dir}source/files/generate_arrays.sh"
local newest_array
newest_array="$(ls -t "$arrays_dir"/files_*.sh 2>/dev/null | head -1)"
if [[ "$force" == "force" ]] || [[ -z "$newest_array" ]] \
|| find "$install_scripts_dir" -name '*.sh' -newer "$newest_array" -print -quit 2>/dev/null | grep -q .; then
[[ -f "$gen_script" ]] && "$gen_script" run
return $?
fi
return 0
}
lpRegen() {
local scope="all" force=""
local a
for a in "$@"; do
case "$a" in
--force|-f|force) force="force" ;;
all|webui|arrays) scope="$a" ;;
*) isNotice "Unknown regen argument: $a (use: all|webui|arrays [--force])" ;;
esac
done
local rc=0
case "$scope" in
arrays) lpRegenArrays "$force" || rc=$? ;;
webui) lpRegenWebui "$force" || rc=$? ;;
all) lpRegenArrays "$force" || rc=$?; lpRegenWebui "$force" || rc=$? ;;
esac
return $rc
}