fix(cleanuparr): make ingress and the env_vars passthrough work (#3085)

* fix(cleanuparr): make ingress work

The add-on declared `ingress: true` without `ingress_port`, so Supervisor
proxied the sidebar entry and "Open Web UI" to its default port 8099, where
nothing listened.

Pointing `ingress_port` at Cleanuparr's own port 11011 is not enough, and
setting Cleanuparr's `BASE_PATH` to the ingress entry is actively wrong: Home
Assistant strips the `/api/hassio_ingress/<token>` prefix before the request
reaches the container, while `BASE_PATH` makes Cleanuparr 404 every request
that does not carry the prefix and redirect `/` to it — an ingress redirect
loop.

Serve port 8099 with nginx instead, as many other add-ons in this repo already
do, and rewrite the one thing the browser gets wrong: the `_server_base_path`
value Cleanuparr injects into index.html, which its Angular frontend builds
every asset, API and SignalR URL from. The prefix is taken from the
`X-Ingress-Path` header Home Assistant Core sets on every ingress request, so
nothing has to be looked up or written at startup, and a request arriving
without that header is left alone. The application keeps serving from the root
on 11011, so direct access is untouched and no user-set option changes.

Fixes #3084

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(cleanuparr): drop webui, which the add-on linter rejects under ingress

`frenck/action-addon-linter` fails the build with "'webui' should be removed,
Ingress is enabled." This is pre-existing — `ingress: true` and `webui` have
coexisted here for a while — but this is the first PR to touch the add-on and
run the linter over it, so it blocks here.

cleanuparr was the only one of the repository's 56 ingress add-ons that set
`webui`; the other 55 omit it. Removing it matches both the linter and the rest
of the repo. "Open Web UI" goes through ingress, and port 11011 stays published
for direct access.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(cleanuparr): make the env_vars passthrough actually work

config.yaml has declared the standard env-var passthrough schema all along, but
nothing in the image ever converted it, so anything a user put in `env_vars`
was dropped. Three separate faults, each sufficient on its own:

- `00-global_var.sh`, the module that does the conversion, was not in the
  Dockerfile's `ARG MODULES`, so it was never installed.
- The module requires jq, both directly and through bashio. The upstream image
  (Ubuntu 24.04 based) ships curl, tzdata, gosu and python3, but not jq.
- The entrypoint ran cont-init scripts as `bash "$script"`, ignoring their
  shebang, so no bashio functions were defined. The module's
  `bashio::supervisor.ping` guard then failed with rc 127 and it exited 0
  without doing anything. This is also why the banner always announced
  "standalone mode (no Supervisor)".

Install jq, add the module, and run the init scripts under bashio the way the
Maintainerr add-on's bespoke entrypoint already does. The module writes /.env,
which this entrypoint now sources before starting Cleanuparr — it runs the
module as a child, so its exports are otherwise lost. The sourcing sits after
nginx has started, so an env_vars entry cannot alter the ingress proxy.

Verified by running the real module under real bashio against a stub
options.json: values containing $, backticks, quotes, an apostrophe and a
backslash round-trip byte for byte through /.env and arrive exported.

PUID/PGID remain read from the image's defaults for the data-directory chown.
Making them take effect would re-own existing users' config directories, which
is a separate decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(cleanuparr): record why sourcing /.env is safe here

01-config_yaml.sh writes unquoted KEY=VALUE lines to the same file. It is not
in this add-on's MODULES, so /.env only ever holds 00-global_var.sh's quoted
output, but the coupling is worth stating at the point that depends on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Alexandre
2026-09-23 16:31:30 +01:00
committed by GitHub
parent 933fe7c537
commit 35dd359202
5 changed files with 108 additions and 4 deletions

View File

@@ -1,4 +1,22 @@
## 2.10.6.1 (2026-09-23)
- Fix ingress: the add-on declared `ingress: true` without `ingress_port`, so Home
Assistant proxied the sidebar entry and "Open Web UI" to the default port 8099,
where nothing listened. An nginx proxy now serves that port and rewrites the base
path Cleanuparr's web UI builds its asset, API and SignalR URLs from, so they
resolve under the ingress path instead of the Home Assistant root. Direct access
on port 11011 is unchanged (#3084)
- Remove `webui`, which the add-on linter rejects when ingress is enabled and which
the repository's other 55 ingress add-ons do not set; "Open Web UI" uses ingress
and port 11011 stays published
- Fix `env_vars`: the add-on declared the env-var passthrough in its schema but never
installed `00-global_var.sh`, the module that converts it, and could not have run it —
the image has no `jq`, and cont-init scripts were started with plain `bash`, so the
module's bashio guard always took its no-Supervisor branch and exited. Install `jq`,
add the module, run the init scripts under bashio as the Maintainerr add-on does, and
source the generated `/.env` before starting Cleanuparr. `PUID`/`PGID` are still read
from the image's defaults for the data-directory `chown` — see the pull request
## 2.10.6 (2026-09-19)
- Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases)
- Migrate legacy add-on configuration map names to current app configuration terminology.

View File

@@ -36,6 +36,13 @@ ENV S6_CMD_WAIT_FOR_SERVICES=1 \
# 3 Install apps #
##################
# nginx proxies Home Assistant ingress (see rootfs/etc/nginx/nginx.conf); jq is
# required by bashio and by 00-global_var.sh, and the upstream image ships neither.
# Installed before the rootfs is copied so dpkg does not own our nginx.conf.
RUN apt-get update && \
apt-get install -y --no-install-recommends nginx jq && \
rm -rf /var/lib/apt/lists/*
# Add rootfs
COPY rootfs/ /
RUN find /. -type f \( -name "*.sh" \) -print -exec chmod +x {} \;
@@ -49,7 +56,7 @@ RUN BASHIO_VERSION="0.14.3" && \
rm -rf /tmp/bashio
# Modules
ARG MODULES="00-banner.sh 01-custom_script.sh"
ARG MODULES="00-banner.sh 00-global_var.sh 01-custom_script.sh"
# Automatic modules download
COPY ha_automodules.sh /ha_automodules.sh

View File

@@ -91,5 +91,4 @@ schema:
TZ: str?
slug: cleanuparr
url: https://github.com/alexbelgium/hassio-addons/tree/master/cleanuparr
version: "2.10.6"
webui: "[PROTO:ssl]://[HOST]:[PORT:11011]"
version: "2.10.6.1"

View File

@@ -0,0 +1,57 @@
# Ingress proxy for Cleanuparr.
#
# Home Assistant serves the add-on under /api/hassio_ingress/<token>/ but strips
# that prefix before the request reaches this container, passing it along in the
# X-Ingress-Path header instead. Cleanuparr's own BASE_PATH option cannot be
# used: it makes the application reject every request that does not carry the
# prefix. So the prefix is put back into the page on its way out instead, and
# the application keeps serving from the root on port 11011 for direct access.
user root;
pid /var/run/nginx.pid;
worker_processes 1;
error_log /dev/stderr error;
events {
worker_connections 512;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
client_max_body_size 0;
server_tokens off;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Requests that arrive without the header are not coming through ingress.
map $http_x_ingress_path $ingress_base_path {
default $http_x_ingress_path;
'' /;
}
server {
listen 8099 default_server;
location / {
proxy_pass http://127.0.0.1:11011;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# Cleanuparr's index.html carries the base path its frontend builds
# every asset, API and SignalR URL from. Rewrite it to the ingress
# prefix so those URLs come back through ingress.
proxy_set_header Accept-Encoding "";
sub_filter_once off;
sub_filter "window['_server_base_path'] = '/';" "window['_server_base_path'] = '$ingress_base_path';";
}
}
}

View File

@@ -20,8 +20,9 @@ fi
if [ -d /etc/cont-init.d ]; then
for script in /etc/cont-init.d/*.sh; do
[ -f "$script" ] || continue
sed -i '1s|.*|#!/usr/bin/env bashio|' "$script"
echo "[Cleanuparr] Running init script: $script"
bash "$script"
bashio "$script"
done
fi
@@ -40,6 +41,28 @@ ln -sfn "$HA_DATA_DIR" /app/config
chown -R "${PUID:-0}:${PGID:-0}" "$HA_DATA_DIR"
# ─── Ingress proxy ───────────────────────────────────────────────────────────
# See /etc/nginx/nginx.conf for why ingress needs a proxy at all.
echo "[Cleanuparr] Starting ingress proxy on port 8099..."
nginx
# ─── Add-on options as environment variables ─────────────────────────────────
# 00-global_var.sh turns /data/options.json, the env_vars list included, into
# /.env. It runs as a child of this script, so sourcing its output here is what
# actually puts those variables in Cleanuparr's environment. Deliberately after
# nginx has started: an env_vars entry cannot then affect the ingress proxy.
#
# Sourcing is only safe because 00-global_var.sh is the sole writer of /.env
# here and quotes every value. 01-config_yaml.sh appends bare KEY=VALUE lines,
# so `MY_VAR: hello world` would run `world` as a command: do not add that
# module to MODULES without quoting its output first.
if [ -f /.env ]; then
set -a
# shellcheck disable=SC1091
. /.env
set +a
fi
# ─── Start Cleanuparr directly (bypass original /entrypoint.sh) ──────────────
echo "[Cleanuparr] Starting application on port ${HTTP_PORTS:-11011}..."
cd /app