From c3494f7d1929f283dc78ca3d950e1b5d110184ba Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 19 Aug 2026 19:58:08 +0100 Subject: [PATCH] Make CFG_SEARXNG_THEME actually apply The hook substituted `simple_style: auto`, a line that only exists in SearXNG's full bundled settings.yml. The file generated here is the minimal `use_default_settings: true` form with no ui: block at all, so the sed matched nothing and the theme setting had never taken effect on any install. It could not have worked even with the right pattern: the entrypoint chowns settings.yml to searxng:searxng (uid 977) mode 644 on first start, so the host-side docker user cannot write to it. The edit now runs inside the container via docker exec, targeting the real key path ui.theme_args.simple_style. Three shapes are handled so the hook stays correct on repeat installs and alongside hand edits: substitute in place when simple_style already exists, nest theme_args inside an existing ui: block rather than appending a second one (a duplicate YAML key SearXNG refuses to load), and otherwise append the whole block. All three were exercised against the running container and produce valid YAML with exactly one ui: block. awk rather than `sed a\` for the nesting case, since busybox sed does not expand \n in appended text. The value is validated against auto|light|dark|black before being written. SearXNG checks it at startup and exits on anything else, so an unrecognised CFG_SEARXNG_THEME would have taken the app down instead of merely looking wrong; it is now reported and the default left alone. Verified end to end on a base install and a --local instance: both come up, serve 200, and report Dark as the selected style on /preferences, each with its own settings.yml and secret_key. The instance's cloned hook correctly reads CFG_SEARXNG_PROBE_THEME and targets its own container, since the container name is built from $app_name. Both test installs were removed afterwards. Co-Authored-By: Claude Opus 5 --- .../searxng/scripts/searxng_install_hooks.sh | 67 +++++++++++++++++-- .../commands/updater/cli_updater_verify.sh | 36 +++++++++- 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/containers/searxng/scripts/searxng_install_hooks.sh b/containers/searxng/scripts/searxng_install_hooks.sh index dab2a8f..d507643 100644 --- a/containers/searxng/scripts/searxng_install_hooks.sh +++ b/containers/searxng/scripts/searxng_install_hooks.sh @@ -1,14 +1,15 @@ #!/bin/bash -# SearxNG install hooks — wait for settings.yml, apply theme, restart. +# SearXNG install hooks — wait for settings.yml, apply theme, restart. searxng_install_post_start() { local app_name="$1" + local searxng_settings="$containers_dir$app_name/searxng-data/settings.yml" local searxng_timeout=10 local searxng_counter=0 - while [ ! -f "$containers_dir$app_name/searxng-data/settings.yml" ]; do + while [ ! -f "$searxng_settings" ]; do if [ "$searxng_counter" -ge "$searxng_timeout" ]; then isNotice "File not found after 10 seconds. Exiting..." return 0 @@ -18,9 +19,65 @@ searxng_install_post_start() searxng_counter=$((searxng_counter + 1)) done - local result - result=$(runFileOp sed -i "s/simple_style: auto/simple_style: $CFG_SEARXNG_THEME/" "$containers_dir$app_name/searxng-data/settings.yml") - checkSuccess "Changing from light mode to dark mode to avoid eye strain installs" + # SearXNG validates this against a fixed set and refuses to start on anything + # else, so a typo in the config would take the whole app down rather than just + # look wrong. Check it here instead. + local searxng_theme="${CFG_SEARXNG_THEME:-auto}" + case "$searxng_theme" in + auto|light|dark|black) ;; + *) + isNotice "CFG_SEARXNG_THEME='$searxng_theme' is not one of auto|light|dark|black — leaving the theme at SearXNG's default." + return 0 + ;; + esac + + # The edit runs INSIDE the container, for two reasons the old `runFileOp sed` + # on the host could not satisfy: + # + # 1. Ownership. The entrypoint chowns this file to searxng:searxng (uid 977) + # on first start, mode 644 — so the host-side docker user cannot write to + # it at all. + # 2. Key path. The old sed replaced `simple_style: auto`, which only exists + # in SearXNG's full bundled settings.yml. The file generated here is the + # minimal `use_default_settings: true` form and has no ui: block at all, + # so the substitution matched nothing and CFG_SEARXNG_THEME had never had + # any effect on any install. + # + # The setting lives at ui.theme_args.simple_style. All three shapes are + # handled so this stays correct on a reinstall over existing data (where the + # key is already present) and if someone has hand-added their own ui: block — + # appending a second one would be a duplicate YAML key and SearXNG would + # refuse to load it. + local searxng_container="${app_name}-service" + local searxng_result + searxng_result=$(runFileOp docker exec -i "$searxng_container" sh -s "$searxng_theme" <<'SEARXNG_THEME_EOS' +set -e +theme="$1" +f=/etc/searxng/settings.yml +[ -f "$f" ] || exit 1 + +if grep -qE '^[[:space:]]*simple_style:' "$f"; then + # Already set (reinstall over existing data): update in place, keeping + # whatever indentation it currently sits at. + sed -i -E "s/^([[:space:]]*)simple_style:.*/\1simple_style: $theme/" "$f" +elif grep -qE '^ui:[[:space:]]*$' "$f"; then + # A ui: block exists but carries no theme_args — nest ours inside it rather + # than appending a second ui:, which would be a duplicate YAML key. awk, not + # `sed a\`, because busybox sed does not expand \n in appended text. + awk -v t="$theme" ' + /^ui:[[:space:]]*$/ { print; print " theme_args:"; print " simple_style: " t; next } + { print } + ' "$f" > "$f.lp.tmp" + cat "$f.lp.tmp" > "$f" + rm -f "$f.lp.tmp" +else + # The normal case for a LibrePortal install: the generated file is the + # minimal use_default_settings form with no ui: block at all. + printf '\nui:\n theme_args:\n simple_style: %s\n' "$theme" >> "$f" +fi +SEARXNG_THEME_EOS +) + checkSuccess "Applying SearXNG theme '$searxng_theme'" dockerComposeRestart $app_name } diff --git a/scripts/cli/commands/updater/cli_updater_verify.sh b/scripts/cli/commands/updater/cli_updater_verify.sh index 96f6454..a7fdb5e 100644 --- a/scripts/cli/commands/updater/cli_updater_verify.sh +++ b/scripts/cli/commands/updater/cli_updater_verify.sh @@ -25,8 +25,40 @@ # conservative and is NOT sufficient for a stepped upgrade — the engine refuses # to ladder an app that has not declared a real one. -# Container name for an app's primary service, matching the compose convention. -_updaterPrimaryContainer() { printf '%s-service' "${1//_/-}"; } +# Container name for an app's ANCHOR service — the one whose image carries the +# app's own version. Read from the compose rather than assumed, because +# "-service" is a convention and not every app follows it: matrix names its +# anchor service matrix-synapse and stoat names its api, so the assumption +# inspected a container that does not exist, found no state at all, and the +# generic verifier could only ever time out on exactly the stateful apps that +# most need verifying. Falls back to the convention when there is no compose. +_updaterPrimaryContainer() { + local app="$1" + local compose="${containers_dir%/}/$app/docker-compose.yml" + local fallback; fallback="$(printf '%s-service' "${app//_/-}")" + [ -f "$compose" ] || { printf '%s' "$fallback"; return 0; } + + local up; up="$(printf '%s' "$app" | tr '[:lower:]' '[:upper:]')" + # The service block that owns the bare _VERSION_TAG sentinel, then that + # block's container_name (compose's own answer for what the container is + # called). Service name is the fallback: compose defaults to it. + local name + name="$(awk -v key="#LIBREPORTAL|${up}_VERSION_TAG|" ''' + /^[[:space:]]{2,4}[a-zA-Z0-9_-]+:[[:space:]]*(#|$)/ { + s=$1; sub(/:.*/,"",s); gsub(/[[:space:]]/,"",s) + if (found && cname=="") { print svc; exit } + if (found) exit + svc=s; cname="" + } + index($0,key) { found=1 } + found && /^[[:space:]]*container_name:/ { + cname=$2; gsub(/["']/,"",cname); print cname; exit + } + END { if (found && cname=="") print svc } + ''' "$compose" 2>/dev/null | head -1 | tr -d "[:space:]")" + + [ -n "$name" ] && printf '%s' "$name" || printf '%s' "$fallback" +} # Generic health: running, not restarting, healthcheck (if any) reporting # healthy, and STILL true after a settle period — a crash-loop looks perfect