feat(config): reconcileConfigFile now syncs comments from template (preserves user values)

Existing installs were locked out of template-side description /
options / marker changes because reconciliation kept the user's whole
`CFG_KEY=value     # comment` line verbatim. So new metadata like the
**DEV** marker I'm adding for the developer-mode feature wouldn't take
effect on already-deployed boxes — only fresh installs.

Updated reconcileConfigFile to split each line into value-part and
comment-part. User value is still sacred; the comment (title,
description, [options], **ADVANCED**/**DEV** markers) now comes from
the template. Field renames, label tweaks, marker additions/removals
shipped in a release reach existing installs on the next CLI
invocation (which runs the reconciler).

Specifically unblocks: the developer-mode WebUI feature (CFG_DEV_MODE
field gets added by the existing add-only path; CFG_INSTALL_MODE and
CFG_RELEASE_CHANNEL now pick up their new **DEV** markers and the
'Release - Stable' / 'Bleeding Edge' labels).

Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-26 23:53:21 +01:00
parent 9d8b1c5948
commit ea59d5b268

View File

@ -4,9 +4,34 @@
# - keep the user's existing value for any key the template still defines
# - add new template keys (CFG_REQUIREMENT_CONFIGS_AUTO_UPDATE)
# - drop keys the template no longer defines (CFG_REQUIREMENT_CONFIGS_AUTO_DELETE)
# - take the COMMENT (title, description, [options], **DEV** / **ADVANCED**
# markers) from the template, so label / metadata / marker changes shipped
# in a release reach existing installs without nuking user values.
# Structure, ordering and comments follow the template. Non-interactive, atomic,
# and keeps a <file>.bak. Refuses to act on a missing/empty template so a broken
# clone can never wipe a live config.
# Split `CFG_KEY=value...#comment` into its value-part and comment-part.
# Used by reconcileConfigFile to swap the template's comment onto the user's
# value. Sets _reconcile_value and _reconcile_comment as side outputs.
_reconcileSplitValueComment()
{
local line="$1"
# Drop the "CFG_KEY=" prefix; remainder is value [whitespace #comment].
local rest="${line#*=}"
if [[ "$rest" == *"#"* ]]; then
_reconcile_value="${rest%%#*}"
# Strip trailing whitespace from the value half.
_reconcile_value="${_reconcile_value%"${_reconcile_value##*[![:space:]]}"}"
_reconcile_comment="#${rest#*#}"
else
_reconcile_value="$rest"
# Strip trailing whitespace from the value half.
_reconcile_value="${_reconcile_value%"${_reconcile_value##*[![:space:]]}"}"
_reconcile_comment=""
fi
}
reconcileConfigFile()
{
local live="$1" template="$2"
@ -16,18 +41,34 @@ reconcileConfigFile()
[[ -f "$live" ]] || return 0
runInstallOp test -s "$template" 2>/dev/null || return 0
declare -A live_line emitted
declare -A live_value emitted
local line key
while IFS= read -r line; do
[[ "$line" =~ ^(CFG_[A-Za-z0-9_]+)= ]] && live_line["${BASH_REMATCH[1]}"]="$line"
done < <(runInstallOp cat "$live")
local tmp; tmp=$(mktemp)
while IFS= read -r line; do
if [[ "$line" =~ ^(CFG_[A-Za-z0-9_]+)= ]]; then
key="${BASH_REMATCH[1]}"
if [[ -n "${live_line[$key]+x}" ]]; then
printf '%s\n' "${live_line[$key]}" >> "$tmp" # keep the user's value
_reconcileSplitValueComment "$line"
live_value["$key"]="$_reconcile_value"
fi
done < <(runInstallOp cat "$live")
local tmp; tmp=$(mktemp)
local tmpl_value tmpl_comment merged
while IFS= read -r line; do
if [[ "$line" =~ ^(CFG_[A-Za-z0-9_]+)= ]]; then
key="${BASH_REMATCH[1]}"
_reconcileSplitValueComment "$line"
tmpl_value="$_reconcile_value"
tmpl_comment="$_reconcile_comment"
if [[ -n "${live_value[$key]+x}" ]]; then
# Keep the user's value, take the template's comment (so
# title/description/options/markers stay in sync with the repo).
if [[ -n "$tmpl_comment" ]]; then
merged="${key}=${live_value[$key]} ${tmpl_comment}"
else
merged="${key}=${live_value[$key]}"
fi
printf '%s\n' "$merged" >> "$tmp"
emitted["$key"]=1
elif [[ "$do_add" == "true" ]]; then
printf '%s\n' "$line" >> "$tmp" # new key, template default