Compare commits

...

16 Commits

Author SHA1 Message Date
github-actions
8d04564f94 Github bot : issues linked to readme 2026-08-12 12:33:50 +00:00
Alexandre
3af76ebb7a fix: wait for the Supervisor API before running add-on startup scripts (#2967)
* fix: wait for the Supervisor API before running startup scripts

48 add-ons build their nginx ingress config out of bashio::addon.ip_address
and bashio::addon.ingress_port. Both come from one GET /addons/self/info, and
when that is answered before the Supervisor is ready bashio prints nothing.
Nine add-ons paste the result straight into a sed and end up writing
"listen : default_server;", which nginx rejects with `invalid port in ":"`;
the other 39 assign first and abort under set -e, leaving %%port%%
placeholders. Either way ingress is dead for that boot.

ha_entrypoint.sh now polls /addons/self/info once before the cont-init loop
and waits until it reports this add-on's ip_address (and, for ingress
add-ons, a non-zero ingress_port). Bounded at 30s via HA_SUPERVISOR_WAIT,
never fatal, and skipped entirely without SUPERVISOR_TOKEN or curl. When the
Supervisor is already up -- the normal case -- it costs one request.

qBittorrent is bumped so the change is actually built and reaches the add-on
with the open report; the other add-ons pick it up on their next rebuild.

Refs #2949, #2962

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

* fix: harden the Supervisor wait after bot review

- HA_SUPERVISOR_WAIT=08 was accepted by test -gt but read as octal by
  arithmetic expansion, leaving deadline empty; the comparison then errored
  every iteration and the loop never exited, hanging start-up. Digits-only
  validation plus base-10 forcing.
- A request started near the deadline could run --max-time past it. The
  per-request timeout is now capped to the time remaining, and the retry
  sleep is skipped once the budget is gone, so the ceiling is exact.

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

* refactor: probe the Supervisor through bashio instead of curl + sed

The wait reimplemented what the 48 consumers already do: it called
/addons/self/info with curl and picked the fields out with sed. That parallel
implementation was where one of the review findings landed, and it left a
residual race -- proving the API answered a moment ago says nothing about the
bashio call that runs next.

Probing through bashio removes both. bashio caches a successful
/addons/self/info under ${CACHE_DIR:-/tmp/.bashio}, so once the probe returns,
every bashio::addon.* call in every cont-init script reads that file rather
than asking the Supervisor again. Verified: one bashio::addon.ip_address call
writes a 26 KB addons.self.info.cache.

One call also settles all the fields, so the separate ingress/ingress_port
branch was redundant and is gone: a populated ip_address means the whole object
is cached. 36 -> 31 code lines.

Two consequences handled: bashio's own curl carries no --max-time (api.sh:41),
so each attempt is bounded with timeout; and bashio-standalone.sh answers these
calls from environment variables without ever contacting the Supervisor, so
BASHIO_LIB_FULL gates the probe to images carrying the real library.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:33:28 +02:00
Alexandre
fa348c7051 Refactor addon build-args to use yq for YAML files
https://github.com/alexbelgium/hassio-addons/issues/2966
2026-08-12 14:29:41 +02:00
github-actions
ac205b7901 Github bot : issues linked to readme 2026-08-12 12:05:59 +00:00
Alexandre
295efe88e2 Update onpr_check-pr.yaml 2026-08-12 13:45:45 +02:00
Alexandre
4c7c1f99a4 Update onpush_builder.yaml 2026-08-12 13:45:39 +02:00
Alexandre
1e3ec448df Update build.json 2026-08-12 13:31:00 +02:00
github-actions
3e384fb068 GitHub bot: changelog [nobuild] 2026-08-12 09:09:39 +00:00
Alexandre
843a909366 Update config.yaml 2026-08-12 11:07:53 +02:00
Alexandre
c7d48baba1 Update config.yaml 2026-08-12 11:07:33 +02:00
Alexandre
c53342f2e8 fix(komga): rewrite the cookie path onto the ingress entry (#2964)
Komga scopes its cookies to its servlet context path, so the browser never
sent them back from the ingress url and every request after a successful
login was anonymous (401).

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:33:27 +02:00
Alexandre
bb1f7c302b fix(komga): ship an apparmor profile so local disks can be mounted (#2963)
* fix(komga): ship an apparmor profile so local disks can be mounted

Without apparmor.txt Supervisor adds no apparmor security_opt, so Docker's
default profile applies and denies mount() and raw block device access:
mount reported 'cannot mount /dev/sda1 read-only' and the kernel logged
'/dev/disk/by-label/NAS: Can't open blockdev'.

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

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-12 08:41:36 +02:00
Alexandre
cfcdd94ae6 fix(komga): bound the nginx readiness probes (#2961)
* fix(komga): bound the nginx readiness probes and log an exhausted wait

Follow-up to #2960, which merged one commit before this landed.

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

* fix(komga): use a wall clock deadline for the readiness wait

An attempt count plus a per probe timeout stretched the wait to roughly twice
the advertised 15 minute ceiling.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:14:28 +02:00
github-actions
985f570263 Github bot : issues linked to readme 2026-08-12 04:01:32 +00:00
github-actions
e7a07b1d15 GitHub bot : README updated 2026-08-11 17:19:27 +00:00
Alexandre
44c58ba22b Update config.yaml nobuild 2026-08-11 18:59:23 +02:00
13 changed files with 218 additions and 19 deletions

View File

@@ -91,7 +91,7 @@ jobs:
uses: actions/checkout@v7.0.1
- name: 🔎 Run Home Assistant Add-on Lint
uses: frenck/action-addon-linter@f6bef06a4cee6c67924b0a70be643aacb8500a43
uses: frenck/action-addon-linter@v2
with:
path: "./${{ matrix.addon }}"
@@ -193,15 +193,15 @@ jobs:
- name: 💽 Create addon build-args
id: build_args
shell: bash
run: |
{
echo "armhf=BUILD_FROM=$(jq -r .build_from.armhf // empty ${{ steps.information.outputs.build }})"
echo "armv7=BUILD_FROM=$(jq -r .build_from.armv7 // empty ${{ steps.information.outputs.build }})"
echo "aarch64=BUILD_FROM=$(jq -r .build_from.aarch64 // empty ${{ steps.information.outputs.build }})"
echo "amd64=BUILD_FROM=$(jq -r .build_from.amd64 // empty ${{ steps.information.outputs.build }})"
echo "i386=BUILD_FROM=$(jq -r .build_from.i386 // empty ${{ steps.information.outputs.build }})"
} >> "$GITHUB_OUTPUT"
build_file="${{ steps.information.outputs.build }}"
case "$build_file" in
*.yaml|*.yml) build_json="$(yq -o=json '.' "$build_file")" ;;
*) build_json="$(cat "$build_file")" ;;
esac
for arch in armhf armv7 aarch64 amd64 i386; do
echo "${arch}=BUILD_FROM=$(printf '%s' "$build_json" | jq -r --arg a "$arch" '.build_from[$a] // empty')"
done >> "$GITHUB_OUTPUT"
- name: 🏗️ Set up QEMU
uses: docker/setup-qemu-action@v4

View File

@@ -114,7 +114,7 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- name: Run Home Assistant Add-on Lint
uses: frenck/action-addon-linter@f6bef06a4cee6c67924b0a70be643aacb8500a43
uses: frenck/action-addon-linter@v2
with:
path: "./${{ matrix.addon }}"

View File

@@ -173,9 +173,13 @@ fi
####################################
BASHIO_LIB=""
BASHIO_LIB_FULL=false
for f in /usr/lib/bashio/bashio.sh /usr/lib/bashio/lib.sh /usr/src/bashio/bashio.sh /usr/local/lib/bashio/bashio.sh; do
if [ -f "$f" ]; then
BASHIO_LIB="$f"
# The real library, which talks to the Supervisor. The standalone shim below only reads
# environment variables, which matters to wait_for_supervisor().
BASHIO_LIB_FULL=true
break
fi
done
@@ -188,6 +192,83 @@ if [ -z "$BASHIO_LIB" ]; then
done
fi
##############################
# Wait for the Supervisor API #
##############################
# Many cont-init scripts build their nginx ingress config out of bashio::addon.ip_address and
# bashio::addon.ingress_port. Both come from one GET /addons/self/info, and when that is answered
# before the Supervisor is ready bashio prints nothing: the add-on then either writes
# "listen : default_server;" -- which nginx rejects with `invalid port in ":"` -- or aborts under
# set -e and leaves the %%port%% placeholders in place. Either way the add-on cannot serve ingress.
# Ask for the same values here, through the same bashio calls, until they come back usable --
# rather than making 48 add-ons defend themselves against the same empty answer.
#
# Going through bashio rather than curl is what makes this reliable rather than merely likely:
# bashio caches a successful /addons/self/info under ${CACHE_DIR:-/tmp/.bashio}, so once this
# returns, every later bashio::addon.* call in every cont-init script reads that file instead of
# asking the Supervisor again. A probe that only proved the API was up a moment ago would leave
# the very next call free to fail.
#
# Bounded and never fatal: an add-on with no SUPERVISOR_TOKEN, or a Supervisor that stays
# unreachable, still has to start. HA_SUPERVISOR_WAIT (seconds, default 30) sets the ceiling; 0
# skips the wait. When the Supervisor is already up -- the normal case -- this costs one request.
wait_for_supervisor() {
local max="${HA_SUPERVISOR_WAIT:-30}"
local started deadline remaining attempt announced=0
# Nothing to wait for without a token. The standalone shim is excluded too: it answers these
# calls from environment variables and never contacts the Supervisor, so it can never satisfy
# the probe and would burn the whole ceiling on every boot.
[ -n "${SUPERVISOR_TOKEN:-}" ] || return 0
[ "${BASHIO_LIB_FULL:-false}" = "true" ] || return 0
# bashio's own curl carries no --max-time, so each attempt is bounded from the outside.
command -v timeout >/dev/null 2>&1 || return 0
# Digits only, then forced to base 10: `test -gt` accepts a zero-padded override like 08, but
# arithmetic expansion reads it as octal and fails, which would leave the deadline empty and
# spin the loop below forever.
case "$max" in '' | *[!0-9]*) return 0 ;; esac
max=$((10#$max))
[ "$max" -gt 0 ] || return 0
started=$SECONDS
deadline=$((started + max))
while :; do
remaining=$((deadline - SECONDS))
if [ "$remaining" -le 0 ]; then
echo -e "\e[38;5;214m$(date) WARNING: Supervisor API did not report this add-on's network details within ${max}s, continuing anyway\e[0m"
return 0
fi
# No single attempt may outlive the ceiling it is bounded by.
attempt=5
[ "$remaining" -lt "$attempt" ] && attempt="$remaining"
# One call is enough to settle all of them: bashio fetches the whole /addons/self/info object
# and caches it, so a populated ip_address means ingress_port and the rest are cached too.
# Run in a child shell so bashio's globals and traps stay out of the entrypoint; its own error
# logging is dropped because a failed attempt here is expected, not news.
# shellcheck disable=SC2016
if timeout "$attempt" bash -c '. "$1" && [ -n "$(bashio::addon.ip_address)" ]' \
_ "$BASHIO_LIB" >/dev/null 2>&1; then
[ "$announced" -eq 0 ] || echo "Supervisor API ready after $((SECONDS - started))s"
return 0
fi
if [ "$announced" -eq 0 ]; then
echo "Waiting for the Supervisor API to report this add-on's network details..."
announced=1
fi
# Skipped when the attempt already consumed what was left, so the sleep cannot overshoot.
[ "$((deadline - SECONDS))" -gt 0 ] && sleep 1
done
}
wait_for_supervisor
####################
# Starting scripts #
####################

View File

@@ -56,7 +56,7 @@ If you want to do add the repository manually, please follow the procedure highl
### Number of addons
- In the repository : 137
- In the repository : 138
- Installed : 325872
### Top 3
@@ -567,6 +567,16 @@ If you want to do add the repository manually, please follow the procedure highl
![aarch64][aarch64-badge]
![amd64][amd64-badge]
&#10003; ![image](https://api.iconify.design/mdi/book-open-page-variant.svg) [Komga](komga/) : Free and open source comics/mangas media server
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fkomga%2Fconfig.yaml)
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fkomga%2Fupdater.json)
![aarch64][aarch64-badge]
![amd64][amd64-badge]
![ingress][ingress-badge]
![smb][smb-badge]
![localdisks][localdisks-badge]
&#10003; ![image](https://api.iconify.design/mdi/speedometer.svg) [LibreSpeed](librespeed/) : A very lightweight speed test implemented in Javascript, using XMLHttpRequest and Web Workers
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Flibrespeed%2Fconfig.yaml)

View File

@@ -1,3 +1,17 @@
## 1.26.1.4 (12-08-2026)
- Minor bugs fixed
## 1.26.1.3 (2026-08-12)
- Fix : 401 errors after a successful login through ingress. Komga scopes its session cookies to its servlet context path (`Path=/komga`), which the browser never sends back from the ingress url, so every request after the login was anonymous. Nginx now rewrites the cookie path onto the ingress entry
## 1.26.1.2 (2026-08-12)
- Fix : local disks (`localdisks`) and SMB shares failed to mount with `cannot mount /dev/sdX read-only`. Without an `apparmor.txt` the add-on ran under Docker's default AppArmor profile, which denies `mount` and raw block device access. Ships the same profile as the other add-ons that mount disks
## 1.26.1.1 (2026-08-11)
- Bound the nginx readiness probes (`--connect-timeout` / `--max-time`) so a stalled connection cannot hang the wait, and log a warning when Komga has not answered within 15 minutes
## 1.26.1 (2026-08-11)
- Initial release, based on gotson/komga ([changelog](https://github.com/gotson/komga/releases))

68
komga/apparmor.txt Normal file
View File

@@ -0,0 +1,68 @@
#include <tunables/global>
profile komga_addon flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
capability chown,
capability dac_override,
capability dac_read_search,
capability fowner,
capability setgid,
capability setuid,
capability sys_chroot,
capability sys_admin,
file,
signal,
mount,
umount,
remount,
network udp,
network tcp,
network dgram,
network stream,
network inet,
network inet6,
network netlink raw,
network unix dgram,
# Entrypoint stack
/init ix,
/run/{s6,s6-rc*,service}/** ix,
/package/** ix,
/command/** ix,
/run/{,**} rwk,
/dev/tty rw,
/bin/** ix,
/usr/bin/** ix,
/usr/lib/bashio/** ix,
/etc/s6/** rix,
/run/s6/** rix,
/etc/services.d/** rwix,
/etc/cont-init.d/** rwix,
/etc/cont-finish.d/** rwix,
/init rix,
/var/run/** mrwkl,
/var/run/ mrwkl,
/dev/i2c-1 mrwkl,
# Files required
/dev/fuse mrwkl,
/dev/sda1 mrwkl,
/dev/sdb1 mrwkl,
/dev/nvme0 mrwkl,
/dev/nvme1 mrwkl,
/dev/mmcblk0p1 mrwkl,
/dev/* mrwkl,
/tmp/** mrkwl,
# Data access
/data/** rw,
# suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
ptrace (trace,read) peer=docker-default,
# docker daemon confinement requires explicit allow rule for signal
signal (receive) set=(kill,term) peer=/usr/bin/docker,
}

View File

@@ -1,6 +1,6 @@
{
"build_from": {
"aarch64": "gotson/komga:1.26.1",
"amd64": "gotson/komga:1.26.1"
"aarch64": "gotson/komga:latest",
"amd64": "gotson/komga:latest"
}
}

View File

@@ -66,7 +66,7 @@ devices:
- /dev/nvme2
image: ghcr.io/alexbelgium/komga-{arch}
ingress: true
ingress_entry: komga
ingress_entry: komga/next
init: false
map:
- addon_config:rw
@@ -101,4 +101,4 @@ schema:
slug: komga
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/komga
version: "1.26.1"
version: "1.26.1.4"

View File

@@ -33,6 +33,13 @@ server {
proxy_redirect http://127.0.0.1:25600/ %%ingress_entry%%/;
proxy_redirect / %%ingress_entry%%/;
# Komga scopes its cookies to the servlet context path
# (Set-Cookie: ...; Path=/komga). The browser lives under the ingress
# entry, so such a cookie is never sent back : login succeeds, then
# every following request arrives anonymous and Komga answers 401.
proxy_cookie_path /komga %%ingress_entry%%/komga;
proxy_cookie_path / %%ingress_entry%%/;
# Komga renders its index page with Thymeleaf @{...} link expressions,
# so every asset url and window.resourceBaseUrl carry the context path
# (/komga). Ingress strips its own prefix before forwarding, so the

View File

@@ -8,12 +8,27 @@ set -e
# bashio::net.wait_for : bashio takes (port host timeout) while the bundled
# bashio-standalone.sh takes (host port timeout), and picking the wrong one
# would either fail instantly or block for the whole timeout.
for _ in $(seq 1 180); do
if curl -sf -o /dev/null "http://127.0.0.1:25600/komga/"; then
# The per probe timeouts keep the 15 minute ceiling real : without them a
# half open connection would hang a single probe, and the loop, forever.
# A wall clock deadline, not an attempt count : a failed probe costs up to
# max-time on top of the sleep, so counting attempts would stretch the wait to
# roughly twice the advertised ceiling.
komga_ready=false
deadline=$((SECONDS + 900))
while [ "$SECONDS" -lt "$deadline" ]; do
if curl -sf --connect-timeout 2 --max-time 5 -o /dev/null "http://127.0.0.1:25600/komga/"; then
komga_ready=true
break
fi
sleep 5
done
# Deliberately not fatal : nginx serving a 502 tells the user something is wrong
# and starts working by itself once komga finally answers, while refusing to
# start would take ingress down for good after ha_entrypoint gives up retrying.
if [ "$komga_ready" != true ]; then
bashio::log.warning "Komga did not answer within 15 minutes. Starting NGinx anyway : ingress will return 502 until it does."
fi
bashio::log.info "Starting NGinx..."
exec nginx

View File

@@ -1,4 +1,8 @@
## 5.2.3.2 (2026-08-12)
- Fix : ingress could fail permanently with `nginx: [emerg] invalid port in ":"`. `30-nginx.sh` pastes `bashio::addon.ip_address` and `bashio::addon.ingress_port` straight into the nginx config; when the Supervisor answers before it is ready both come back empty and the config gets `listen : default_server;`. The add-on entrypoint now waits (up to 30s, tunable with `HA_SUPERVISOR_WAIT`) for the Supervisor to report the add-on's network details before any startup script runs
## 5.2.3-1 (2026-07-09)
- Rebuild images after VueTorrent download path fix

View File

@@ -143,4 +143,4 @@ schema:
slug: qbittorrent
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "5.2.3.1"
version: "5.2.3.2"

View File

@@ -1,4 +1,4 @@
## &#9888; Open Request : [✨ [REQUEST] qbittorrent (opened 2025-10-11)](https://github.com/alexbelgium/hassio-addons/issues/2139) by [@nyok92](https://github.com/nyok92)
## &#9888; Open Issue : [✨ [REQUEST] qbittorrent (opened 2025-10-11)](https://github.com/alexbelgium/hassio-addons/issues/2139) by [@nyok92](https://github.com/nyok92)
## &#9888; Open Request : [✨ [REQUEST] transmission, document where torrent files are stored (opened 2026-07-14)](https://github.com/alexbelgium/hassio-addons/issues/2852) by [@bilogic](https://github.com/bilogic)
# Hass.io Add-ons: Tor with bridges