From 38d6a3b2db21c52784577d704af0e8c586afd566 Mon Sep 17 00:00:00 2001 From: alexbelgium <44178713+alexbelgium@users.noreply.github.com> Date: Sun, 13 Sep 2026 00:51:50 +0000 Subject: [PATCH] fix: auto-fix linting issues --- .../scripts/env_trace.sh | 19 +- .../hassio-addon-workflow/scripts/measure.sh | 14 +- .../scripts/pr_review.sh | 299 ++++--- .../scripts/preflight.sh | 9 +- .../hassio-addon-workflow/scripts/traps.sh | 14 +- .../hassio-addon-workflow/scripts/validate.sh | 76 +- .github/prompts/issue-classify.md | 2 +- .github/prompts/issue-fix.md | 2 +- .github/scripts/ai_guard_paths.sh | 2 +- .github/scripts/ai_triage_context.sh | 172 ++-- .templates/00-banner.sh | 14 +- .templates/00-global_var.sh | 13 +- .templates/00-smb_mounts.sh | 780 +++++++++--------- .templates/01-config_yaml.sh | 6 +- .templates/01-custom_script.sh | 210 ++--- .templates/90-dns_set.sh | 6 +- .templates/bashio-standalone.sh | 441 +++++----- .templates/ha_autoapps.sh | 4 +- .templates/ha_automatic_packages.sh | 4 +- .templates/ha_entrypoint.sh | 580 ++++++------- CLAUDE.md | 4 +- addons_updater/README.md | 2 +- aurral/CHANGELOG.md | 20 +- aurral/README.md | 2 +- autobrr/CHANGELOG.md | 12 +- autobrr/README.md | 2 +- bazarr/README.md | 2 +- bazarr/rootfs/etc/services.d/nginx/run | 0 bentopdf/README.md | 12 +- .../etc/s6-overlay/s6-rc.d/bentopdf/run | 0 birdnet-go-dev/README.md | 4 +- birdnet-go-dev/merge-prs.sh | 7 +- .../rootfs/etc/cont-init.d/01-structure.sh | 6 +- .../rootfs/etc/cont-init.d/33-mariadb.sh | 6 +- birdnet-go/CHANGELOG.md | 10 +- .../rootfs/etc/cont-init.d/01-structure.sh | 6 +- birdnet-pi-zach/DOCS.md | 6 +- .../rootfs/helpers/birdnet_to_mqtt.py | 1 + birdnet-pi/DOCS.md | 6 +- birdnet-pi/rootfs/helpers/birdnet_to_mqtt.py | 1 + birdnet-pipy/CHANGELOG.md | 14 +- birdnet-pipy/config.yaml | 2 +- .../rootfs/etc/cont-init.d/01-structure.sh | 26 +- .../rootfs/etc/cont-init.d/10-config.sh | 12 +- .../rootfs/etc/cont-init.d/15-audio_perms.sh | 48 +- birdnet-pipy/rootfs/etc/services.d/api/run | 14 +- .../rootfs/etc/services.d/icecast/run | 0 birdnet-pipy/rootfs/etc/services.d/main/run | 0 birdnet-pipy/rootfs/etc/services.d/model/run | 0 birdnet-pipy/rootfs/etc/services.d/nginx/run | 0 bitwarden/CHANGELOG.md | 6 +- .../etc/s6-overlay/s6-rc.d/init-nginx/run | 0 .../rootfs/etc/s6-overlay/s6-rc.d/nginx/run | 0 .../etc/s6-overlay/s6-rc.d/vaultwarden/run | 6 +- booksonic_air/README.md | 2 +- browser_brave/CHANGELOG.md | 32 +- browser_chromium/CHANGELOG.md | 20 +- .../rootfs/etc/cont-init.d/20-folders.sh | 1 - .../rootfs/etc/usr/bin/wrapped-chromium | 34 +- browserless_chrome/CHANGELOG.md | 26 +- calibre/CHANGELOG.md | 10 +- calibre/README.md | 2 +- calibre_web/CHANGELOG.md | 2 +- calibre_web/README.md | 4 +- .../etc/cont-init.d/80-configuration.sh | 8 +- changedetection.io/CHANGELOG.md | 6 +- changedetection.io/README.md | 2 +- .../rootfs/etc/services.d/nginx/run | 0 claude_desktop/CHANGELOG.md | 28 +- claude_desktop/rootfs/defaults/autostart | 4 +- .../rootfs/etc/cont-init.d/81-codex_cli.sh | 7 +- .../rootfs/etc/cont-init.d/82-claude_tools.sh | 28 +- .../etc/cont-init.d/83-claude_permissions.sh | 2 +- .../etc/cont-init.d/86-claude_safestorage.sh | 2 +- .../usr/local/bin/claude-tools-doctor.sh | 6 +- .../rootfs/usr/local/bin/codex-login | 0 claude_desktop/rootfs/usr/local/bin/ha-cli | 14 +- .../bin/headroom-posttooluse-compress.py | 14 +- cleanuparr/CHANGELOG.md | 10 +- cleanuparr/README.md | 2 +- cleanuparr/config.yaml | 3 +- cleanuparr/rootfs/ha_entrypoint.sh | 2 +- cloudcommander/CHANGELOG.md | 8 +- cloudcommander/README.md | 2 +- .../rootfs/etc/cont-init.d/99-run.sh | 2 +- codex/CHANGELOG.md | 26 +- codex/README.md | 2 +- collabora/CHANGELOG.md | 14 +- collabora/README.md | 2 +- collabora/rootfs/etc/cont-init.d/99-run.sh | 6 +- comicarr/CHANGELOG.md | 6 +- comicarr/README.md | 2 +- comicarr/rootfs/etc/services.d/comicarr/run | 0 comicarr/rootfs/etc/services.d/nginx/run | 0 comixed/README.md | 2 +- elasticsearch/CHANGELOG.md | 6 +- .../rootfs/usr/local/bin/addon-init.sh | 18 +- emby/CHANGELOG.md | 2 +- emby/README.md | 2 +- emby/rootfs/etc/services.d/nginx/run | 0 emby_beta/CHANGELOG.md | 30 +- enedisgateway2mqtt/README.md | 2 +- enedisgateway2mqtt_dev/README.md | 2 +- ente/CHANGELOG.md | 6 +- ente/README.md | 4 +- epicgamesfree/CHANGELOG.md | 20 +- epicgamesfree/README.md | 2 +- .../rootfs/etc/cont-init.d/99-run.sh | 2 +- filebrowser/CHANGELOG.md | 8 +- filebrowser/README.md | 2 +- filebrowser_quantum/CHANGELOG.md | 18 +- filebrowser_quantum/README.md | 2 +- .../rootfs/etc/cont-init.d/99-run.sh | 36 +- fireflyiii/CHANGELOG.md | 2 +- fireflyiii/README.md | 2 +- fireflyiii/rootfs/etc/cont-init.d/99-run.sh | 2 +- fireflyiii_data_importer/CHANGELOG.md | 2 +- fireflyiii_data_importer/README.md | 2 +- .../rootfs/templates/cronupdate | 26 +- flaresolverr/CHANGELOG.md | 2 +- flexget/CHANGELOG.md | 24 +- flexget/README.md | 2 +- free_games_claimer/CHANGELOG.md | 2 +- free_games_claimer/README.md | 2 +- .../rootfs/etc/cont-init.d/99-run.sh | 8 +- .../rootfs/templates/config.env | 8 +- .../usr/local/bin/migrate_vogler_data.py | 19 +- gazpar2mqtt/CHANGELOG.md | 2 +- gazpar2mqtt/README.md | 2 +- gitea/CHANGELOG.md | 10 +- gitea/README.md | 2 +- grampsweb/CHANGELOG.md | 12 +- grampsweb/README.md | 4 +- grav/CHANGELOG.md | 26 +- grav/README.md | 2 +- guacamole/CHANGELOG.md | 2 +- guacamole/README.md | 2 +- immich/CHANGELOG.md | 10 +- immich/README.md | 2 +- immich_cuda/CHANGELOG.md | 8 +- immich_cuda/README.md | 2 +- immich_cuda/config.yaml | 3 +- immich_frame/CHANGELOG.md | 4 +- immich_frame/README.md | 6 +- immich_frame/rootfs/etc/cont-init.d/99-run.sh | 24 +- immich_noml/CHANGELOG.md | 8 +- immich_noml/README.md | 2 +- immich_noml/config.yaml | 3 +- immich_openvino/CHANGELOG.md | 8 +- immich_openvino/README.md | 2 +- immich_power_tools/CHANGELOG.md | 4 +- immich_power_tools/README.md | 2 +- inadyn/README.md | 4 +- jackett/CHANGELOG.md | 40 +- jackett/README.md | 2 +- jellyfin/CHANGELOG.md | 4 +- jellyfin/README.md | 2 +- jellyfin/config.yaml | 2 +- .../rootfs/etc/cont-init.d/25-video_fix.sh | 12 +- jellyfin/rootfs/etc/services.d/nginx/run | 0 joal/README.md | 2 +- joplin/CHANGELOG.md | 2 +- joplin/README.md | 2 +- joplin/rootfs/etc/cont-init.d/99-run.sh | 25 +- kometa/CHANGELOG.md | 12 +- komga/README.md | 2 +- librespeed/CHANGELOG.md | 6 +- lidarr/README.md | 2 +- linkwarden/CHANGELOG.md | 6 +- linkwarden/README.md | 2 +- linkwarden/rootfs/etc/cont-init.d/99-run.sh | 2 +- maintainerr/CHANGELOG.md | 32 +- maintainerr/config.yaml | 3 +- maintainerr/rootfs/ha_entrypoint.sh | 4 +- manyfold/CHANGELOG.md | 16 +- manyfold/README.md | 4 +- .../etc/s6-overlay/s6-rc.d/manyfold/finish | 0 manyfold/run.sh | 339 ++++---- mealie/CHANGELOG.md | 12 +- mealie/README.md | 2 +- monica/README.md | 2 +- monica/rootfs/etc/cont-init.d/99-run.sh | 18 +- mylar3/CHANGELOG.md | 4 +- mylar3/README.md | 2 +- navidrome/CHANGELOG.md | 4 +- navidrome/README.md | 2 +- netalertx/CHANGELOG.md | 6 +- netalertx_fa/CHANGELOG.md | 6 +- .../rootfs/etc/cont-init.d/00-config.sh | 82 +- .../rootfs/etc/services.d/caddy/run | 4 +- .../rootfs/etc/services.d/dashboard/run | 12 +- .../rootfs/etc/services.d/management/run | 22 +- .../rootfs/etc/services.d/relay/run | 12 +- .../rootfs/etc/services.d/signal/run | 8 +- nextcloud/CHANGELOG.md | 10 +- nextcloud/README.md | 8 +- nginx_webserver_proxy/README.md | 2 +- nzbget/CHANGELOG.md | 30 +- nzbget/README.md | 2 +- nzbget/rootfs/etc/services.d/nginx/run | 0 .../etc/s6-overlay/s6-rc.d/addon-init/script | 12 +- .../etc/s6-overlay/s6-rc.d/addon-init/up | 0 .../etc/s6-overlay/s6-rc.d/couchdb/finish | 0 obsidian_syncserver_solo/CHANGELOG.md | 2 +- obsidian_syncserver_ssl/CHANGELOG.md | 2 +- openproject/CHANGELOG.md | 12 +- photoprism/CHANGELOG.md | 4 +- photoprism/README.md | 2 +- piwigo/README.md | 2 +- plex/CHANGELOG.md | 20 +- plex/README.md | 2 +- plex/rootfs/etc/cont-init.d/21-folders.sh | 4 +- portainer/CHANGELOG.md | 8 +- portainer/README.md | 2 +- portainer_agent/CHANGELOG.md | 4 +- .../rootfs/etc/services.d/portainer_agent/run | 2 +- portainer_agent/rootfs/usr/sbin/healthcheck | 6 +- portainer_be/CHANGELOG.md | 2 +- portainer_be/README.md | 2 +- postgres_15/README.md | 2 +- postgres_15/rootfs/etc/cont-init.d/99-run.sh | 2 +- prowlarr/CHANGELOG.md | 32 +- prowlarr/README.md | 2 +- qbittorrent/README.md | 2 +- .../rootfs/etc/cont-init.d/93-openvpn.sh | 4 +- .../rootfs/etc/cont-init.d/94-wireguard.sh | 4 +- .../rootfs/etc/cont-init.d/95-vpn-monitor.sh | 2 +- .../s6-overlay/s6-rc.d/svc-qbittorrent/run | 0 qbittorrent/rootfs/etc/services.d/timer/run | 0 .../rootfs/etc/services.d/vpn-monitor/run | 9 +- .../rootfs/etc/services.d/vpn-upnp/run | 12 +- qbittorrent/rootfs/usr/local/sbin/vpn | 0 radarr/CHANGELOG.md | 4 +- radarr/README.md | 2 +- readarr/README.md | 2 +- readarr/rootfs/etc/services.d/nginx/run | 0 requestrr/README.md | 2 +- sabnzbd/CHANGELOG.md | 10 +- sabnzbd/README.md | 2 +- sabnzbd/rootfs/etc/services.d/nginx/finish | 0 scrutiny/CHANGELOG.md | 22 +- scrutiny/README.md | 2 +- .../local/bin/scrutiny-ha-influxdb-preflight | 304 +++---- scrutiny_fa/CHANGELOG.md | 22 +- scrutiny_fa/README.md | 2 +- .../etc/services.d/nginx/finish | 0 scrutiny_fa/scrutiny-ha-influxdb-preflight | 304 +++---- scrutiny_fa_original/CHANGELOG.md | 10 +- scrutiny_fa_original/README.md | 2 +- scrutiny_original/CHANGELOG.md | 10 +- scrutiny_original/README.md | 2 +- seafile/CHANGELOG.md | 2 +- seafile/README.md | 2 +- seafile/rootfs/etc/cont-init.d/99-run.sh | 16 +- seerr/CHANGELOG.md | 2 +- seerr/README.md | 2 +- seerr/config.yaml | 3 +- seerr/rootfs/etc/services.d/nginx/run | 0 seerr/rootfs/etc/services.d/seerr/finish | 0 signalk/CHANGELOG.md | 10 +- signalk/rootfs/etc/cont-init.d/99-run.sh | 1 - social_to_mealie/CHANGELOG.md | 6 +- social_to_mealie/README.md | 2 +- sonarr/CHANGELOG.md | 16 +- sonarr/README.md | 2 +- sponsorblockcast/README.md | 2 +- spotify_to_plex/CHANGELOG.md | 4 +- spotify_to_plex/README.md | 2 +- tandoor_recipes/CHANGELOG.md | 8 +- tandoor_recipes/README.md | 2 +- tdarr/CHANGELOG.md | 18 +- tdarr/README.md | 2 +- teamspeak/README.md | 2 +- transmission/CHANGELOG.md | 2 +- transmission/README.md | 2 +- transmission_openvpn/CHANGELOG.md | 6 +- transmission_openvpn/config.yaml | 2 +- .../rootfs/etc/openvpn/update-resolv-conf | 82 +- ubooquity/README.md | 2 +- unpackerr/CHANGELOG.md | 2 +- unpackerr/README.md | 2 +- unpackerr/rootfs/etc/cont-init.d/90-vpn.sh | 2 +- webtop/README.md | 2 +- webtop_kde/CHANGELOG.md | 20 +- wger/CHANGELOG.md | 2 +- whoogle/README.md | 2 +- zoneminder/CHANGELOG.md | 2 +- zoneminder/rootfs/etc/cont-init.d/99-run.sh | 18 +- zoraxy/CHANGELOG.md | 2 +- zoraxy/rootfs/etc/services.d/zoraxy/run | 0 zzz_archived_code-server/CHANGELOG.md | 22 +- zzz_archived_jellyseerr/CHANGELOG.md | 2 +- zzz_archived_jellyseerr/config.yaml | 3 +- zzz_archived_kapowarr/README.md | 2 +- .../rootfs/etc/services.d/kapowarr/run | 0 .../rootfs/etc/services.d/nginx/run | 0 .../rootfs/etc/cont-init.d/99-run.sh | 0 zzz_archived_omada_v3/CHANGELOG.md | 2 +- zzz_archived_overseerr/README.md | 2 +- .../etc/cont-init.d/00-config_location.sh | 0 .../rootfs/etc/cont-init.d/20-folders.sh | 0 zzz_archived_paperless_ngx/CHANGELOG.md | 10 +- .../rootfs/etc/cont-init.d/99-run.sh | 5 +- .../etc/s6-overlay/s6-rc.d/init-tor/run | 38 +- .../rootfs/etc/s6-overlay/s6-rc.d/tor/run | 0 305 files changed, 2900 insertions(+), 2742 deletions(-) mode change 100644 => 100755 bazarr/rootfs/etc/services.d/nginx/run mode change 100644 => 100755 bentopdf/rootfs/etc/s6-overlay/s6-rc.d/bentopdf/run mode change 100644 => 100755 birdnet-pipy/rootfs/etc/services.d/api/run mode change 100644 => 100755 birdnet-pipy/rootfs/etc/services.d/icecast/run mode change 100644 => 100755 birdnet-pipy/rootfs/etc/services.d/main/run mode change 100644 => 100755 birdnet-pipy/rootfs/etc/services.d/model/run mode change 100644 => 100755 birdnet-pipy/rootfs/etc/services.d/nginx/run mode change 100644 => 100755 bitwarden/rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run mode change 100644 => 100755 bitwarden/rootfs/etc/s6-overlay/s6-rc.d/nginx/run mode change 100644 => 100755 bitwarden/rootfs/etc/s6-overlay/s6-rc.d/vaultwarden/run mode change 100755 => 100644 browser_chromium/rootfs/etc/cont-init.d/20-folders.sh mode change 100644 => 100755 changedetection.io/rootfs/etc/services.d/nginx/run mode change 100755 => 100644 claude_desktop/rootfs/usr/local/bin/codex-login mode change 100755 => 100644 claude_desktop/rootfs/usr/local/bin/ha-cli mode change 100755 => 100644 claude_desktop/rootfs/usr/local/bin/headroom-posttooluse-compress.py mode change 100644 => 100755 comicarr/rootfs/etc/services.d/comicarr/run mode change 100644 => 100755 comicarr/rootfs/etc/services.d/nginx/run mode change 100644 => 100755 emby/rootfs/etc/services.d/nginx/run mode change 100644 => 100755 jellyfin/rootfs/etc/services.d/nginx/run mode change 100755 => 100644 manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish mode change 100644 => 100755 netbird-server/rootfs/etc/services.d/caddy/run mode change 100644 => 100755 netbird-server/rootfs/etc/services.d/dashboard/run mode change 100644 => 100755 netbird-server/rootfs/etc/services.d/management/run mode change 100644 => 100755 netbird-server/rootfs/etc/services.d/relay/run mode change 100644 => 100755 netbird-server/rootfs/etc/services.d/signal/run mode change 100644 => 100755 nzbget/rootfs/etc/services.d/nginx/run mode change 100755 => 100644 obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/script mode change 100755 => 100644 obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/up mode change 100755 => 100644 obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/couchdb/finish mode change 100644 => 100755 qbittorrent/rootfs/etc/s6-overlay/s6-rc.d/svc-qbittorrent/run mode change 100644 => 100755 qbittorrent/rootfs/etc/services.d/timer/run mode change 100644 => 100755 qbittorrent/rootfs/etc/services.d/vpn-upnp/run mode change 100755 => 100644 qbittorrent/rootfs/usr/local/sbin/vpn mode change 100644 => 100755 readarr/rootfs/etc/services.d/nginx/run mode change 100755 => 100644 sabnzbd/rootfs/etc/services.d/nginx/finish mode change 100755 => 100644 scrutiny_fa/rootfs_overlay/etc/services.d/nginx/finish mode change 100644 => 100755 seerr/rootfs/etc/services.d/nginx/run mode change 100755 => 100644 seerr/rootfs/etc/services.d/seerr/finish mode change 100755 => 100644 transmission_openvpn/rootfs/etc/openvpn/update-resolv-conf mode change 100644 => 100755 zoraxy/rootfs/etc/services.d/zoraxy/run mode change 100644 => 100755 zzz_archived_kapowarr/rootfs/etc/services.d/kapowarr/run mode change 100644 => 100755 zzz_archived_kapowarr/rootfs/etc/services.d/nginx/run mode change 100644 => 100755 zzz_archived_omada/rootfs/etc/cont-init.d/99-run.sh mode change 100644 => 100755 zzz_archived_overseerr/rootfs/etc/cont-init.d/00-config_location.sh mode change 100644 => 100755 zzz_archived_overseerr/rootfs/etc/cont-init.d/20-folders.sh mode change 100644 => 100755 zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/tor/run diff --git a/.claude/skills/hassio-addon-workflow/scripts/env_trace.sh b/.claude/skills/hassio-addon-workflow/scripts/env_trace.sh index b092ff2720..92df333179 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/env_trace.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/env_trace.sh @@ -30,8 +30,14 @@ VAR="${1:?usage: env_trace.sh [process-name-or-pid]}" TARGET="${2:-}" # VAR is interpolated into grep/sed patterns below — restrict it to a valid env var name case "$VAR" in - [A-Za-z_]*) [ -z "${VAR//[A-Za-z0-9_]/}" ] || { echo "invalid env var name: $VAR" >&2; exit 1; } ;; - *) echo "invalid env var name: $VAR" >&2; exit 1 ;; + [A-Za-z_]*) [ -z "${VAR//[A-Za-z0-9_]/}" ] || { + echo "invalid env var name: $VAR" >&2 + exit 1 + } ;; + *) + echo "invalid env var name: $VAR" >&2 + exit 1 + ;; esac echo "== tracing ${VAR} ==" @@ -39,7 +45,7 @@ echo echo "1. /data/options.json (the user's saved options)" if [ -f /data/options.json ]; then - python3 - "$VAR" <<'PY' + python3 - "$VAR" << 'PY' import json, sys var = sys.argv[1] try: @@ -80,7 +86,8 @@ echo "3. s6 container_environment (only read by services using #!/usr/bin/with-c seen3=0 for d in /var/run/s6/container_environment /run/s6/container_environment; do if [ -f "$d/$VAR" ]; then - echo " $d/$VAR = [$(cat "$d/$VAR")]"; seen3=1 + echo " $d/$VAR = [$(cat "$d/$VAR")]" + seen3=1 fi done [ "$seen3" -eq 0 ] && echo " not present in either envdir" @@ -122,8 +129,8 @@ else done fi # What it was actually launched with beats any theory about its environment. - tr '\0' '\n' < "/proc/$pid/cmdline" 2> /dev/null | tail -n +2 | - grep -iE "res|screen|${VAR}" | head -3 | sed 's/^/ argv: /' + tr '\0' '\n' < "/proc/$pid/cmdline" 2> /dev/null | tail -n +2 \ + | grep -iE "res|screen|${VAR}" | head -3 | sed 's/^/ argv: /' done fi fi diff --git a/.claude/skills/hassio-addon-workflow/scripts/measure.sh b/.claude/skills/hassio-addon-workflow/scripts/measure.sh index d5c060154d..a0021851a8 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/measure.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/measure.sh @@ -31,7 +31,7 @@ awk '{t+=$2} END{printf " threads=%d\n", t}' <(ps -eo pid,nlwp --no-headers 2> echo echo "== per-process memory (top 20 by PSS) ==" printf ' %-28s %8s %8s %8s\n' COMMAND RSS PSS PRIVATE -python3 - "$OUT" <<'PY' +python3 - "$OUT" << 'PY' import os, sys rows = [] for pid in filter(str.isdigit, os.listdir('/proc')): @@ -62,17 +62,17 @@ PY echo echo "== reserved-but-not-resident (lazy allocations, NOT leaks) ==" -ipcs -m 2>/dev/null | awk 'NR>3 && $5 ~ /^[0-9]+$/ && $5 > 50000000 {printf " SysV shm %.0f MB (owner %s) — check Rss in /proc//smaps before calling it used\n", $5/1048576, $3}' +ipcs -m 2> /dev/null | awk 'NR>3 && $5 ~ /^[0-9]+$/ && $5 > 50000000 {printf " SysV shm %.0f MB (owner %s) — check Rss in /proc//smaps before calling it used\n", $5/1048576, $3}' echo echo "== CPU over ${SAMPLE}s (idle unless you are driving the UI) ==" # utime+stime. Parsed after the LAST ')' because field 2 is (comm) and may contain spaces — # a plain $14+$15 is wrong for anything like 'npm exec @foo' and silently reports a fabricated # number rather than failing. -jiffies() { awk -F') ' '{n=split($NF,a," "); print a[12]+a[13]}' "/proc/$1/stat" 2>/dev/null; } +jiffies() { awk -F') ' '{n=split($NF,a," "); print a[12]+a[13]}' "/proc/$1/stat" 2> /dev/null; } # jiffies are USER_HZ units — almost always 100, but read it rather than assume it -HZ=$(getconf CLK_TCK 2>/dev/null) && [ "$HZ" -gt 0 ] 2>/dev/null || HZ=100 +HZ=$(getconf CLK_TCK 2> /dev/null) && [ "$HZ" -gt 0 ] 2> /dev/null || HZ=100 # Sample EVERY readable process, not the top-N of ps.txt: that list is sorted by RSS, # and the busiest process is not necessarily a big one. @@ -86,14 +86,14 @@ for pid in "${!t0[@]}"; do [ -r "/proc/$pid/stat" ] || continue t1=$(jiffies "$pid") || continue [ -n "$t1" ] && [ -n "${t0[$pid]}" ] || continue - delta=$(( t1 - ${t0[$pid]} )) + delta=$((t1 - ${t0[$pid]})) [ "$delta" -gt 0 ] || continue pct=$(awk -v d="$delta" -v s="$SAMPLE" -v hz="$HZ" 'BEGIN{printf "%.2f", d*100/(hz*s)}') - comm=$(tr -d '\0' < "/proc/$pid/comm" 2>/dev/null) + comm=$(tr -d '\0' < "/proc/$pid/comm" 2> /dev/null) echo "$pct $pid $comm" done | sort -rn | head -12 | awk '{printf " %6s%% %-8s %s\n", $1, $2, $3}' echo -echo " established conns on :8082/:3000/:3001 = $(ss -tn 2>/dev/null | grep -cE 'ESTAB.*:(8082|3000|3001)')" +echo " established conns on :8082/:3000/:3001 = $(ss -tn 2> /dev/null | grep -cE 'ESTAB.*:(8082|3000|3001)')" echo " (those are claude_desktop/webtop viewer ports; 0 here means CPU above is idle burn)" echo " raw ps: $OUT/ps.txt" diff --git a/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh b/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh index 6f4570822c..fd350b27d7 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh @@ -22,146 +22,173 @@ set -uo pipefail REPO="${HASSIO_REPO:-}" [ -z "$REPO" ] && REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner 2> /dev/null) -[ -z "$REPO" ] && { echo "cannot determine repo; set HASSIO_REPO=owner/name" >&2; exit 1; } +[ -z "$REPO" ] && { + echo "cannot determine repo; set HASSIO_REPO=owner/name" >&2 + exit 1 +} echo "repo: $REPO" >&2 -CMD="${1:-}"; PR="${2:-}" -[ -z "$CMD" ] || [ -z "$PR" ] && { sed -n '2,20p' "$0" | sed 's/^# \?//'; exit 1; } +CMD="${1:-}" +PR="${2:-}" +[ -z "$CMD" ] || [ -z "$PR" ] && { + sed -n '2,20p' "$0" | sed 's/^# \?//' + exit 1 +} case "$CMD" in -list) - echo "== inline comments on #$PR ==" - gh api "repos/$REPO/pulls/$PR/comments" --paginate \ - --jq 'sort_by(.created_at)[] | "=== [\(.id)] \(.user.login) | \(.path):\(.line // .original_line) ===\n\(.body)\n"' - echo "== review bodies ==" - gh api "repos/$REPO/pulls/$PR/reviews" \ - --jq '.[] | select(.body != "") | "--- \(.user.login) (\(.state)) ---\n\(.body[0:4000])\n"' - ;; -status) - gh pr checks "$PR" 2>&1 | head -15 - echo - gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:50){nodes{id isResolved path comments(first:1){nodes{author{login}}}}}}}}" \ - --jq '.data.repository.pullRequest.reviewThreads.nodes[] | "\(if .isResolved then "resolved" else "OPEN " end) \(.id) \(.comments.nodes[0].author.login) \(.path)"' - ;; -reply) - ID="${3:?comment id}"; BODY="${4:?text or @file}" - if [ "${BODY#@}" != "$BODY" ]; then - out=$(gh api "repos/$REPO/pulls/$PR/comments/$ID/replies" -F body=@"${BODY#@}" --jq '.id' 2>&1) - rc=$? - else - out=$(gh api "repos/$REPO/pulls/$PR/comments/$ID/replies" -f body="$BODY" --jq '.id' 2>&1) - rc=$? - fi - # Silently "succeeding" here is worse than failing: a later session reads the transcript and - # believes a reviewer was answered when they were not. - if [ "$rc" -eq 0 ] && [ -n "$out" ]; then - echo "replied to $ID (comment $out)" - else - echo "FAILED to reply to $ID: $out" >&2 - echo " (top-level review bodies have different ids and cannot take replies here)" >&2 - exit 1 - fi - ;; -resolve) - shift 2 - ids="$*" - if [ "${ids:-}" = "--all" ]; then - echo "About to resolve EVERY unresolved thread. Only do this if you have read and" - echo "answered each one — a resolved-but-wrong thread is worse than an open one." - gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:100){nodes{isResolved path comments(first:1){nodes{author{login} body}}}}}}}" \ - --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false) | " - \(.comments.nodes[0].author.login) \(.path): \(.comments.nodes[0].body[0:90])"' - printf 'Type "yes" to resolve all: '; read -r ok - [ "$ok" = "yes" ] || { echo "aborted"; exit 1; } - ids="" - elif [ -z "$ids" ]; then - echo "usage: pr_review.sh resolve (or --all, with confirmation)" >&2 - echo "resolve each thread as you answer it; get ids from: pr_review.sh status $PR" >&2 - exit 1 - fi - if [ -z "$ids" ]; then - ids=$(gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:50){nodes{id isResolved}}}}}" \ - --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false) | .id') - fi - [ -z "$ids" ] && { echo "nothing unresolved"; exit 0; } - rfail=0 - for id in $ids; do - r=$(gh api graphql -f query="mutation{resolveReviewThread(input:{threadId:\"$id\"}){thread{isResolved}}}" \ - --jq '.data.resolveReviewThread.thread.isResolved' 2>&1) - echo " $id -> $r" - [ "$r" = "true" ] || rfail=1 - done - # exiting 0 on a failed mutation would let a session believe threads were resolved - exit "$rfail" - ;; -watch) - MINS="${3:-180}" # the addon build alone has taken ~3h; 20 was far too short - # Checks that are red on essentially every add-on PR here and gate nothing: master carries no - # branch protection, and #3019, #3044 and #3050 all merged with Codacy failing. They are kept - # out of the verdict but always printed, so the reader still sees them and can judge. This is - # deliberately a denylist of known noise, not an allowlist of blocking checks — a job added to - # CI later counts as blocking until someone puts it here on purpose. - ADVISORY_CHECKS="Codacy Static Code Analysis" # one per line if more are ever added - wfail=2 # not 0: running out of minutes with checks still pending is not a pass - c=""; bstates=""; adv="" - for i in $(seq 1 "$MINS"); do - # gh pr checks emits TAB-separated columns with no header when its output is not a TTY, - # which inside this $(... | awk) it never is. (Attached to a terminal it prints a wholly - # different ANSI table; --json would be sturdier still but does not exist before gh 2.36, - # and 2.23 ships here.) Every blocking gate has spaces in its name — "Addon linting - # (wger)", "Test addon build (wger)" — so awk's default separator split them on - # whitespace: "Codacy Static Code Analysisfail" became "Codacy=Static" and the state - # column was never read at all. watch printed "all passing" on a red #3044 and on #3042 - # with the linter failing, and could not see a pending build either. - # If that format ever does change, the allowlist below fails safe rather than passing: a - # header row lands in the failure branch, and a space-aligned table parses to no rows, - # which keeps watch waiting instead of returning 0. - rows=$(gh pr checks "$PR" 2> /dev/null | awk -F'\t' -v ADV="$ADVISORY_CHECKS" ' + list) + echo "== inline comments on #$PR ==" + gh api "repos/$REPO/pulls/$PR/comments" --paginate \ + --jq 'sort_by(.created_at)[] | "=== [\(.id)] \(.user.login) | \(.path):\(.line // .original_line) ===\n\(.body)\n"' + echo "== review bodies ==" + gh api "repos/$REPO/pulls/$PR/reviews" \ + --jq '.[] | select(.body != "") | "--- \(.user.login) (\(.state)) ---\n\(.body[0:4000])\n"' + ;; + status) + gh pr checks "$PR" 2>&1 | head -15 + echo + gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:50){nodes{id isResolved path comments(first:1){nodes{author{login}}}}}}}}" \ + --jq '.data.repository.pullRequest.reviewThreads.nodes[] | "\(if .isResolved then "resolved" else "OPEN " end) \(.id) \(.comments.nodes[0].author.login) \(.path)"' + ;; + reply) + ID="${3:?comment id}" + BODY="${4:?text or @file}" + if [ "${BODY#@}" != "$BODY" ]; then + out=$(gh api "repos/$REPO/pulls/$PR/comments/$ID/replies" -F body=@"${BODY#@}" --jq '.id' 2>&1) + rc=$? + else + out=$(gh api "repos/$REPO/pulls/$PR/comments/$ID/replies" -f body="$BODY" --jq '.id' 2>&1) + rc=$? + fi + # Silently "succeeding" here is worse than failing: a later session reads the transcript and + # believes a reviewer was answered when they were not. + if [ "$rc" -eq 0 ] && [ -n "$out" ]; then + echo "replied to $ID (comment $out)" + else + echo "FAILED to reply to $ID: $out" >&2 + echo " (top-level review bodies have different ids and cannot take replies here)" >&2 + exit 1 + fi + ;; + resolve) + shift 2 + ids="$*" + if [ "${ids:-}" = "--all" ]; then + echo "About to resolve EVERY unresolved thread. Only do this if you have read and" + echo "answered each one — a resolved-but-wrong thread is worse than an open one." + gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:100){nodes{isResolved path comments(first:1){nodes{author{login} body}}}}}}}" \ + --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false) | " - \(.comments.nodes[0].author.login) \(.path): \(.comments.nodes[0].body[0:90])"' + printf 'Type "yes" to resolve all: ' + read -r ok + [ "$ok" = "yes" ] || { + echo "aborted" + exit 1 + } + ids="" + elif [ -z "$ids" ]; then + echo "usage: pr_review.sh resolve (or --all, with confirmation)" >&2 + echo "resolve each thread as you answer it; get ids from: pr_review.sh status $PR" >&2 + exit 1 + fi + if [ -z "$ids" ]; then + ids=$(gh api graphql -f query="{repository(owner:\"${REPO%%/*}\",name:\"${REPO##*/}\"){pullRequest(number:$PR){reviewThreads(first:50){nodes{id isResolved}}}}}" \ + --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false) | .id') + fi + [ -z "$ids" ] && { + echo "nothing unresolved" + exit 0 + } + rfail=0 + for id in $ids; do + r=$(gh api graphql -f query="mutation{resolveReviewThread(input:{threadId:\"$id\"}){thread{isResolved}}}" \ + --jq '.data.resolveReviewThread.thread.isResolved' 2>&1) + echo " $id -> $r" + [ "$r" = "true" ] || rfail=1 + done + # exiting 0 on a failed mutation would let a session believe threads were resolved + exit "$rfail" + ;; + watch) + MINS="${3:-180}" # the addon build alone has taken ~3h; 20 was far too short + # Checks that are red on essentially every add-on PR here and gate nothing: master carries no + # branch protection, and #3019, #3044 and #3050 all merged with Codacy failing. They are kept + # out of the verdict but always printed, so the reader still sees them and can judge. This is + # deliberately a denylist of known noise, not an allowlist of blocking checks — a job added to + # CI later counts as blocking until someone puts it here on purpose. + ADVISORY_CHECKS="Codacy Static Code Analysis" # one per line if more are ever added + wfail=2 # not 0: running out of minutes with checks still pending is not a pass + c="" + bstates="" + adv="" + for i in $(seq 1 "$MINS"); do + # gh pr checks emits TAB-separated columns with no header when its output is not a TTY, + # which inside this $(... | awk) it never is. (Attached to a terminal it prints a wholly + # different ANSI table; --json would be sturdier still but does not exist before gh 2.36, + # and 2.23 ships here.) Every blocking gate has spaces in its name — "Addon linting + # (wger)", "Test addon build (wger)" — so awk's default separator split them on + # whitespace: "Codacy Static Code Analysisfail" became "Codacy=Static" and the state + # column was never read at all. watch printed "all passing" on a red #3044 and on #3042 + # with the linter failing, and could not see a pending build either. + # If that format ever does change, the allowlist below fails safe rather than passing: a + # header row lands in the failure branch, and a space-aligned table parses to no rows, + # which keeps watch waiting instead of returning 0. + rows=$(gh pr checks "$PR" 2> /dev/null | awk -F'\t' -v ADV="$ADVISORY_CHECKS" ' BEGIN { n = split(ADV, a, "\n"); for (j = 1; j <= n; j++) adv[a[j]] = 1 } NF >= 2 { print (($1 in adv) ? "A" : "B") "\t" $1 "=" $2 "\t" $2 }') - if [ -z "$rows" ]; then - # Normal in the first minutes after `gh pr create`, and also whenever gh errors. - # Calling that "settled" would report success for checks that never ran. - echo "[$i] no checks reported yet (gh returned nothing) — still waiting" - sleep 60; continue - fi - c=$(printf '%s\n' "$rows" | cut -f2 | tr '\n' ' ') - echo "[$i] $c" - # Judge the state column only, never the joined name=state line: a check whose NAME - # contains "fail" must not read as a failure. - bstates=$(printf '%s\n' "$rows" | awk -F'\t' '$1 == "B" { print $3 }' | tr '\n' ' ') - adv=$(printf '%s\n' "$rows" | awk -F'\t' '$1 == "A" { print $2 }' | tr '\n' ' ') - if [ -z "$bstates" ]; then - echo " only advisory checks have reported — no blocking check has run yet" - sleep 60; continue - fi - # Allowlist the good states rather than denylisting the bad ones: an unrecognised state - # must land in the failure branch, because falling through to "passing" is this command's - # worst outcome. - nbad=0; npend=0 - for s in $bstates; do - case "$s" in - pass | skipping) ;; - pending) npend=$((npend + 1)) ;; - *) nbad=$((nbad + 1)) ;; - esac + if [ -z "$rows" ]; then + # Normal in the first minutes after `gh pr create`, and also whenever gh errors. + # Calling that "settled" would report success for checks that never ran. + echo "[$i] no checks reported yet (gh returned nothing) — still waiting" + sleep 60 + continue + fi + c=$(printf '%s\n' "$rows" | cut -f2 | tr '\n' ' ') + echo "[$i] $c" + # Judge the state column only, never the joined name=state line: a check whose NAME + # contains "fail" must not read as a failure. + bstates=$(printf '%s\n' "$rows" | awk -F'\t' '$1 == "B" { print $3 }' | tr '\n' ' ') + adv=$(printf '%s\n' "$rows" | awk -F'\t' '$1 == "A" { print $2 }' | tr '\n' ' ') + if [ -z "$bstates" ]; then + echo " only advisory checks have reported — no blocking check has run yet" + sleep 60 + continue + fi + # Allowlist the good states rather than denylisting the bad ones: an unrecognised state + # must land in the failure branch, because falling through to "passing" is this command's + # worst outcome. + nbad=0 + npend=0 + for s in $bstates; do + case "$s" in + pass | skipping) ;; + pending) npend=$((npend + 1)) ;; + *) nbad=$((nbad + 1)) ;; + esac + done + if [ "$nbad" -gt 0 ]; then + echo "settled — blocking checks FAILED:" + printf '%s\n' "$rows" \ + | awk -F'\t' '$1 == "B" && $3 != "pass" && $3 != "skipping" && $3 != "pending" { print " " $2 }' + wfail=1 + break + elif [ "$npend" -gt 0 ]; then + sleep 60 + continue + else + echo "settled — blocking checks passing" + wfail=0 + break + fi done - if [ "$nbad" -gt 0 ]; then - echo "settled — blocking checks FAILED:" - printf '%s\n' "$rows" | - awk -F'\t' '$1 == "B" && $3 != "pass" && $3 != "skipping" && $3 != "pending" { print " " $2 }' - wfail=1; break - elif [ "$npend" -gt 0 ]; then - sleep 60; continue - else - echo "settled — blocking checks passing"; wfail=0; break - fi - done - [ "$wfail" -eq 2 ] && echo "gave up after ${MINS}m, checks still unsettled — NOT a pass" - # Printed on pass and on failure alike: it is excluded from the verdict, not hidden. - [ -n "$adv" ] && echo " advisory (non-blocking, not counted in the verdict): $adv" - # A PR touching no */config.* skips the CHANGELOG, linter and build jobs outright (#3018). - case " $bstates " in *" skipping "*) echo " ...of which some were SKIPPED — a skipped job tested nothing" ;; esac - echo "note: long queues here are usually account runner contention, not your diff." - exit "$wfail" - ;; -*) echo "unknown: $CMD"; exit 1 ;; + [ "$wfail" -eq 2 ] && echo "gave up after ${MINS}m, checks still unsettled — NOT a pass" + # Printed on pass and on failure alike: it is excluded from the verdict, not hidden. + [ -n "$adv" ] && echo " advisory (non-blocking, not counted in the verdict): $adv" + # A PR touching no */config.* skips the CHANGELOG, linter and build jobs outright (#3018). + case " $bstates " in *" skipping "*) echo " ...of which some were SKIPPED — a skipped job tested nothing" ;; esac + echo "note: long queues here are usually account runner contention, not your diff." + exit "$wfail" + ;; + *) + echo "unknown: $CMD" + exit 1 + ;; esac diff --git a/.claude/skills/hassio-addon-workflow/scripts/preflight.sh b/.claude/skills/hassio-addon-workflow/scripts/preflight.sh index 6c2ecdd73c..854b58312c 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/preflight.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/preflight.sh @@ -59,7 +59,10 @@ if [ -n "${BUILD_VERSION:-}" ]; then if [ -z "$SLUG" ] && [ -n "${HOSTNAME:-}" ]; then base=$(printf '%s' "$HOSTNAME" | sed 's/^[0-9a-f]\{8\}-//') for cand in "$(printf '%s' "$base" | tr '-' '_')" "$base"; do - [ -f "$REPO/$cand/config.yaml" ] && { SLUG="$cand"; break; } + [ -f "$REPO/$cand/config.yaml" ] && { + SLUG="$cand" + break + } done [ -z "$SLUG" ] && SLUG="$base" fi @@ -81,8 +84,8 @@ if [ -n "${BUILD_VERSION:-}" ]; then else echo " MISMATCH — this branch is NOT what is running." git fetch origin master --quiet 2> /dev/null - master=$(git show origin/master:"$SLUG/config.yaml" 2> /dev/null | - grep -E '^version:' | head -1 | tr -d "\"'" | awk '{print $2}') + master=$(git show origin/master:"$SLUG/config.yaml" 2> /dev/null \ + | grep -E '^version:' | head -1 | tr -d "\"'" | awk '{print $2}') echo " origin/master version = ${master:-unknown}" echo " -> work from origin/master; analysing this branch will mislead you." echo diff --git a/.claude/skills/hassio-addon-workflow/scripts/traps.sh b/.claude/skills/hassio-addon-workflow/scripts/traps.sh index 33e5f01dcd..e97546d48a 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/traps.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/traps.sh @@ -12,7 +12,10 @@ set -uo pipefail TRAPS="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/references/traps.md" -[ -f "$TRAPS" ] || { echo "not found: $TRAPS" >&2; exit 1; } +[ -f "$TRAPS" ] || { + echo "not found: $TRAPS" >&2 + exit 1 +} # The Contents list duplicates the headings; grep the headings themselves so the list cannot drift. list() { @@ -20,13 +23,16 @@ list() { grep '^## ' "$TRAPS" | grep -v '^## Contents' | sed 's/^## / /' } -[ $# -eq 0 ] && { list; exit 0; } +[ $# -eq 0 ] && { + list + exit 0 +} # awk over exact heading text: section names contain '/' and other characters that would need # escaping in a sed address, and a keyword matching several headings should be an error, not a # silent pick of the first. -mapfile -t matches < <(grep '^## ' "$TRAPS" | grep -v '^## Contents' | - grep -iF -- "$1" | sed 's/^## //') +mapfile -t matches < <(grep '^## ' "$TRAPS" | grep -v '^## Contents' \ + | grep -iF -- "$1" | sed 's/^## //') case "${#matches[@]}" in 0) diff --git a/.claude/skills/hassio-addon-workflow/scripts/validate.sh b/.claude/skills/hassio-addon-workflow/scripts/validate.sh index 442f5361ae..794dc97c87 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/validate.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/validate.sh @@ -13,22 +13,31 @@ set -uo pipefail if root=$(git rev-parse --show-toplevel 2> /dev/null); then cd "$root" || exit 1; fi ADDON="${1:-}" -[ "${ADDON:-}" = "--vs-master" ] && { ADDON=""; set -- --vs-master; } +[ "${ADDON:-}" = "--vs-master" ] && { + ADDON="" + set -- --vs-master +} VS_MASTER=false for a in "$@"; do [ "$a" = "--vs-master" ] && VS_MASTER=true; done if [ -z "$ADDON" ]; then - mapfile -t _dirs < <(git diff --name-only origin/master...HEAD 2> /dev/null | - cut -d/ -f1 | sort -u | grep -vE '^\.' ) + mapfile -t _dirs < <(git diff --name-only origin/master...HEAD 2> /dev/null \ + | cut -d/ -f1 | sort -u | grep -vE '^\.') if [ "${#_dirs[@]}" -gt 1 ]; then echo "several changed dirs: ${_dirs[*]}" - echo "pass one explicitly: validate.sh "; exit 1 + echo "pass one explicitly: validate.sh " + exit 1 fi ADDON="${_dirs[0]:-}" fi -[ -z "$ADDON" ] && { echo "usage: validate.sh [--vs-master]"; exit 1; } +[ -z "$ADDON" ] && { + echo "usage: validate.sh [--vs-master]" + exit 1 +} git rev-parse --verify origin/master > /dev/null 2>&1 || { - echo "origin/master missing — run: git fetch origin master"; exit 1; } + echo "origin/master missing — run: git fetch origin master" + exit 1 +} export PYTHONDONTWRITEBYTECODE=1 echo "== validating $ADDON ==" @@ -36,14 +45,22 @@ fail=0 note() { printf ' %-13s %s\n' "$1" "$2"; } # execline `run`/`finish` files are not shell (25 of them here, across 21 add-ons). Neither # linter below can read one, so anything either says about it is noise. -is_execline() { local l; IFS= read -r l < "$1" 2> /dev/null; [[ $l == '#!'*execlineb* ]]; } +is_execline() { + local l + IFS= read -r l < "$1" 2> /dev/null + [[ $l == '#!'*execlineb* ]] +} # Shell: bash -n then shellcheck -x (follows sourced files, as CI does). One list for both. files=() while IFS= read -r f; do is_execline "$f" || files+=("$f"); done \ < <(find "$ADDON" -type f \( -name '*.sh' -o -name 'run' -o -name 'finish' -o -name 'autostart' \) 2> /dev/null) for f in "${files[@]}"; do - if ! out=$(bash -n "$f" 2>&1); then note "bash -n" "FAIL $f"; echo "$out" | sed 's/^/ /'; fail=1; fi + if ! out=$(bash -n "$f" 2>&1); then + note "bash -n" "FAIL $f" + echo "$out" | sed 's/^/ /' + fail=1 + fi done [ "$fail" -eq 0 ] && note "bash -n" "${#files[@]} file(s) checked" @@ -65,13 +82,16 @@ command -v hadolint > /dev/null 2>&1 && [ -f "$ADDON/Dockerfile" ] && { if [ -f "$ADDON/config.yaml" ]; then # path passed as argv, never interpolated into Python source - python3 - "$ADDON/config.yaml" <<'PY' || { note "config.yaml" "FAIL parse"; fail=1; } + python3 - "$ADDON/config.yaml" << 'PY' || { import yaml,sys d=yaml.safe_load(open(sys.argv[1])) print(' %-13s ok (version=%s, %d options)' % ('config.yaml', d.get('version'), len(d.get('options') or {}))) missing=[k for k in (d.get('options') or {}) if k not in (d.get('schema') or {})] if missing: print(' %-13s options with no schema entry: %s' % ('WARN', missing)); sys.exit(0) PY + note "config.yaml" "FAIL parse" + fail=1 + } command -v yamllint > /dev/null 2>&1 && { yl=$(yamllint -f parsable "$ADDON/config.yaml" 2>&1 | grep -c .) note "yamllint" "$yl finding(s) (compare with --vs-master)" @@ -79,7 +99,10 @@ PY fi while IFS= read -r f; do - python3 -m py_compile "$f" 2> /dev/null || { note "py_compile" "FAIL $f"; fail=1; } + python3 -m py_compile "$f" 2> /dev/null || { + note "py_compile" "FAIL $f" + fail=1 + } done < <(find "$ADDON" -type f -name '*.py' 2> /dev/null) $VS_MASTER && command -v npx > /dev/null 2>&1 && [ -f "$ADDON/CHANGELOG.md" ] && { @@ -96,7 +119,8 @@ if git diff --name-only origin/master...HEAD 2> /dev/null | grep -Fxq "$ADDON/CH note "CHANGELOG" "updated" else # This one IS gated: onpr_check-pr.yaml exits 1 without it. - note "CHANGELOG" "NOT UPDATED for $ADDON — CI hard-gates this"; fail=1 + note "CHANGELOG" "NOT UPDATED for $ADDON — CI hard-gates this" + fail=1 fi if git diff origin/master...HEAD -- "$ADDON/config.yaml" 2> /dev/null | grep -q '^+version:'; then note "version" "bumped" @@ -110,7 +134,8 @@ note "docker build" "NOT tested locally (dockerd unavailable) — CI is the only if $VS_MASTER; then echo echo "== findings ADDED by this diff (pre-existing ones filtered out) ==" - tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT added=0 # Fed by process substitution, not a pipe: a pipeline runs this in a subshell, where the # findings below could never reach $fail and the verdict would contradict the list. @@ -125,14 +150,26 @@ if $VS_MASTER; then # cancel it out and report a false clean. case "$f" in *.sh | *autostart | */run | */finish) - command -v shellcheck > /dev/null 2>&1 || { echo " $f: SKIPPED (shellcheck not installed)"; continue; } - cmd() { shellcheck -x -f gcc "$1" 2>&1 | sed 's/^[^:]*:[0-9]*:[0-9]*://'; } ;; + command -v shellcheck > /dev/null 2>&1 || { + echo " $f: SKIPPED (shellcheck not installed)" + continue + } + cmd() { shellcheck -x -f gcc "$1" 2>&1 | sed 's/^[^:]*:[0-9]*:[0-9]*://'; } + ;; *.yaml | *.yml) - command -v yamllint > /dev/null 2>&1 || { echo " $f: SKIPPED (yamllint not installed)"; continue; } - cmd() { yamllint -f parsable "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*:[0-9]*//'; } ;; + command -v yamllint > /dev/null 2>&1 || { + echo " $f: SKIPPED (yamllint not installed)" + continue + } + cmd() { yamllint -f parsable "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*:[0-9]*//'; } + ;; *Dockerfile) - command -v hadolint > /dev/null 2>&1 || { echo " $f: SKIPPED (hadolint not installed)"; continue; } - cmd() { hadolint "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*//'; } ;; + command -v hadolint > /dev/null 2>&1 || { + echo " $f: SKIPPED (hadolint not installed)" + continue + } + cmd() { hadolint "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*//'; } + ;; *) continue ;; esac b=$(cmd "$tmp/base" | sort) @@ -141,7 +178,8 @@ if $VS_MASTER; then [ -n "$new" ] && { echo " $f: $(printf '%s\n' "$new" | grep -c .) NEW finding(s)" printf '%s\n' "$new" | sed 's/^/ /' | head -5 - added=1; fail=1 + added=1 + fail=1 } done < <(git diff --name-only origin/master...HEAD -- "$ADDON" 2> /dev/null) [ "$added" -eq 0 ] && echo " (none — this diff introduced no new lint findings)" diff --git a/.github/prompts/issue-classify.md b/.github/prompts/issue-classify.md index fe2e7d9562..4ff92a0865 100644 --- a/.github/prompts/issue-classify.md +++ b/.github/prompts/issue-classify.md @@ -40,7 +40,7 @@ the check is a guard, not a common case.) ## Rule 1 — pick exactly one verdict | verdict | when | -|---|---| +| --- | --- | | `duplicate` | An existing open or closed issue reports the same thing. Set `duplicate_of`. | | `needs-info` | You cannot tell what is wrong without the add-on version, HA version, architecture, config, or the actual log output. | | `question` | A usage question answerable from `DOCS.md`, the wiki, or the add-on config. Not a defect. | diff --git a/.github/prompts/issue-fix.md b/.github/prompts/issue-fix.md index d0cfbfa1c2..1d710dfda5 100644 --- a/.github/prompts/issue-fix.md +++ b/.github/prompts/issue-fix.md @@ -39,7 +39,7 @@ rest are on you: nothing checks them, so breaking one ships silently. that value, and compare: | `version` vs `U` | what to do | example | - |---|---|---| + | --- | --- | --- | | identical | **append** `.1` | sonarr `4.0.19.3001` -> `4.0.19.3001.1` | | `U` + `.` + digits | **increment** those digits | radarr `6.3.0.10514.1` -> `6.3.0.10514.2` | | anything else | **leave it alone** | plex, readarr, joal | diff --git a/.github/scripts/ai_guard_paths.sh b/.github/scripts/ai_guard_paths.sh index 6e37523c82..94468b2d2d 100755 --- a/.github/scripts/ai_guard_paths.sh +++ b/.github/scripts/ai_guard_paths.sh @@ -44,7 +44,7 @@ for pr in $PRS; do echo "$BAD" # Ensure the label exists before adding it — with set -e a missing # label would abort the whole loop and skip any PRs behind this one. - gh label create "ai:blocked" --repo "$REPO" --color ededed >/dev/null 2>&1 || true + gh label create "ai:blocked" --repo "$REPO" --color ededed > /dev/null 2>&1 || true gh pr ready "$pr" --repo "$REPO" --undo || true gh pr edit "$pr" --repo "$REPO" --add-label "ai:blocked" gh pr comment "$pr" --repo "$REPO" --body \ diff --git a/.github/scripts/ai_triage_context.sh b/.github/scripts/ai_triage_context.sh index 9d08d3a984..c8d1a71a31 100755 --- a/.github/scripts/ai_triage_context.sh +++ b/.github/scripts/ai_triage_context.sh @@ -15,110 +15,116 @@ CTX="$OUT/context.md" : > "$CTX" gh issue view "$ISSUE_NUMBER" --repo "$REPO" \ - --json number,title,body,author,labels,createdAt,comments > "$OUT/issue.json" + --json number,title,body,author,labels,createdAt,comments > "$OUT/issue.json" TITLE=$(jq -r '.title' "$OUT/issue.json") # ---------------------------------------------------------------- addon slug # Titles follow "🐛 [Immich Frame] ENV_VARS arent being picked up". -RAW=$(sed -n 's/.*\[\([^]]*\)\].*/\1/p' <<<"$TITLE" | head -n1) +RAW=$(sed -n 's/.*\[\([^]]*\)\].*/\1/p' <<< "$TITLE" | head -n1) ADDON="" if [ -n "$RAW" ]; then - CAND=$(tr '[:upper:] ' '[:lower:]_' <<<"$RAW") - # Directory list without checking out any of them. - git ls-tree -d --name-only HEAD > "$OUT/dirs.txt" - for guess in "$CAND" "${CAND//_/-}" "${CAND//_/.}"; do - if grep -qxF "$guess" "$OUT/dirs.txt"; then ADDON="$guess"; break; fi - done - # Separator-insensitive exact match: a title like "[Calibre-web]" (hyphen) - # against a directory named calibre_web (underscore) matches neither exact - # guess above, and would otherwise fall through to the substring fallback - # below, which picks the shorter "calibre" instead — the wrong add-on. - # Stripping -, _, . from both sides before comparing catches this case. - if [ -z "$ADDON" ]; then - CAND_STRIPPED=$(tr -d '_.-' <<<"$CAND") - while IFS= read -r dir; do - if [ "$(tr -d '_.-' <<<"$dir")" = "$CAND_STRIPPED" ]; then ADDON="$dir"; break; fi - done < "$OUT/dirs.txt" - fi - # Last resort: longest directory name contained in the candidate. - if [ -z "$ADDON" ]; then - ADDON=$(awk -v c="$CAND" 'length($0)>2 && index(c,$0){print length($0)"\t"$0}' \ + CAND=$(tr '[:upper:] ' '[:lower:]_' <<< "$RAW") + # Directory list without checking out any of them. + git ls-tree -d --name-only HEAD > "$OUT/dirs.txt" + for guess in "$CAND" "${CAND//_/-}" "${CAND//_/.}"; do + if grep -qxF "$guess" "$OUT/dirs.txt"; then + ADDON="$guess" + break + fi + done + # Separator-insensitive exact match: a title like "[Calibre-web]" (hyphen) + # against a directory named calibre_web (underscore) matches neither exact + # guess above, and would otherwise fall through to the substring fallback + # below, which picks the shorter "calibre" instead — the wrong add-on. + # Stripping -, _, . from both sides before comparing catches this case. + if [ -z "$ADDON" ]; then + CAND_STRIPPED=$(tr -d '_.-' <<< "$CAND") + while IFS= read -r dir; do + if [ "$(tr -d '_.-' <<< "$dir")" = "$CAND_STRIPPED" ]; then + ADDON="$dir" + break + fi + done < "$OUT/dirs.txt" + fi + # Last resort: longest directory name contained in the candidate. + if [ -z "$ADDON" ]; then + ADDON=$(awk -v c="$CAND" 'length($0)>2 && index(c,$0){print length($0)"\t"$0}' \ "$OUT/dirs.txt" | sort -rn | head -n1 | cut -f2) - fi + fi fi { - echo "# Issue #${ISSUE_NUMBER}" - echo - echo "Repo: ${REPO}" - echo "Addon resolved from title: ${ADDON:-UNRESOLVED}" - echo - echo "## Title" - echo "$TITLE" - echo - echo "## Author" - jq -r '.author.login' "$OUT/issue.json" - echo - echo "## Body" - echo '```' - jq -r '.body // "(empty)"' "$OUT/issue.json" - echo '```' - echo - echo "## Existing comments (in order)" - jq -r '.comments[]? | "### @\(.author.login)\n\(.body)\n"' "$OUT/issue.json" - echo - echo "## Existing labels" - jq -r '[.labels[]?.name] | join(", ")' "$OUT/issue.json" + echo "# Issue #${ISSUE_NUMBER}" + echo + echo "Repo: ${REPO}" + echo "Addon resolved from title: ${ADDON:-UNRESOLVED}" + echo + echo "## Title" + echo "$TITLE" + echo + echo "## Author" + jq -r '.author.login' "$OUT/issue.json" + echo + echo "## Body" + echo '```' + jq -r '.body // "(empty)"' "$OUT/issue.json" + echo '```' + echo + echo "## Existing comments (in order)" + jq -r '.comments[]? | "### @\(.author.login)\n\(.body)\n"' "$OUT/issue.json" + echo + echo "## Existing labels" + jq -r '[.labels[]?.name] | join(", ")' "$OUT/issue.json" } >> "$CTX" # ------------------------------------------------------------- addon sources if [ -n "$ADDON" ]; then - { - echo - echo "## Addon files: ${ADDON}/" - # `set` REPLACES the checkout list, so .templates has to be repeated here - # or the workflow's sparse-checkout of it is silently undone at this point - # — which is exactly the state that starved #2949 of its turn budget. - if ! git sparse-checkout set --no-cone .github/prompts .github/scripts .templates "$ADDON" 2>&1; then - # Swallowing this used to leave ADDON resolved with no files behind it, - # so the classifier could still reach high confidence off the addon - # name alone. Say so explicitly, in the same word Rule 2 already keys - # its low-confidence check on. - echo - echo "**Could not check out this add-on's source. Treat as UNRESOLVED for confidence purposes.**" - else - for f in config.yaml config.json Dockerfile CHANGELOG.md DOCS.md README.md; do - [ -f "$ADDON/$f" ] || continue + { echo - echo "### ${ADDON}/${f}" - echo '```' - head -c 8000 "$ADDON/$f" - echo '```' - done + echo "## Addon files: ${ADDON}/" + # `set` REPLACES the checkout list, so .templates has to be repeated here + # or the workflow's sparse-checkout of it is silently undone at this point + # — which is exactly the state that starved #2949 of its turn budget. + if ! git sparse-checkout set --no-cone .github/prompts .github/scripts .templates "$ADDON" 2>&1; then + # Swallowing this used to leave ADDON resolved with no files behind it, + # so the classifier could still reach high confidence off the addon + # name alone. Say so explicitly, in the same word Rule 2 already keys + # its low-confidence check on. + echo + echo "**Could not check out this add-on's source. Treat as UNRESOLVED for confidence purposes.**" + else + for f in config.yaml config.json Dockerfile CHANGELOG.md DOCS.md README.md; do + [ -f "$ADDON/$f" ] || continue + echo + echo "### ${ADDON}/${f}" + echo '```' + head -c 8000 "$ADDON/$f" + echo '```' + done - echo - echo "## Recent commits touching ${ADDON}/" - git log -n 15 --date=short --pretty='- %ad %h %s' -- "$ADDON" 2>/dev/null || true - fi - } >> "$CTX" + echo + echo "## Recent commits touching ${ADDON}/" + git log -n 15 --date=short --pretty='- %ad %h %s' -- "$ADDON" 2> /dev/null || true + fi + } >> "$CTX" fi # -------------------------------------------------------- possible duplicates { - echo - echo "## Similar existing issues (candidate duplicates)" - KEYWORDS=$(tr -cs '[:alnum:]' ' ' <<<"$TITLE" \ - | tr '[:upper:]' '[:lower:]' \ - | tr ' ' '\n' | awk 'length($0)>3' | head -n6 | paste -sd' ') - # Excludes the issue being triaged: if it's already indexed by GitHub search - # by the time this runs, keyword overlap with its own title would otherwise - # list it as a "candidate duplicate" of itself. - gh search issues --repo "$REPO" --limit 15 \ - --json number,title,state,url -- "$KEYWORDS" 2>/dev/null \ - | jq -r --argjson self "$ISSUE_NUMBER" \ - '.[] | select(.number != $self) | "- #\(.number) [\(.state)] \(.title)"' \ - || echo "(search unavailable)" + echo + echo "## Similar existing issues (candidate duplicates)" + KEYWORDS=$(tr -cs '[:alnum:]' ' ' <<< "$TITLE" \ + | tr '[:upper:]' '[:lower:]' \ + | tr ' ' '\n' | awk 'length($0)>3' | head -n6 | paste -sd' ') + # Excludes the issue being triaged: if it's already indexed by GitHub search + # by the time this runs, keyword overlap with its own title would otherwise + # list it as a "candidate duplicate" of itself. + gh search issues --repo "$REPO" --limit 15 \ + --json number,title,state,url -- "$KEYWORDS" 2> /dev/null \ + | jq -r --argjson self "$ISSUE_NUMBER" \ + '.[] | select(.number != $self) | "- #\(.number) [\(.state)] \(.title)"' \ + || echo "(search unavailable)" } >> "$CTX" echo "context bundle: $(wc -c < "$CTX") bytes, addon=${ADDON:-none}" diff --git a/.templates/00-banner.sh b/.templates/00-banner.sh index 60483f96e2..e33b16b241 100755 --- a/.templates/00-banner.sh +++ b/.templates/00-banner.sh @@ -6,7 +6,7 @@ set -e # Banner # ====================================================================== -if ! bashio::supervisor.ping 2>/dev/null; then +if ! bashio::supervisor.ping 2> /dev/null; then # Source standalone bashio first to provide function definitions if [ -f /usr/local/lib/bashio-standalone.sh ]; then source /usr/local/lib/bashio-standalone.sh @@ -19,11 +19,11 @@ if ! bashio::supervisor.ping 2>/dev/null; then if [ ! -f /usr/bin/bashio ]; then cp -rf /usr/local/lib/bashio-standalone.sh /usr/bin/bashio fi - grep -rl "^#!.*bashio" /etc | - while IFS= read -r f; do - grep -qF "source /usr/local/lib/bashio-standalone.sh" "$f" && continue - sed -i '1a source /usr/local/lib/bashio-standalone.sh' "$f" - done + grep -rl "^#!.*bashio" /etc \ + | while IFS= read -r f; do + grep -qF "source /usr/local/lib/bashio-standalone.sh" "$f" && continue + sed -i '1a source /usr/local/lib/bashio-standalone.sh' "$f" + done else bashio::log.blue '-----------------------------------------------------------' bashio::log.blue " Add-on: $(bashio::addon.name)" @@ -49,7 +49,7 @@ bashio::log.green ' Provided by: https://github.com/alexbelgium/hassio-addons ' bashio::log.blue '-----------------------------------------------------------' # Adapt user abc -if command -v id &>/dev/null && id abc &>/dev/null; then +if command -v id &> /dev/null && id abc &> /dev/null; then if bashio::config.has_value "PUID" && bashio::config.has_value "PGID"; then PUID="$(bashio::config "PUID")" PGID="$(bashio::config "PGID")" diff --git a/.templates/00-global_var.sh b/.templates/00-global_var.sh index 711c65c82d..11dc64b91b 100755 --- a/.templates/00-global_var.sh +++ b/.templates/00-global_var.sh @@ -152,15 +152,15 @@ if [[ "${1:-}" == "--self-test" ]]; then done fi - [[ "$self_test_rc" -eq 0 ]] && - echo "${#self_test_values[@]} values round-tripped unchanged (export block + dotenv)" + [[ "$self_test_rc" -eq 0 ]] \ + && echo "${#self_test_values[@]} values round-tripped unchanged (export block + dotenv)" exit "$self_test_rc" fi ################################################################################ # Guard: only run inside Supervisor-managed add-ons ################################################################################ -if ! bashio::supervisor.ping 2>/dev/null; then +if ! bashio::supervisor.ping 2> /dev/null; then echo "..." exit 0 fi @@ -179,7 +179,7 @@ ENV_FILE="/.env" ETC_ENV_FILE="/etc/environment" [[ -f "$JSONSOURCE" ]] || bashio::exit.nok "Missing $JSONSOURCE" -command -v jq >/dev/null || bashio::exit.nok "jq is required" +command -v jq > /dev/null || bashio::exit.nok "jq is required" mkdir -p /etc touch "$ETC_ENV_FILE" @@ -193,7 +193,10 @@ SECRETSOURCE="" resolve_secret() { local v="$1" name line - [[ "$v" =~ ^[[:space:]]*\!secret[[:space:]]+(.+)$ ]] || { printf '%s' "$v"; return; } + [[ "$v" =~ ^[[:space:]]*\!secret[[:space:]]+(.+)$ ]] || { + printf '%s' "$v" + return + } name="${BASH_REMATCH[1]}" [[ -n "$SECRETSOURCE" ]] || bashio::exit.nok "Secrets not mounted" diff --git a/.templates/00-smb_mounts.sh b/.templates/00-smb_mounts.sh index 50d902710e..3e714ecc7d 100755 --- a/.templates/00-smb_mounts.sh +++ b/.templates/00-smb_mounts.sh @@ -4,9 +4,9 @@ set -e -if ! bashio::supervisor.ping 2>/dev/null; then - bashio::log.blue "Disabled : please use another method" - exit 0 +if ! bashio::supervisor.ping 2> /dev/null; then + bashio::log.blue "Disabled : please use another method" + exit 0 fi bashio::log.notice "This script is used to mount remote smb/cifs/nfs shares. Instructions here : https://github.com/alexbelgium/hassio-addons/wiki/Mounting-remote-shares-in-Addons" @@ -16,167 +16,167 @@ bashio::log.notice "This script is used to mount remote smb/cifs/nfs shares. Ins #################### cleanup_cred() { - if [[ -n "${CRED_FILE:-}" && -f "${CRED_FILE:-}" ]]; then - rm -f "$CRED_FILE" || true - fi - CRED_FILE="" + if [[ -n "${CRED_FILE:-}" && -f "${CRED_FILE:-}" ]]; then + rm -f "$CRED_FILE" || true + fi + CRED_FILE="" } test_mount() { - MOUNTED=false - ERROR_MOUNT=false - mountpoint="/mnt/$diskname" - - if ! mountpoint -q "$mountpoint"; then - return 0 - fi - - _test_write() { - local testfile="$mountpoint/.writetest_$$" - if : >"$testfile" 2>/dev/null; then - rm -f "$testfile" 2>/dev/null || true - return 0 - else - rm -f "$testfile" 2>/dev/null || true - return 1 - fi - } - - if _test_write 2>/dev/null; then - MOUNTED=true - return 0 - fi - - local orig_mountoptions="$MOUNTOPTIONS" - - if [[ "$FSTYPE" == "cifs" && "$orig_mountoptions" != *"noserverino"* ]]; then - echo "... retrying mount with noserverino" - MOUNTOPTIONS="${orig_mountoptions},noserverino" - - umount "$mountpoint" 2>/dev/null || true - if mount_drive "$MOUNTOPTIONS"; then - if _test_write 2>/dev/null; then - MOUNTED=true - return 0 - fi - fi - fi - - if [[ "$FSTYPE" == "cifs" && "$orig_mountoptions" != *"noperm"* ]]; then - echo "... retrying mount with noperm" - MOUNTOPTIONS="${orig_mountoptions},noperm" - - umount "$mountpoint" 2>/dev/null || true - if mount_drive "$MOUNTOPTIONS"; then - if _test_write 2>/dev/null; then - MOUNTED=true - return 0 - fi - fi - fi - - if mountpoint -q "$mountpoint"; then - MOUNTED="readonly" - else MOUNTED=false - fi - return 0 + ERROR_MOUNT=false + mountpoint="/mnt/$diskname" + + if ! mountpoint -q "$mountpoint"; then + return 0 + fi + + _test_write() { + local testfile="$mountpoint/.writetest_$$" + if : > "$testfile" 2> /dev/null; then + rm -f "$testfile" 2> /dev/null || true + return 0 + else + rm -f "$testfile" 2> /dev/null || true + return 1 + fi + } + + if _test_write 2> /dev/null; then + MOUNTED=true + return 0 + fi + + local orig_mountoptions="$MOUNTOPTIONS" + + if [[ "$FSTYPE" == "cifs" && "$orig_mountoptions" != *"noserverino"* ]]; then + echo "... retrying mount with noserverino" + MOUNTOPTIONS="${orig_mountoptions},noserverino" + + umount "$mountpoint" 2> /dev/null || true + if mount_drive "$MOUNTOPTIONS"; then + if _test_write 2> /dev/null; then + MOUNTED=true + return 0 + fi + fi + fi + + if [[ "$FSTYPE" == "cifs" && "$orig_mountoptions" != *"noperm"* ]]; then + echo "... retrying mount with noperm" + MOUNTOPTIONS="${orig_mountoptions},noperm" + + umount "$mountpoint" 2> /dev/null || true + if mount_drive "$MOUNTOPTIONS"; then + if _test_write 2> /dev/null; then + MOUNTED=true + return 0 + fi + fi + fi + + if mountpoint -q "$mountpoint"; then + MOUNTED="readonly" + else + MOUNTED=false + fi + return 0 } mount_drive() { - MOUNTED=true - MOUNTOPTIONS="$1" + MOUNTED=true + MOUNTOPTIONS="$1" - if [[ "$FSTYPE" == "cifs" ]]; then - mount -t cifs -o "$MOUNTOPTIONS" "$disk" "/mnt/$diskname" 2>"$ERRORCODE_FILE" || MOUNTED=false - elif [[ "$FSTYPE" == "nfs" ]]; then - mount -t nfs -o "$MOUNTOPTIONS" "$disk" "/mnt/$diskname" 2>"$ERRORCODE_FILE" || MOUNTED=false - fi + if [[ "$FSTYPE" == "cifs" ]]; then + mount -t cifs -o "$MOUNTOPTIONS" "$disk" "/mnt/$diskname" 2> "$ERRORCODE_FILE" || MOUNTED=false + elif [[ "$FSTYPE" == "nfs" ]]; then + mount -t nfs -o "$MOUNTOPTIONS" "$disk" "/mnt/$diskname" 2> "$ERRORCODE_FILE" || MOUNTED=false + fi - if [[ "$MOUNTED" == "true" ]]; then - test_mount - fi + if [[ "$MOUNTED" == "true" ]]; then + test_mount + fi } # Retry ladder: SMB3 -> SMB2 when mount fails due to dialect/negotiation issues retry_cifs_with_vers_ladder_on_dialect_failure() { - [[ "${FSTYPE:-}" == "cifs" ]] || return 0 - [[ "${MOUNTED:-false}" == "false" ]] || return 0 - [[ "${CIFS_LADDER_ATTEMPTED:-false}" == "false" ]] || return 0 + [[ "${FSTYPE:-}" == "cifs" ]] || return 0 + [[ "${MOUNTED:-false}" == "false" ]] || return 0 + [[ "${CIFS_LADDER_ATTEMPTED:-false}" == "false" ]] || return 0 - local err mountpoint - mountpoint="/mnt/$diskname" - err="$(cat "$ERRORCODE_FILE" 2>/dev/null || true)" + local err mountpoint + mountpoint="/mnt/$diskname" + err="$(cat "$ERRORCODE_FILE" 2> /dev/null || true)" - if echo "$err" | grep -Eq 'mount error\(13\)|Permission denied|NT_STATUS_(LOGON_FAILURE|ACCESS_DENIED)|STATUS_(LOGON_FAILURE|ACCESS_DENIED)'; then - return 0 - fi + if echo "$err" | grep -Eq 'mount error\(13\)|Permission denied|NT_STATUS_(LOGON_FAILURE|ACCESS_DENIED)|STATUS_(LOGON_FAILURE|ACCESS_DENIED)'; then + return 0 + fi - if ! echo "$err" | grep -Eq 'mount error\(22\)|mount error\(95\)|mount error\(112\)|Server abruptly closed the connection|does not support the SMB version|Protocol negotiation|NT_STATUS_CONNECTION_DISCONNECTED'; then - return 0 - fi + if ! echo "$err" | grep -Eq 'mount error\(22\)|mount error\(95\)|mount error\(112\)|Server abruptly closed the connection|does not support the SMB version|Protocol negotiation|NT_STATUS_CONNECTION_DISCONNECTED'; then + return 0 + fi - CIFS_LADDER_ATTEMPTED=true - bashio::log.warning "...... CIFS negotiation/dialect failure: trying SMB dialect ladder (3.x -> 2.x -> 1.0)." + CIFS_LADDER_ATTEMPTED=true + bashio::log.warning "...... CIFS negotiation/dialect failure: trying SMB dialect ladder (3.x -> 2.x -> 1.0)." - local base_opts try_opts vers vopt sectry + local base_opts try_opts vers vopt sectry - base_opts="$MOUNTOPTIONS" - base_opts="$(echo "$base_opts" | sed -E 's/,vers=[^,]+//g; s/,sec=[^,]+//g')" - - local -a opt_variants=("" ",nounix" ",noserverino" ",nounix,noserverino") - local -a sec_variants=("" ",sec=ntlmssp" ",sec=ntlmv2" ",sec=ntlm") - local -a vers_variants=("3.1.1" "3.02" "3.0" "2.1" "2.0" "1.0") - - for vopt in "${opt_variants[@]}"; do - for vers in "${vers_variants[@]}"; do - for sectry in "${sec_variants[@]}"; do - [[ "$MOUNTED" == "false" ]] || break - umount "$mountpoint" 2>/dev/null || true - try_opts="${base_opts}${vopt},vers=${vers}${sectry}" - mount_drive "$try_opts" - done - [[ "$MOUNTED" == "false" ]] || break - done - [[ "$MOUNTED" == "false" ]] || break - done - - if [[ "$MOUNTED" == "false" ]]; then - bashio::log.warning "...... still failing after vers ladder; retrying with reduced CIFS options." base_opts="$MOUNTOPTIONS" base_opts="$(echo "$base_opts" | sed -E 's/,vers=[^,]+//g; s/,sec=[^,]+//g')" - base_opts="${base_opts//,mfsymlinks/}" - base_opts="${base_opts//,nobrl/}" - base_opts="$(echo "$base_opts" | sed -E 's/,iocharset=[^,]+//g')" - local -a vers_variants2=("2.1" "2.0" "1.0") + local -a opt_variants=("" ",nounix" ",noserverino" ",nounix,noserverino") + local -a sec_variants=("" ",sec=ntlmssp" ",sec=ntlmv2" ",sec=ntlm") + local -a vers_variants=("3.1.1" "3.02" "3.0" "2.1" "2.0" "1.0") + for vopt in "${opt_variants[@]}"; do - for vers in "${vers_variants2[@]}"; do - for sectry in "${sec_variants[@]}"; do - [[ "$MOUNTED" == "false" ]] || break - umount "$mountpoint" 2>/dev/null || true - try_opts="${base_opts}${vopt},vers=${vers}${sectry}" - mount_drive "$try_opts" + for vers in "${vers_variants[@]}"; do + for sectry in "${sec_variants[@]}"; do + [[ "$MOUNTED" == "false" ]] || break + umount "$mountpoint" 2> /dev/null || true + try_opts="${base_opts}${vopt},vers=${vers}${sectry}" + mount_drive "$try_opts" + done + [[ "$MOUNTED" == "false" ]] || break done [[ "$MOUNTED" == "false" ]] || break - done - [[ "$MOUNTED" == "false" ]] || break done if [[ "$MOUNTED" == "false" ]]; then - for vopt in "${opt_variants[@]}"; do - for vers in "${vers_variants2[@]}"; do - [[ "$MOUNTED" == "false" ]] || break - umount "$mountpoint" 2>/dev/null || true - try_opts="${base_opts}${vopt},vers=${vers},sec=ntlmssp" - mount_drive "$try_opts" - done - [[ "$MOUNTED" == "false" ]] || break - done - fi - fi + bashio::log.warning "...... still failing after vers ladder; retrying with reduced CIFS options." + base_opts="$MOUNTOPTIONS" + base_opts="$(echo "$base_opts" | sed -E 's/,vers=[^,]+//g; s/,sec=[^,]+//g')" + base_opts="${base_opts//,mfsymlinks/}" + base_opts="${base_opts//,nobrl/}" + base_opts="$(echo "$base_opts" | sed -E 's/,iocharset=[^,]+//g')" - return 0 + local -a vers_variants2=("2.1" "2.0" "1.0") + for vopt in "${opt_variants[@]}"; do + for vers in "${vers_variants2[@]}"; do + for sectry in "${sec_variants[@]}"; do + [[ "$MOUNTED" == "false" ]] || break + umount "$mountpoint" 2> /dev/null || true + try_opts="${base_opts}${vopt},vers=${vers}${sectry}" + mount_drive "$try_opts" + done + [[ "$MOUNTED" == "false" ]] || break + done + [[ "$MOUNTED" == "false" ]] || break + done + + if [[ "$MOUNTED" == "false" ]]; then + for vopt in "${opt_variants[@]}"; do + for vers in "${vers_variants2[@]}"; do + [[ "$MOUNTED" == "false" ]] || break + umount "$mountpoint" 2> /dev/null || true + try_opts="${base_opts}${vopt},vers=${vers},sec=ntlmssp" + mount_drive "$try_opts" + done + [[ "$MOUNTED" == "false" ]] || break + done + fi + fi + + return 0 } ######################## @@ -185,289 +185,289 @@ retry_cifs_with_vers_ladder_on_dialect_failure() { if bashio::config.has_value 'networkdisks'; then - echo "Mounting network share(s)..." + echo "Mounting network share(s)..." - MOREDISKS="$(bashio::config 'networkdisks')" - USERNAME="$(bashio::config 'cifsusername')" - PASSWORD="$(bashio::config 'cifspassword')" + MOREDISKS="$(bashio::config 'networkdisks')" + USERNAME="$(bashio::config 'cifsusername')" + PASSWORD="$(bashio::config 'cifspassword')" - SMBVERS="" - SECVERS="" - CHARSET=",iocharset=utf8" - - MOREDISKS="${MOREDISKS//$'\r'/}" - MOREDISKS="${MOREDISKS//$'\n'/,}" - MOREDISKS="$(echo "$MOREDISKS" | sed -E 's/[[:space:]]*,[[:space:]]*/,/g; s/^[[:space:]]+//; s/[[:space:]]+$//')" - - DOMAINCLIENT="" - CIFSDOMAIN="" - if bashio::config.has_value 'cifsdomain'; then - CIFSDOMAIN="$(bashio::config 'cifsdomain')" - echo "... using domain $CIFSDOMAIN" - DOMAINCLIENT="--workgroup=$CIFSDOMAIN" - fi - - PUID=",uid=$(id -u)" - PGID=",gid=$(id -g)" - if bashio::config.has_value 'PUID' && bashio::config.has_value 'PGID'; then - echo "... using PUID $(bashio::config 'PUID') and PGID $(bashio::config 'PGID')" - PUID=",uid=$(bashio::config 'PUID')" - PGID=",gid=$(bashio::config 'PGID')" - fi - - IFS=',' read -r -a DISK_LIST <<< "$MOREDISKS" - - for disk in "${DISK_LIST[@]}"; do - CRED_FILE="" - cleanup_cred - - disk="${disk//$'\r'/}" - disk="$(echo "$disk" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')" - [[ -z "$disk" ]] && continue - - disk="$(echo "$disk" | sed 's,/$,,')" - disk="${disk//"\040"/ }" - - FSTYPE="cifs" - if [[ "$disk" =~ ^nfs:// ]]; then - FSTYPE="nfs" - disk="${disk#nfs://}" - elif [[ "$disk" =~ ^[^/:]+:/.+ ]]; then - FSTYPE="nfs" - fi - - if [[ "$FSTYPE" == "cifs" ]]; then - server="$(echo "$disk" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1)" - else - server="${disk%%:*}" - fi - - diskname="$disk" - diskname="${diskname//\\//}" - diskname="${diskname##*/}" - - if mountpoint -q "/mnt/$diskname" 2>/dev/null; then - bashio::log.warning "...... mount point /mnt/$diskname already in use (name collision for $disk). Skipping this share." - continue - fi - - ERRORCODE_FILE="/tmp/mount_error_${diskname//[^a-zA-Z0-9._-]/_}.log" - : >"$ERRORCODE_FILE" || true - - MOUNTED=false - CIFS_LADDER_ATTEMPTED=false - SMBVERS_FORCE="" - SECVERS_FORCE="" SMBVERS="" SECVERS="" + CHARSET=",iocharset=utf8" - echo "... mounting ($FSTYPE) $disk" + MOREDISKS="${MOREDISKS//$'\r'/}" + MOREDISKS="${MOREDISKS//$'\n'/,}" + MOREDISKS="$(echo "$MOREDISKS" | sed -E 's/[[:space:]]*,[[:space:]]*/,/g; s/^[[:space:]]+//; s/[[:space:]]+$//')" - if [[ "$FSTYPE" == "cifs" ]]; then - if [[ ! "$disk" =~ ^//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/.+ ]]; then - bashio::log.fatal "...... invalid CIFS path \"$disk\". Use //123.12.12.12/sharedfolder,//123.12.12.12/sharedfolder2" - echo "Invalid CIFS path structure: $disk" >"$ERRORCODE_FILE" || true - continue - fi - else - if [[ ! "$disk" =~ ^[^/:]+:/.+ ]]; then - bashio::log.fatal "...... invalid NFS path \"$disk\". Use server:/export/path or 123.12.12.12:/export/path" - echo "Invalid NFS path structure: $disk" >"$ERRORCODE_FILE" || true - continue - fi + DOMAINCLIENT="" + CIFSDOMAIN="" + if bashio::config.has_value 'cifsdomain'; then + CIFSDOMAIN="$(bashio::config 'cifsdomain')" + echo "... using domain $CIFSDOMAIN" + DOMAINCLIENT="--workgroup=$CIFSDOMAIN" fi - mkdir -p "/mnt/$diskname" - chown root:root "/mnt/$diskname" + PUID=",uid=$(id -u)" + PGID=",gid=$(id -g)" + if bashio::config.has_value 'PUID' && bashio::config.has_value 'PGID'; then + echo "... using PUID $(bashio::config 'PUID') and PGID $(bashio::config 'PGID')" + PUID=",uid=$(bashio::config 'PUID')" + PGID=",gid=$(bashio::config 'PGID')" + fi - if [[ "$FSTYPE" == "cifs" ]]; then - CRED_FILE="$(mktemp /tmp/cifs-cred.XXXXXX)" - chmod 600 "$CRED_FILE" - { - printf 'username=%s\n' "$USERNAME" - printf 'password=%s\n' "$PASSWORD" - if [[ -n "${CIFSDOMAIN:-}" ]]; then - printf 'domain=%s\n' "$CIFSDOMAIN" + IFS=',' read -r -a DISK_LIST <<< "$MOREDISKS" + + for disk in "${DISK_LIST[@]}"; do + CRED_FILE="" + cleanup_cred + + disk="${disk//$'\r'/}" + disk="$(echo "$disk" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')" + [[ -z "$disk" ]] && continue + + disk="$(echo "$disk" | sed 's,/$,,')" + disk="${disk//"\040"/ }" + + FSTYPE="cifs" + if [[ "$disk" =~ ^nfs:// ]]; then + FSTYPE="nfs" + disk="${disk#nfs://}" + elif [[ "$disk" =~ ^[^/:]+:/.+ ]]; then + FSTYPE="nfs" fi - } >"$CRED_FILE" - fi - if [[ "$FSTYPE" == "cifs" ]]; then - mount_drive "rw,file_mode=0775,dir_mode=0775,credentials=${CRED_FILE},nobrl,mfsymlinks${SMBVERS}${SECVERS}${PUID}${PGID}${CHARSET}" - if [[ "$MOUNTED" == "false" ]]; then - retry_cifs_with_vers_ladder_on_dialect_failure - fi - else - mount_drive "rw,nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2" - fi + if [[ "$FSTYPE" == "cifs" ]]; then + server="$(echo "$disk" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1)" + else + server="${disk%%:*}" + fi - if [[ "$MOUNTED" == "false" ]]; then - if [[ "$FSTYPE" == "cifs" ]]; then - if command -v nmap >/dev/null 2>&1; then - output="$(nmap -F "$server" -T5 -oG - 2>/dev/null || true)" - if ! echo "$output" | grep -q "445/open"; then - if echo "$output" | grep -q "/open"; then - bashio::log.fatal "...... $server is reachable but SMB port not opened, stopping script" - else - bashio::log.fatal "...... fatal : $server not reachable, is it correct" - fi - cleanup_cred - rm -f "$ERRORCODE_FILE" 2>/dev/null || true + diskname="$disk" + diskname="${diskname//\\//}" + diskname="${diskname##*/}" + + if mountpoint -q "/mnt/$diskname" 2> /dev/null; then + bashio::log.warning "...... mount point /mnt/$diskname already in use (name collision for $disk). Skipping this share." continue - fi - echo "...... $server is confirmed reachable" - else - bashio::log.warning "...... nmap not available; skipping SMB port reachability test" fi - if command -v smbclient >/dev/null 2>&1; then - OUTPUT="$(smbclient -t 2 -L "$server" -U "$USERNAME%$PASSWORD" -c "exit" $DOMAINCLIENT 2>&1 || true)" - - if echo "$OUTPUT" | grep -q "LOGON_FAILURE"; then - bashio::log.fatal "...... incorrect Username, Password, or Domain! Script will stop." - cleanup_cred - rm -f "$ERRORCODE_FILE" 2>/dev/null || true - bashio::addon.stop - elif echo "$OUTPUT" | grep -q "tree connect failed" || echo "$OUTPUT" | grep -q "NT_STATUS_CONNECTION_DISCONNECTED"; then - echo "... using SMBv1" - bashio::log.warning "...... share reachable only with legacy SMBv1 (NT1) negotiation. Forcing SMBv1 options." - SMBVERS_FORCE=",vers=1.0" - SECVERS_FORCE=",sec=ntlm" - elif ! echo "$OUTPUT" | grep -q "Disk"; then - bashio::log.fatal "...... no shares found. Invalid or inaccessible SMB path?" - else - echo "...... credentials are valid" - fi - else - bashio::log.warning "...... smbclient not available; skipping SMB credential test" - fi - - SMBRAW="" - SMB1_DETECTED=false - if command -v nmap >/dev/null 2>&1; then - NMAP_OUTPUT="$(nmap --script smb-protocols -p 445 "$server" 2>/dev/null || true)" - SMBRAW="$( - echo "$NMAP_OUTPUT" \ - | awk '/SMB2_DIALECT_/ {print $NF}' \ - | sed 's/SMB2_DIALECT_//' \ - | tr -d '_' \ - | sort -V | tail -n 1 || true - )" - if [[ -z "$SMBRAW" ]] && echo "$NMAP_OUTPUT" | grep -Eiq 'NT LM 0\.12|SMBv1|NT1'; then - SMB1_DETECTED=true - fi - fi + ERRORCODE_FILE="/tmp/mount_error_${diskname//[^a-zA-Z0-9._-]/_}.log" + : > "$ERRORCODE_FILE" || true + MOUNTED=false + CIFS_LADDER_ATTEMPTED=false + SMBVERS_FORCE="" + SECVERS_FORCE="" SMBVERS="" - case "$SMBRAW" in - 311) SMBVERS=",vers=3.1.1" ;; - 302) SMBVERS=",vers=3.02" ;; - 300) SMBVERS=",vers=3.0" ;; - 210) SMBVERS=",vers=2.1" ;; - 202|200) SMBVERS=",vers=2.0" ;; - *) SMBVERS="" ;; - esac + SECVERS="" - if [[ -z "$SMBVERS" && "$SMB1_DETECTED" == "true" ]]; then - echo "...... SMB version detected via nmap : SMBv1 (NT LM 0.12)" - SMBVERS=",vers=1.0" - SECVERS=",sec=ntlm" - fi + echo "... mounting ($FSTYPE) $disk" - if [[ -n "$SMBVERS" ]]; then - echo "...... SMB version detected : ${SMBVERS#,vers=}" - elif command -v smbclient >/dev/null 2>&1 && smbclient -t 2 -L "$server" -m NT1 -U "$USERNAME%$PASSWORD" $DOMAINCLIENT -c "exit" &>/dev/null; then - echo "...... SMB version : only SMBv1 is supported, this can lead to issues" - SECVERS=",sec=ntlm" - SMBVERS=",vers=1.0" + if [[ "$FSTYPE" == "cifs" ]]; then + if [[ ! "$disk" =~ ^//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/.+ ]]; then + bashio::log.fatal "...... invalid CIFS path \"$disk\". Use //123.12.12.12/sharedfolder,//123.12.12.12/sharedfolder2" + echo "Invalid CIFS path structure: $disk" > "$ERRORCODE_FILE" || true + continue + fi else - echo "...... SMB version : couldn't detect, falling back to SMB3->SMB2->SMB1 ladder on negotiation/dialect failure" - SMBVERS=",vers=3.1.1" + if [[ ! "$disk" =~ ^[^/:]+:/.+ ]]; then + bashio::log.fatal "...... invalid NFS path \"$disk\". Use server:/export/path or 123.12.12.12:/export/path" + echo "Invalid NFS path structure: $disk" > "$ERRORCODE_FILE" || true + continue + fi fi - if [[ -n "$SMBVERS_FORCE" ]]; then - if [[ -n "$SMBVERS" && "$SMBVERS" != "$SMBVERS_FORCE" ]]; then - bashio::log.warning "...... overriding detected SMB version ${SMBVERS#,vers=} with forced ${SMBVERS_FORCE#,vers=} (server requires legacy protocol)" - fi - SMBVERS="$SMBVERS_FORCE" - [[ -z "$SECVERS" ]] && SECVERS="$SECVERS_FORCE" + mkdir -p "/mnt/$diskname" + chown root:root "/mnt/$diskname" + + if [[ "$FSTYPE" == "cifs" ]]; then + CRED_FILE="$(mktemp /tmp/cifs-cred.XXXXXX)" + chmod 600 "$CRED_FILE" + { + printf 'username=%s\n' "$USERNAME" + printf 'password=%s\n' "$PASSWORD" + if [[ -n "${CIFSDOMAIN:-}" ]]; then + printf 'domain=%s\n' "$CIFSDOMAIN" + fi + } > "$CRED_FILE" fi - SECVERS_BASE="$SECVERS" - for SECTRY in "$SECVERS_BASE" ",sec=ntlmv2" ",sec=ntlmssp" ",sec=ntlmsspi" ",sec=krb5i" ",sec=krb5" ",sec=ntlm" ",sec=ntlmv2i"; do - if [[ "$MOUNTED" == "false" ]]; then - mount_drive "rw,file_mode=0775,dir_mode=0775,credentials=${CRED_FILE},nobrl,mfsymlinks${SMBVERS}${SECTRY}${PUID}${PGID}${CHARSET}" - fi - done + if [[ "$FSTYPE" == "cifs" ]]; then + mount_drive "rw,file_mode=0775,dir_mode=0775,credentials=${CRED_FILE},nobrl,mfsymlinks${SMBVERS}${SECVERS}${PUID}${PGID}${CHARSET}" + if [[ "$MOUNTED" == "false" ]]; then + retry_cifs_with_vers_ladder_on_dialect_failure + fi + else + mount_drive "rw,nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2" + fi if [[ "$MOUNTED" == "false" ]]; then - retry_cifs_with_vers_ladder_on_dialect_failure + if [[ "$FSTYPE" == "cifs" ]]; then + if command -v nmap > /dev/null 2>&1; then + output="$(nmap -F "$server" -T5 -oG - 2> /dev/null || true)" + if ! echo "$output" | grep -q "445/open"; then + if echo "$output" | grep -q "/open"; then + bashio::log.fatal "...... $server is reachable but SMB port not opened, stopping script" + else + bashio::log.fatal "...... fatal : $server not reachable, is it correct" + fi + cleanup_cred + rm -f "$ERRORCODE_FILE" 2> /dev/null || true + continue + fi + echo "...... $server is confirmed reachable" + else + bashio::log.warning "...... nmap not available; skipping SMB port reachability test" + fi + + if command -v smbclient > /dev/null 2>&1; then + OUTPUT="$(smbclient -t 2 -L "$server" -U "$USERNAME%$PASSWORD" -c "exit" $DOMAINCLIENT 2>&1 || true)" + + if echo "$OUTPUT" | grep -q "LOGON_FAILURE"; then + bashio::log.fatal "...... incorrect Username, Password, or Domain! Script will stop." + cleanup_cred + rm -f "$ERRORCODE_FILE" 2> /dev/null || true + bashio::addon.stop + elif echo "$OUTPUT" | grep -q "tree connect failed" || echo "$OUTPUT" | grep -q "NT_STATUS_CONNECTION_DISCONNECTED"; then + echo "... using SMBv1" + bashio::log.warning "...... share reachable only with legacy SMBv1 (NT1) negotiation. Forcing SMBv1 options." + SMBVERS_FORCE=",vers=1.0" + SECVERS_FORCE=",sec=ntlm" + elif ! echo "$OUTPUT" | grep -q "Disk"; then + bashio::log.fatal "...... no shares found. Invalid or inaccessible SMB path?" + else + echo "...... credentials are valid" + fi + else + bashio::log.warning "...... smbclient not available; skipping SMB credential test" + fi + + SMBRAW="" + SMB1_DETECTED=false + if command -v nmap > /dev/null 2>&1; then + NMAP_OUTPUT="$(nmap --script smb-protocols -p 445 "$server" 2> /dev/null || true)" + SMBRAW="$( + echo "$NMAP_OUTPUT" \ + | awk '/SMB2_DIALECT_/ {print $NF}' \ + | sed 's/SMB2_DIALECT_//' \ + | tr -d '_' \ + | sort -V | tail -n 1 || true + )" + if [[ -z "$SMBRAW" ]] && echo "$NMAP_OUTPUT" | grep -Eiq 'NT LM 0\.12|SMBv1|NT1'; then + SMB1_DETECTED=true + fi + fi + + SMBVERS="" + case "$SMBRAW" in + 311) SMBVERS=",vers=3.1.1" ;; + 302) SMBVERS=",vers=3.02" ;; + 300) SMBVERS=",vers=3.0" ;; + 210) SMBVERS=",vers=2.1" ;; + 202 | 200) SMBVERS=",vers=2.0" ;; + *) SMBVERS="" ;; + esac + + if [[ -z "$SMBVERS" && "$SMB1_DETECTED" == "true" ]]; then + echo "...... SMB version detected via nmap : SMBv1 (NT LM 0.12)" + SMBVERS=",vers=1.0" + SECVERS=",sec=ntlm" + fi + + if [[ -n "$SMBVERS" ]]; then + echo "...... SMB version detected : ${SMBVERS#,vers=}" + elif command -v smbclient > /dev/null 2>&1 && smbclient -t 2 -L "$server" -m NT1 -U "$USERNAME%$PASSWORD" $DOMAINCLIENT -c "exit" &> /dev/null; then + echo "...... SMB version : only SMBv1 is supported, this can lead to issues" + SECVERS=",sec=ntlm" + SMBVERS=",vers=1.0" + else + echo "...... SMB version : couldn't detect, falling back to SMB3->SMB2->SMB1 ladder on negotiation/dialect failure" + SMBVERS=",vers=3.1.1" + fi + + if [[ -n "$SMBVERS_FORCE" ]]; then + if [[ -n "$SMBVERS" && "$SMBVERS" != "$SMBVERS_FORCE" ]]; then + bashio::log.warning "...... overriding detected SMB version ${SMBVERS#,vers=} with forced ${SMBVERS_FORCE#,vers=} (server requires legacy protocol)" + fi + SMBVERS="$SMBVERS_FORCE" + [[ -z "$SECVERS" ]] && SECVERS="$SECVERS_FORCE" + fi + + SECVERS_BASE="$SECVERS" + for SECTRY in "$SECVERS_BASE" ",sec=ntlmv2" ",sec=ntlmssp" ",sec=ntlmsspi" ",sec=krb5i" ",sec=krb5" ",sec=ntlm" ",sec=ntlmv2i"; do + if [[ "$MOUNTED" == "false" ]]; then + mount_drive "rw,file_mode=0775,dir_mode=0775,credentials=${CRED_FILE},nobrl,mfsymlinks${SMBVERS}${SECTRY}${PUID}${PGID}${CHARSET}" + fi + done + + if [[ "$MOUNTED" == "false" ]]; then + retry_cifs_with_vers_ladder_on_dialect_failure + fi + + else + if command -v nmap > /dev/null 2>&1; then + output="$(nmap -F "$server" -T5 -oG - 2> /dev/null || true)" + if ! echo "$output" | grep -Eq '(2049|111)/open'; then + bashio::log.fatal "...... $server is reachable but NFS ports not open" + fi + else + bashio::log.warning "...... nmap not available; skipping NFS port reachability test" + fi + + for NFVER in 4.2 4.1 4 3; do + if [[ "$MOUNTED" == "false" ]]; then + mount_drive "rw,nfsvers=${NFVER},proto=tcp" + fi + done + fi fi - else - if command -v nmap >/dev/null 2>&1; then - output="$(nmap -F "$server" -T5 -oG - 2>/dev/null || true)" - if ! echo "$output" | grep -Eq '(2049|111)/open'; then - bashio::log.fatal "...... $server is reachable but NFS ports not open" - fi + if [[ "$MOUNTED" == "true" ]]; then + bashio::log.info "...... $disk successfully mounted to /mnt/$diskname with options ${MOUNTOPTIONS/$PASSWORD/XXXXXXXXXX}" + rm -f "$ERRORCODE_FILE" 2> /dev/null || true + + if [[ "$FSTYPE" == "cifs" && "$MOUNTOPTIONS" == *"vers=1.0"* ]]; then + bashio::log.warning "" + bashio::log.warning "Your SMB system requires SMBv1. This is an obsolete protocol. Please correct this to prevent issues." + bashio::log.warning "" + fi + + cleanup_cred + + elif [[ "$MOUNTED" == "readonly" ]]; then + bashio::log.warning "...... $disk mounted to /mnt/$diskname but is READ-ONLY or not writable by UID/GID ${PUID#,uid=}:${PGID#,gid=}." + bashio::log.warning "...... Check Samba share permissions, or try setting PUID/PGID to 0/0 (root), or adjust server ACLs." + rm -f "$ERRORCODE_FILE" 2> /dev/null || true + cleanup_cred + else - bashio::log.warning "...... nmap not available; skipping NFS port reachability test" + if [[ "$FSTYPE" == "cifs" ]]; then + bashio::log.fatal "Error, unable to mount $disk to /mnt/$diskname with username $USERNAME. Please check share path, username/password/domain; try UID/GID 0." + bashio::log.fatal "Here is some debugging info :" + if command -v smbclient > /dev/null 2>&1; then + smbclient -t 2 -L "$server" -U "$USERNAME%$PASSWORD" -c "exit" $DOMAINCLIENT || true + else + bashio::log.warning "smbclient not available; cannot print SMB debugging info" + fi + + mount_drive "rw,credentials=${CRED_FILE}${PUID}${PGID}" + if [[ "$MOUNTED" == "false" ]]; then + retry_cifs_with_vers_ladder_on_dialect_failure + fi + else + bashio::log.fatal "Error, unable to mount NFS share $disk to /mnt/$diskname. Please check export path and allowlist for this client." + mount_drive "rw" + fi + + ERR_READ="$(cat "$ERRORCODE_FILE" 2> /dev/null || true)" + bashio::log.fatal "Error read : ${ERR_READ:-unknown error}, addon will stop in 1 min" + + umount "/mnt/$diskname" 2> /dev/null || true + rmdir "/mnt/$diskname" 2> /dev/null || true + cleanup_cred + rm -f "$ERRORCODE_FILE" 2> /dev/null || true + + bashio::addon.stop fi - - for NFVER in 4.2 4.1 4 3; do - if [[ "$MOUNTED" == "false" ]]; then - mount_drive "rw,nfsvers=${NFVER},proto=tcp" - fi - done - fi - fi - - if [[ "$MOUNTED" == "true" ]]; then - bashio::log.info "...... $disk successfully mounted to /mnt/$diskname with options ${MOUNTOPTIONS/$PASSWORD/XXXXXXXXXX}" - rm -f "$ERRORCODE_FILE" 2>/dev/null || true - - if [[ "$FSTYPE" == "cifs" && "$MOUNTOPTIONS" == *"vers=1.0"* ]]; then - bashio::log.warning "" - bashio::log.warning "Your SMB system requires SMBv1. This is an obsolete protocol. Please correct this to prevent issues." - bashio::log.warning "" - fi - - cleanup_cred - - elif [[ "$MOUNTED" == "readonly" ]]; then - bashio::log.warning "...... $disk mounted to /mnt/$diskname but is READ-ONLY or not writable by UID/GID ${PUID#,uid=}:${PGID#,gid=}." - bashio::log.warning "...... Check Samba share permissions, or try setting PUID/PGID to 0/0 (root), or adjust server ACLs." - rm -f "$ERRORCODE_FILE" 2>/dev/null || true - cleanup_cred - - else - if [[ "$FSTYPE" == "cifs" ]]; then - bashio::log.fatal "Error, unable to mount $disk to /mnt/$diskname with username $USERNAME. Please check share path, username/password/domain; try UID/GID 0." - bashio::log.fatal "Here is some debugging info :" - if command -v smbclient >/dev/null 2>&1; then - smbclient -t 2 -L "$server" -U "$USERNAME%$PASSWORD" -c "exit" $DOMAINCLIENT || true - else - bashio::log.warning "smbclient not available; cannot print SMB debugging info" - fi - - mount_drive "rw,credentials=${CRED_FILE}${PUID}${PGID}" - if [[ "$MOUNTED" == "false" ]]; then - retry_cifs_with_vers_ladder_on_dialect_failure - fi - else - bashio::log.fatal "Error, unable to mount NFS share $disk to /mnt/$diskname. Please check export path and allowlist for this client." - mount_drive "rw" - fi - - ERR_READ="$(cat "$ERRORCODE_FILE" 2>/dev/null || true)" - bashio::log.fatal "Error read : ${ERR_READ:-unknown error}, addon will stop in 1 min" - - umount "/mnt/$diskname" 2>/dev/null || true - rmdir "/mnt/$diskname" 2>/dev/null || true - cleanup_cred - rm -f "$ERRORCODE_FILE" 2>/dev/null || true - - bashio::addon.stop - fi - done + done fi diff --git a/.templates/01-config_yaml.sh b/.templates/01-config_yaml.sh index e7973e3b2a..7fdc07ec63 100755 --- a/.templates/01-config_yaml.sh +++ b/.templates/01-config_yaml.sh @@ -138,9 +138,9 @@ append_unique_line() { # $1=file, $2=line local _file="$1" local _line="$2" - mkdir -p "$(dirname "$_file")" 2>/dev/null || true - touch "$_file" 2>/dev/null || true - grep -qxF -- "$_line" "$_file" 2>/dev/null || echo "$_line" >> "$_file" + mkdir -p "$(dirname "$_file")" 2> /dev/null || true + touch "$_file" 2> /dev/null || true + grep -qxF -- "$_line" "$_file" 2> /dev/null || echo "$_line" >> "$_file" } while IFS= read -r line; do diff --git a/.templates/01-custom_script.sh b/.templates/01-custom_script.sh index 84d7c7e779..bd260b2230 100755 --- a/.templates/01-custom_script.sh +++ b/.templates/01-custom_script.sh @@ -7,27 +7,33 @@ set -e ########################################## pick_exec_dir() { - local d - for d in /dev/shm /run /var/run /mnt /root /; do - if [ -d "$d" ] && [ -w "$d" ]; then - local t="${d%/}/.exec_test_$$" - printf '#!/bin/sh\necho ok\n' >"$t" 2>/dev/null || { rm -f "$t" 2>/dev/null || true; continue; } - chmod 700 "$t" 2>/dev/null || { rm -f "$t" 2>/dev/null || true; continue; } - if "$t" >/dev/null 2>&1; then - rm -f "$t" 2>/dev/null || true - echo "$d" - return 0 - fi - rm -f "$t" 2>/dev/null || true - fi - done - return 1 + local d + for d in /dev/shm /run /var/run /mnt /root /; do + if [ -d "$d" ] && [ -w "$d" ]; then + local t="${d%/}/.exec_test_$$" + printf '#!/bin/sh\necho ok\n' > "$t" 2> /dev/null || { + rm -f "$t" 2> /dev/null || true + continue + } + chmod 700 "$t" 2> /dev/null || { + rm -f "$t" 2> /dev/null || true + continue + } + if "$t" > /dev/null 2>&1; then + rm -f "$t" 2> /dev/null || true + echo "$d" + return 0 + fi + rm -f "$t" 2> /dev/null || true + fi + done + return 1 } EXEC_DIR="$(pick_exec_dir || true)" if [ -z "${EXEC_DIR:-}" ]; then - echo "ERROR: Could not find an exec-capable writable directory." - exit 1 + echo "ERROR: Could not find an exec-capable writable directory." + exit 1 fi ###################### @@ -35,14 +41,14 @@ fi ###################### candidate_shebangs=( - "/command/with-contenv bashio" - "/usr/bin/with-contenv bashio" - "/usr/bin/env bashio" - "/usr/bin/bashio" - "/usr/bin/bash" - "/bin/bash" - "/usr/bin/sh" - "/bin/sh" + "/command/with-contenv bashio" + "/usr/bin/with-contenv bashio" + "/usr/bin/env bashio" + "/usr/bin/bashio" + "/usr/bin/bash" + "/bin/bash" + "/usr/bin/sh" + "/bin/sh" ) SHEBANG_ERRORS=() @@ -85,76 +91,76 @@ echo "${_bv:-PROBE_OK}" ' validate_shebang() { - local candidate="$1" - local tmp out rc - local errfile msg + local candidate="$1" + local tmp out rc + local errfile msg - # shellcheck disable=SC2206 - local cmd=( $candidate ) - local exe="${cmd[0]}" + # shellcheck disable=SC2206 + local cmd=($candidate) + local exe="${cmd[0]}" - if [ ! -x "$exe" ]; then - SHEBANG_ERRORS+=(" - FAIL (not executable): #!$candidate") + if [ ! -x "$exe" ]; then + SHEBANG_ERRORS+=(" - FAIL (not executable): #!$candidate") + return 1 + fi + + tmp="${EXEC_DIR%/}/shebang_test.$$.$RANDOM" + errfile="${EXEC_DIR%/}/shebang_probe_err.$$" + { + printf '#!%s\n' "$candidate" + printf '%s\n' "$probe_script_content" + } > "$tmp" + chmod 700 "$tmp" 2> /dev/null || true + + set +e + out="$("$tmp" 2> "$errfile")" + rc=$? + set -e + + rm -f "$tmp" 2> /dev/null || true + + if [ "$rc" -eq 0 ] && [ -n "${out:-}" ] && [ "$out" != "null" ]; then + rm -f "$errfile" 2> /dev/null || true + return 0 + fi + + msg=$' - FAIL: #!'"$candidate"$'\n'" rc=$rc, stdout='${out:-}'"$'\n' + if [ -s "$errfile" ]; then + msg+=$' stderr:\n' + msg+="$(sed -n '1,8p' "$errfile")"$'\n' + else + msg+=$' stderr: \n' + fi + SHEBANG_ERRORS+=("$msg") + rm -f "$errfile" 2> /dev/null || true return 1 - fi - - tmp="${EXEC_DIR%/}/shebang_test.$$.$RANDOM" - errfile="${EXEC_DIR%/}/shebang_probe_err.$$" - { - printf '#!%s\n' "$candidate" - printf '%s\n' "$probe_script_content" - } >"$tmp" - chmod 700 "$tmp" 2>/dev/null || true - - set +e - out="$("$tmp" 2>"$errfile")" - rc=$? - set -e - - rm -f "$tmp" 2>/dev/null || true - - if [ "$rc" -eq 0 ] && [ -n "${out:-}" ] && [ "$out" != "null" ]; then - rm -f "$errfile" 2>/dev/null || true - return 0 - fi - - msg=$' - FAIL: #!'"$candidate"$'\n'" rc=$rc, stdout='${out:-}'"$'\n' - if [ -s "$errfile" ]; then - msg+=$' stderr:\n' - msg+="$(sed -n '1,8p' "$errfile")"$'\n' - else - msg+=$' stderr: \n' - fi - SHEBANG_ERRORS+=("$msg") - rm -f "$errfile" 2>/dev/null || true - return 1 } shebang="" for candidate in "${candidate_shebangs[@]}"; do - if validate_shebang "$candidate"; then - shebang="$candidate" - break - fi + if validate_shebang "$candidate"; then + shebang="$candidate" + break + fi done if [ -z "$shebang" ]; then - echo "ERROR: No valid shebang found." >&2 - printf ' - %s\n' "${candidate_shebangs[@]}" >&2 - if [ "${#SHEBANG_ERRORS[@]}" -gt 0 ]; then - printf '%s\n' "${SHEBANG_ERRORS[@]}" >&2 - fi - exit 1 + echo "ERROR: No valid shebang found." >&2 + printf ' - %s\n' "${candidate_shebangs[@]}" >&2 + if [ "${#SHEBANG_ERRORS[@]}" -gt 0 ]; then + printf '%s\n' "${SHEBANG_ERRORS[@]}" >&2 + fi + exit 1 fi -if ! command -v bashio::addon.version >/dev/null 2>&1; then - for f in /usr/lib/bashio/bashio.sh /usr/lib/bashio/lib.sh /usr/src/bashio/bashio.sh /usr/local/lib/bashio/bashio.sh /usr/local/lib/bashio-standalone.sh; do - if [ -f "$f" ]; then - # shellcheck disable=SC1090 - . "$f" - break - fi - done +if ! command -v bashio::addon.version > /dev/null 2>&1; then + for f in /usr/lib/bashio/bashio.sh /usr/lib/bashio/lib.sh /usr/src/bashio/bashio.sh /usr/local/lib/bashio/bashio.sh /usr/local/lib/bashio-standalone.sh; do + if [ -f "$f" ]; then + # shellcheck disable=SC1090 + . "$f" + break + fi + done fi ################## @@ -162,9 +168,9 @@ fi ################## # Exit if /config is not mounted or HA not used -if [ ! -d /config ] || ! bashio::supervisor.ping 2>/dev/null; then - echo "..." - exit 0 +if [ ! -d /config ] || ! bashio::supervisor.ping 2> /dev/null; then + echo "..." + exit 0 fi # Define slug @@ -173,11 +179,11 @@ slug="${slug#*_}" # Check type of config folder if [ ! -f /config/configuration.yaml ] && [ ! -f /config/configuration.json ]; then - CONFIGLOCATION="/config" - CONFIGFILEBROWSER="/app_configs/${HOSTNAME/-/_}/$slug.sh" + CONFIGLOCATION="/config" + CONFIGFILEBROWSER="/app_configs/${HOSTNAME/-/_}/$slug.sh" else - CONFIGLOCATION="/config/addons_autoscripts" - CONFIGFILEBROWSER="/homeassistant/addons_autoscripts/$slug.sh" + CONFIGLOCATION="/config/addons_autoscripts" + CONFIGFILEBROWSER="/homeassistant/addons_autoscripts/$slug.sh" fi # Default location @@ -189,28 +195,28 @@ bashio::log.green "Execute $CONFIGFILEBROWSER if existing" # Download template if no script found and exit if [ ! -f "$CONFIGSOURCE" ]; then - TEMPLATESOURCE="https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/script.template" - curl -f -L -s -S "$TEMPLATESOURCE" --output "$CONFIGSOURCE" || true - exit 0 + TEMPLATESOURCE="https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/script.template" + curl -f -L -s -S "$TEMPLATESOURCE" --output "$CONFIGSOURCE" || true + exit 0 fi # Convert scripts to linux -dos2unix "$CONFIGSOURCE" >/dev/null 2>&1 || true +dos2unix "$CONFIGSOURCE" > /dev/null 2>&1 || true chmod +x "$CONFIGSOURCE" if grep -q 'bashio' "$CONFIGSOURCE"; then - sed -i "1s|^.*|#!$shebang|" "$CONFIGSOURCE" + sed -i "1s|^.*|#!$shebang|" "$CONFIGSOURCE" else - sed -i '1s|^.*|#!/bin/bash|' "$CONFIGSOURCE" + sed -i '1s|^.*|#!/bin/bash|' "$CONFIGSOURCE" fi # Check if there is actual commands while IFS= read -r line; do - line="$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + line="$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - if [[ -n "$line" ]] && [[ ! "$line" =~ ^# ]]; then - bashio::log.green "... script found, executing" - "$CONFIGSOURCE" - break - fi + if [[ -n "$line" ]] && [[ ! "$line" =~ ^# ]]; then + bashio::log.green "... script found, executing" + "$CONFIGSOURCE" + break + fi done < "$CONFIGSOURCE" diff --git a/.templates/90-dns_set.sh b/.templates/90-dns_set.sh index e75afe87f4..6ae50eb459 100755 --- a/.templates/90-dns_set.sh +++ b/.templates/90-dns_set.sh @@ -9,12 +9,12 @@ set -e # Avoid usage of local dns such as adguard home or pihole\n" if bashio::config.has_value 'DNS_server'; then - if ! bashio::config.true "openvpn_enabled" && ! bashio::config.true "wireguard_enabled"; then + if ! bashio::config.true "openvpn_enabled" && ! bashio::config.true "wireguard_enabled"; then # Define variables DNSSERVER=$(bashio::config 'DNS_server') DNS="" DNSLIST="" - + # Get DNS servers # shellcheck disable=SC2086 for server in ${DNSSERVER//,/ }; do # Separate comma separated values @@ -26,7 +26,7 @@ if bashio::config.has_value 'DNS_server'; then bashio::log.warning "DNS $server was requested but can't be pinged. It won't be used" fi done - + # Only add DNS if there are DNS set # shellcheck disable=SC2236 if [[ -n "${DNS:-}" ]]; then diff --git a/.templates/bashio-standalone.sh b/.templates/bashio-standalone.sh index 5489976a98..b9da4f1c73 100755 --- a/.templates/bashio-standalone.sh +++ b/.templates/bashio-standalone.sh @@ -33,293 +33,297 @@ _BASHIO_COLOR=1 [ "${TERM:-}" = "dumb" ] && _BASHIO_COLOR=0 _bashio_color() { - [ "$_BASHIO_COLOR" = "1" ] || return 0 - case "${1:-}" in - blue) printf '\033[34m' ;; - green) printf '\033[32m' ;; - yellow) printf '\033[33m' ;; - red) printf '\033[31m' ;; - magenta) printf '\033[35m' ;; - reset) printf '\033[0m' ;; - *) printf '' ;; - esac + [ "$_BASHIO_COLOR" = "1" ] || return 0 + case "${1:-}" in + blue) printf '\033[34m' ;; + green) printf '\033[32m' ;; + yellow) printf '\033[33m' ;; + red) printf '\033[31m' ;; + magenta) printf '\033[35m' ;; + reset) printf '\033[0m' ;; + *) printf '' ;; + esac } _bashio_log() { - local c="${1:-}"; shift || true - printf '%s%s%s\n' "$(_bashio_color "$c")" "$*" "$(_bashio_color reset)" + local c="${1:-}" + shift || true + printf '%s%s%s\n' "$(_bashio_color "$c")" "$*" "$(_bashio_color reset)" } # ----------------------------------------------------------------------------- # Helpers # ----------------------------------------------------------------------------- _bashio_is_true() { - case "${1:-}" in - 1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;; - *) return 1 ;; - esac + case "${1:-}" in + 1 | true | TRUE | True | yes | YES | Yes | on | ON | On) return 0 ;; + *) return 1 ;; + esac } # ENV mapping helper: # tries variants + prefixes and prints the value if env var is defined (even empty), # returning 0 when found, 1 when not found. _bashio_env_get() { - local key="${1:-}" - [ -n "$key" ] || return 1 + local key="${1:-}" + [ -n "$key" ] || return 1 - local norm norm_uc raw_uc - norm="$(printf '%s' "$key" | tr '.-' '__')" - norm_uc="$(printf '%s' "$norm" | tr '[:lower:]' '[:upper:]')" - raw_uc="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')" + local norm norm_uc raw_uc + norm="$(printf '%s' "$key" | tr '.-' '__')" + norm_uc="$(printf '%s' "$norm" | tr '[:lower:]' '[:upper:]')" + raw_uc="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')" - local variants=( - "$key" - "$raw_uc" - "$norm" - "$norm_uc" - ) + local variants=( + "$key" + "$raw_uc" + "$norm" + "$norm_uc" + ) - local prefixes=("" "CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_") + local prefixes=("" "CFG_" "CONFIG_" "ADDON_" "OPTION_" "OPT_") - local v p name - for v in "${variants[@]}"; do - for p in "${prefixes[@]}"; do - name="${p}${v}" - if [ -n "${!name+x}" ]; then - printf '%s' "${!name}" - return 0 - fi + local v p name + for v in "${variants[@]}"; do + for p in "${prefixes[@]}"; do + name="${p}${v}" + if [ -n "${!name+x}" ]; then + printf '%s' "${!name}" + return 0 + fi + done done - done - return 1 + return 1 } # env presence (even if empty) used by config.exists _bashio_env_has() { - local key="${1:-}" - [ -n "$key" ] || return 1 - _bashio_env_get "$key" >/dev/null 2>&1 + local key="${1:-}" + [ -n "$key" ] || return 1 + _bashio_env_get "$key" > /dev/null 2>&1 } # JSON options source (jq required). Prints value or empty; returns 0 always. _bashio_json_get() { - local key="${1:-}" - local file="${STANDALONE_OPTIONS_JSON:-}" + local key="${1:-}" + local file="${STANDALONE_OPTIONS_JSON:-}" - [ -n "$key" ] || return 0 - [ -n "$file" ] || return 0 - [ -f "$file" ] || return 0 - command -v jq >/dev/null 2>&1 || return 0 + [ -n "$key" ] || return 0 + [ -n "$file" ] || return 0 + [ -f "$file" ] || return 0 + command -v jq > /dev/null 2>&1 || return 0 - # getpath(split(".")) supports nested access; missing => empty - jq -er --arg k "$key" 'getpath(($k|split("."))) // empty' "$file" 2>/dev/null || true + # getpath(split(".")) supports nested access; missing => empty + jq -er --arg k "$key" 'getpath(($k|split("."))) // empty' "$file" 2> /dev/null || true } # Net wait using /dev/tcp with a timeout _bashio_tcp_wait() { - local host="${1:-}" port="${2:-}" to="${3:-30}" - [ -n "$host" ] && [ -n "$port" ] || return 1 + local host="${1:-}" port="${2:-}" to="${3:-30}" + [ -n "$host" ] && [ -n "$port" ] || return 1 - local start now - start="$(date +%s)" - while :; do - if exec 3<>"/dev/tcp/${host}/${port}" 2>/dev/null; then - exec 3>&- 3<&- - return 0 - fi - now="$(date +%s)" - if [ $((now - start)) -ge "$to" ]; then - return 1 - fi - sleep 1 - done + local start now + start="$(date +%s)" + while :; do + if exec 3<> "/dev/tcp/${host}/${port}" 2> /dev/null; then + exec 3>&- 3<&- + return 0 + fi + now="$(date +%s)" + if [ $((now - start)) -ge "$to" ]; then + return 1 + fi + sleep 1 + done } # Prefer nc if present, fallback to /dev/tcp _bashio_tcp_wait_nc() { - command -v nc >/dev/null 2>&1 || return 1 - local host="${1:-}" port="${2:-}" to="${3:-30}" - # BusyBox and OpenBSD nc differ; cover both styles - nc -z -w "$to" "$host" "$port" 2>/dev/null || nc -z "$host" "$port" 2>/dev/null + command -v nc > /dev/null 2>&1 || return 1 + local host="${1:-}" port="${2:-}" to="${3:-30}" + # BusyBox and OpenBSD nc differ; cover both styles + nc -z -w "$to" "$host" "$port" 2> /dev/null || nc -z "$host" "$port" 2> /dev/null } # ----------------------------------------------------------------------------- # Logging API # ----------------------------------------------------------------------------- -bashio::log.blue() { _bashio_log blue "$*"; } -bashio::log.green() { _bashio_log green "$*"; } -bashio::log.yellow() { _bashio_log yellow "$*"; } -bashio::log.red() { _bashio_log red "$*"; } +bashio::log.blue() { _bashio_log blue "$*"; } +bashio::log.green() { _bashio_log green "$*"; } +bashio::log.yellow() { _bashio_log yellow "$*"; } +bashio::log.red() { _bashio_log red "$*"; } bashio::log.magenta() { _bashio_log magenta "$*"; } # Common aliases -bashio::log.info() { bashio::log.blue "$@"; } +bashio::log.info() { bashio::log.blue "$@"; } bashio::log.warning() { bashio::log.yellow "$@"; } -bashio::log.error() { bashio::log.red "$@"; } -bashio::log.debug() { printf '%s\n' "$*"; } +bashio::log.error() { bashio::log.red "$@"; } +bashio::log.debug() { printf '%s\n' "$*"; } # ----------------------------------------------------------------------------- # Supervisor shim # ----------------------------------------------------------------------------- bashio::supervisor.ping() { - _bashio_is_true "${STANDALONE_FORCE_SUPERVISOR_PING:-}" && return 0 - return 1 + _bashio_is_true "${STANDALONE_FORCE_SUPERVISOR_PING:-}" && return 0 + return 1 } # ----------------------------------------------------------------------------- # Add-on metadata # ----------------------------------------------------------------------------- -bashio::addon.name() { printf '%s' "${ADDON_NAME:-Standalone container}"; } -bashio::addon.description() { printf '%s' "${ADDON_DESCRIPTION:-Running without Home Assistant Supervisor}"; } -bashio::addon.version() { printf '%s' "${BUILD_VERSION:-1.0}"; } -bashio::addon.version_latest(){ printf '%s' "${ADDON_VERSION_LATEST:-${BUILD_VERSION:-1.0}}"; } +bashio::addon.name() { printf '%s' "${ADDON_NAME:-Standalone container}"; } +bashio::addon.description() { printf '%s' "${ADDON_DESCRIPTION:-Running without Home Assistant Supervisor}"; } +bashio::addon.version() { printf '%s' "${BUILD_VERSION:-1.0}"; } +bashio::addon.version_latest() { printf '%s' "${ADDON_VERSION_LATEST:-${BUILD_VERSION:-1.0}}"; } bashio::addon.update_available() { - if [ -n "${ADDON_VERSION_LATEST:-}" ] && [ "${ADDON_VERSION_LATEST:-}" != "${BUILD_VERSION:-}" ]; then - printf '%s' "true" - else - printf '%s' "false" - fi + if [ -n "${ADDON_VERSION_LATEST:-}" ] && [ "${ADDON_VERSION_LATEST:-}" != "${BUILD_VERSION:-}" ]; then + printf '%s' "true" + else + printf '%s' "false" + fi } -bashio::addon.ingress_port() { printf '%s' "${ADDON_INGRESS_PORT:-}"; } +bashio::addon.ingress_port() { printf '%s' "${ADDON_INGRESS_PORT:-}"; } bashio::addon.ingress_entry() { printf '%s' "${ADDON_INGRESS_ENTRY:-}"; } -bashio::addon.ip_address() { printf '%s' "${ADDON_IP_ADDRESS:-}"; } +bashio::addon.ip_address() { printf '%s' "${ADDON_IP_ADDRESS:-}"; } # Ports: # - numeric arg "8080" -> env PORT_8080 or ADDON_PORT_8080, fallback to the number # - non-numeric "WEB_PORT" -> resolve as config/env key bashio::addon.port() { - local arg="${1:-}" - if [[ "$arg" =~ ^[0-9]+$ ]]; then - local v="" - v="$(_bashio_env_get "PORT_${arg}" 2>/dev/null || true)" - [ -z "$v" ] && v="$(_bashio_env_get "ADDON_PORT_${arg}" 2>/dev/null || true)" - printf '%s' "${v:-$arg}" - else - printf '%s' "$(_bashio_env_get "$arg" 2>/dev/null || true)" - fi + local arg="${1:-}" + if [[ "$arg" =~ ^[0-9]+$ ]]; then + local v="" + v="$(_bashio_env_get "PORT_${arg}" 2> /dev/null || true)" + [ -z "$v" ] && v="$(_bashio_env_get "ADDON_PORT_${arg}" 2> /dev/null || true)" + printf '%s' "${v:-$arg}" + else + printf '%s' "$(_bashio_env_get "$arg" 2> /dev/null || true)" + fi } # addon.option : write/delete option in JSON when possible; fallback export env bashio::addon.option() { - local key="${1:-}" value="${2-__BASHIO_UNSET__}" file="${STANDALONE_OPTIONS_JSON:-}" - [ -n "$key" ] || return 0 + local key="${1:-}" value="${2-__BASHIO_UNSET__}" file="${STANDALONE_OPTIONS_JSON:-}" + [ -n "$key" ] || return 0 - if [ -n "$file" ] && [ -f "$file" ] && command -v jq >/dev/null 2>&1; then - local tmp - tmp="$(mktemp)" - if [ "$value" = "__BASHIO_UNSET__" ]; then - jq --arg k "$key" 'delpath(($k|split(".")))' "$file" >"$tmp" && mv "$tmp" "$file" - else - jq --arg k "$key" --arg v "$value" 'setpath(($k|split(".")); $v)' "$file" >"$tmp" && mv "$tmp" "$file" + if [ -n "$file" ] && [ -f "$file" ] && command -v jq > /dev/null 2>&1; then + local tmp + tmp="$(mktemp)" + if [ "$value" = "__BASHIO_UNSET__" ]; then + jq --arg k "$key" 'delpath(($k|split(".")))' "$file" > "$tmp" && mv "$tmp" "$file" + else + jq --arg k "$key" --arg v "$value" 'setpath(($k|split(".")); $v)' "$file" > "$tmp" && mv "$tmp" "$file" + fi + return 0 fi - return 0 - fi - # Fallback: export as env (dot/dash -> underscore). Delete becomes no-op. - if [ "$value" != "__BASHIO_UNSET__" ]; then - export "$(printf '%s' "$key" | tr '.-' '__')"="$value" - fi + # Fallback: export as env (dot/dash -> underscore). Delete becomes no-op. + if [ "$value" != "__BASHIO_UNSET__" ]; then + export "$(printf '%s' "$key" | tr '.-' '__')"="$value" + fi } # ----------------------------------------------------------------------------- # System info # ----------------------------------------------------------------------------- bashio::info.operating_system() { - if [ -r /etc/os-release ]; then - # shellcheck disable=SC1091 - . /etc/os-release - printf '%s' "${PRETTY_NAME:-${NAME:-Linux}}" - else - printf '%s' "Linux" - fi + if [ -r /etc/os-release ]; then + # shellcheck disable=SC1091 + . /etc/os-release + printf '%s' "${PRETTY_NAME:-${NAME:-Linux}}" + else + printf '%s' "Linux" + fi } -bashio::info.arch() { uname -m; } -bashio::info.machine() { uname -m; } -bashio::info.homeassistant(){ printf '%s' "standalone"; } -bashio::info.supervisor() { printf '%s' "standalone"; } +bashio::info.arch() { uname -m; } +bashio::info.machine() { uname -m; } +bashio::info.homeassistant() { printf '%s' "standalone"; } +bashio::info.supervisor() { printf '%s' "standalone"; } # ----------------------------------------------------------------------------- # Config API # ----------------------------------------------------------------------------- bashio::config() { - local key="${1:-}" - [ -n "$key" ] || { printf '%s' ""; return 0; } + local key="${1:-}" + [ -n "$key" ] || { + printf '%s' "" + return 0 + } - local v="" - if _bashio_env_get "$key" >/dev/null 2>&1; then - v="$(_bashio_env_get "$key" 2>/dev/null || true)" - fi - [ -z "$v" ] && v="$(_bashio_json_get "$key")" - printf '%s' "${v:-}" + local v="" + if _bashio_env_get "$key" > /dev/null 2>&1; then + v="$(_bashio_env_get "$key" 2> /dev/null || true)" + fi + [ -z "$v" ] && v="$(_bashio_json_get "$key")" + printf '%s' "${v:-}" } bashio::config.has_value() { [ -n "$(bashio::config "$1")" ]; } bashio::config.true() { - _bashio_is_true "$(bashio::config "$1")" + _bashio_is_true "$(bashio::config "$1")" } # config.exists : key is present (env or JSON), even if value is empty bashio::config.exists() { - local key="${1:-}" file="${STANDALONE_OPTIONS_JSON:-}" - [ -n "$key" ] || return 1 + local key="${1:-}" file="${STANDALONE_OPTIONS_JSON:-}" + [ -n "$key" ] || return 1 - if _bashio_env_has "$key"; then - return 0 - fi + if _bashio_env_has "$key"; then + return 0 + fi - if [ -n "$file" ] && [ -f "$file" ] && command -v jq >/dev/null 2>&1; then - jq -e --arg k "$key" 'haspath(($k|split(".")))' "$file" >/dev/null 2>&1 - return $? - fi + if [ -n "$file" ] && [ -f "$file" ] && command -v jq > /dev/null 2>&1; then + jq -e --arg k "$key" 'haspath(($k|split(".")))' "$file" > /dev/null 2>&1 + return $? + fi - return 1 + return 1 } # Common "require.*" shims (advisory/no-op in standalone) -bashio::config.require.ssl() { printf '%s' "${REQUIRE_SSL:-true}"; } +bashio::config.require.ssl() { printf '%s' "${REQUIRE_SSL:-true}"; } bashio::config.require.username() { :; } bashio::config.require.password() { :; } -bashio::config.require.port() { :; } +bashio::config.require.port() { :; } # config.array: # Accepts CSV ("a,b,c"), space/newline-separated text, or JSON array ["a","b"]. # Prints one item per line. bashio::config.array() { - local key="${1:-}" raw - raw="$(bashio::config "$key")" - [ -n "$raw" ] || return 0 + local key="${1:-}" raw + raw="$(bashio::config "$key")" + [ -n "$raw" ] || return 0 - if command -v jq >/dev/null 2>&1 && printf '%s' "$raw" | jq -e . >/dev/null 2>&1; then - printf '%s' "$raw" | jq -r '.[]' 2>/dev/null && return 0 - fi + if command -v jq > /dev/null 2>&1 && printf '%s' "$raw" | jq -e . > /dev/null 2>&1; then + printf '%s' "$raw" | jq -r '.[]' 2> /dev/null && return 0 + fi - if printf '%s' "$raw" | grep -q ','; then - printf '%s' "$raw" | tr ',' '\n' - return 0 - fi + if printf '%s' "$raw" | grep -q ','; then + printf '%s' "$raw" | tr ',' '\n' + return 0 + fi - printf '%s\n' "$raw" + printf '%s\n' "$raw" } # ----------------------------------------------------------------------------- # var helpers # ----------------------------------------------------------------------------- -bashio::var.true() { _bashio_is_true "${1:-}"; } -bashio::var.false() { ! _bashio_is_true "${1:-}"; } +bashio::var.true() { _bashio_is_true "${1:-}"; } +bashio::var.false() { ! _bashio_is_true "${1:-}"; } bashio::var.has_value() { [ -n "${1:-}" ]; } # ----------------------------------------------------------------------------- # Filesystem helpers # ----------------------------------------------------------------------------- -bashio::fs.file_exists() { [ -f "${1:-}" ]; } +bashio::fs.file_exists() { [ -f "${1:-}" ]; } bashio::fs.directory_exists() { [ -d "${1:-}" ]; } bashio::fs.file_contains() { - local f="${1:-}" p="${2:-}" - [ -f "$f" ] && grep -q -- "$p" "$f" 2>/dev/null + local f="${1:-}" p="${2:-}" + [ -f "$f" ] && grep -q -- "$p" "$f" 2> /dev/null } # ----------------------------------------------------------------------------- @@ -327,25 +331,25 @@ bashio::fs.file_contains() { # ----------------------------------------------------------------------------- # Wait for TCP service: bashio::net.wait_for host port [timeout] bashio::net.wait_for() { - local host="${1:-}" port="${2:-}" to="${3:-30}" - _bashio_tcp_wait_nc "$host" "$port" "$to" && return 0 - _bashio_tcp_wait "$host" "$port" "$to" + local host="${1:-}" port="${2:-}" to="${3:-30}" + _bashio_tcp_wait_nc "$host" "$port" "$to" && return 0 + _bashio_tcp_wait "$host" "$port" "$to" } # DNS helper: bashio::dns.host -> prints an IP (or empty) bashio::dns.host() { - local h="${1:-}" - [ -n "$h" ] || return 1 - if command -v getent >/dev/null 2>&1; then - getent ahostsv4 "$h" | awk '{print $1; exit}' - else - nslookup "$h" 2>/dev/null | awk '/^Address: /{print $2; exit}' - fi + local h="${1:-}" + [ -n "$h" ] || return 1 + if command -v getent > /dev/null 2>&1; then + getent ahostsv4 "$h" | awk '{print $1; exit}' + else + nslookup "$h" 2> /dev/null | awk '/^Address: /{print $2; exit}' + fi } # Hostname bashio::host.hostname() { - command -v hostname >/dev/null 2>&1 && hostname || printf '%s' "${HOSTNAME:-unknown}" + command -v hostname > /dev/null 2>&1 && hostname || printf '%s' "${HOSTNAME:-unknown}" } # ----------------------------------------------------------------------------- @@ -355,40 +359,43 @@ bashio::host.hostname() { # bashio::services "mqtt" "host" # bashio::services.available "mqtt" bashio::services() { - local svc="${1:-}" key="${2:-}" - [ -n "$svc" ] && [ -n "$key" ] || { printf '%s' ""; return 0; } + local svc="${1:-}" key="${2:-}" + [ -n "$svc" ] && [ -n "$key" ] || { + printf '%s' "" + return 0 + } - local upper svc_upper var v="" - upper="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')" - svc_upper="$(printf '%s' "$svc" | tr '[:lower:]' '[:upper:]')" + local upper svc_upper var v="" + upper="$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')" + svc_upper="$(printf '%s' "$svc" | tr '[:lower:]' '[:upper:]')" - # Common mappings - case "$svc_upper:$upper" in - MQTT:HOST) var="MQTT_HOST" ;; - MQTT:PORT) var="MQTT_PORT" ;; - MQTT:USERNAME) var="MQTT_USER" ;; - MQTT:PASSWORD) var="MQTT_PASSWORD" ;; - MQTT:TLS) var="MQTT_TLS" ;; - MYSQL:HOST|MARIADB:HOST) var="DB_HOST" ;; - MYSQL:PORT|MARIADB:PORT) var="DB_PORT" ;; - MYSQL:USERNAME|MARIADB:USERNAME) var="DB_USER" ;; - MYSQL:PASSWORD|MARIADB:PASSWORD) var="DB_PASSWORD" ;; - MYSQL:DATABASE|MARIADB:DATABASE) var="DB_NAME" ;; - *) var="${svc_upper}_${upper}" ;; - esac + # Common mappings + case "$svc_upper:$upper" in + MQTT:HOST) var="MQTT_HOST" ;; + MQTT:PORT) var="MQTT_PORT" ;; + MQTT:USERNAME) var="MQTT_USER" ;; + MQTT:PASSWORD) var="MQTT_PASSWORD" ;; + MQTT:TLS) var="MQTT_TLS" ;; + MYSQL:HOST | MARIADB:HOST) var="DB_HOST" ;; + MYSQL:PORT | MARIADB:PORT) var="DB_PORT" ;; + MYSQL:USERNAME | MARIADB:USERNAME) var="DB_USER" ;; + MYSQL:PASSWORD | MARIADB:PASSWORD) var="DB_PASSWORD" ;; + MYSQL:DATABASE | MARIADB:DATABASE) var="DB_NAME" ;; + *) var="${svc_upper}_${upper}" ;; + esac - v="$(_bashio_env_get "$var" 2>/dev/null || true)" - if [ -z "$v" ]; then - v="$(_bashio_json_get "services.${svc}.${key}")" - [ -z "$v" ] && v="$(_bashio_json_get "${svc}.${key}")" - fi - printf '%s' "${v:-}" + v="$(_bashio_env_get "$var" 2> /dev/null || true)" + if [ -z "$v" ]; then + v="$(_bashio_json_get "services.${svc}.${key}")" + [ -z "$v" ] && v="$(_bashio_json_get "${svc}.${key}")" + fi + printf '%s' "${v:-}" } bashio::services.available() { - local svc="${1:-}" host - host="$(bashio::services "$svc" "host")" - [ -n "$host" ] + local svc="${1:-}" host + host="$(bashio::services "$svc" "host")" + [ -n "$host" ] } # ----------------------------------------------------------------------------- @@ -396,39 +403,43 @@ bashio::services.available() { # ----------------------------------------------------------------------------- mkdir -p "$BASHIO_CACHE_DIR" bashio::cache.exists() { [ -f "$BASHIO_CACHE_DIR/${1}.cache" ]; } -bashio::cache.get() { [ -f "$BASHIO_CACHE_DIR/${1}.cache" ] && cat "$BASHIO_CACHE_DIR/${1}.cache"; } -bashio::cache.set() { printf '%s' "${2:-}" > "$BASHIO_CACHE_DIR/${1}.cache"; } +bashio::cache.get() { [ -f "$BASHIO_CACHE_DIR/${1}.cache" ] && cat "$BASHIO_CACHE_DIR/${1}.cache"; } +bashio::cache.set() { printf '%s' "${2:-}" > "$BASHIO_CACHE_DIR/${1}.cache"; } # ----------------------------------------------------------------------------- # jq wrapper (some add-ons call bashio::jq) # ----------------------------------------------------------------------------- -bashio::jq() { command -v jq >/dev/null 2>&1 && jq "$@"; } +bashio::jq() { command -v jq > /dev/null 2>&1 && jq "$@"; } # ----------------------------------------------------------------------------- # Home Assistant token # ----------------------------------------------------------------------------- bashio::homeassistant.token() { - local t="${HOMEASSISTANT_TOKEN:-${HASS_TOKEN:-}}" - if [ -z "$t" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ] && [ -f "${STANDALONE_OPTIONS_JSON:-}" ] && command -v jq >/dev/null 2>&1; then - t="$(jq -er '.homeassistant.token // empty' "$STANDALONE_OPTIONS_JSON" 2>/dev/null || true)" - fi - printf '%s' "${t:-}" + local t="${HOMEASSISTANT_TOKEN:-${HASS_TOKEN:-}}" + if [ -z "$t" ] && [ -n "${STANDALONE_OPTIONS_JSON:-}" ] && [ -f "${STANDALONE_OPTIONS_JSON:-}" ] && command -v jq > /dev/null 2>&1; then + t="$(jq -er '.homeassistant.token // empty' "$STANDALONE_OPTIONS_JSON" 2> /dev/null || true)" + fi + printf '%s' "${t:-}" } # ----------------------------------------------------------------------------- # Exit helpers # ----------------------------------------------------------------------------- -bashio::exit.ok() { exit 0; } -bashio::exit.nok() { local m="${1:-}"; [ -n "$m" ] && bashio::log.red "$m"; exit 1; } +bashio::exit.ok() { exit 0; } +bashio::exit.nok() { + local m="${1:-}" + [ -n "$m" ] && bashio::log.red "$m" + exit 1 +} # ----------------------------------------------------------------------------- # Core config check shim # ----------------------------------------------------------------------------- # Set STANDALONE_CORE_CHECK_CMD="hass --script check_config -c /config" to enable bashio::core.check() { - if [ -n "${STANDALONE_CORE_CHECK_CMD:-}" ]; then - eval "$STANDALONE_CORE_CHECK_CMD" - else - return 0 - fi + if [ -n "${STANDALONE_CORE_CHECK_CMD:-}" ]; then + eval "$STANDALONE_CORE_CHECK_CMD" + else + return 0 + fi } diff --git a/.templates/ha_autoapps.sh b/.templates/ha_autoapps.sh index 33080a310f..aa2cc772ef 100755 --- a/.templates/ha_autoapps.sh +++ b/.templates/ha_autoapps.sh @@ -41,14 +41,14 @@ restore_apt_sources_if_missing() { mirror="http://ports.ubuntu.com/ubuntu-ports" security_mirror="$mirror" fi - cat > /etc/apt/sources.list < /etc/apt/sources.list << EOF2 deb ${mirror} ${codename} main restricted universe multiverse deb ${mirror} ${codename}-updates main restricted universe multiverse deb ${security_mirror} ${codename}-security main restricted universe multiverse EOF2 ;; debian) - cat > /etc/apt/sources.list < /etc/apt/sources.list << EOF2 deb http://deb.debian.org/debian ${codename} main contrib non-free non-free-firmware deb http://deb.debian.org/debian ${codename}-updates main contrib non-free non-free-firmware deb http://deb.debian.org/debian-security ${codename}-security main contrib non-free non-free-firmware diff --git a/.templates/ha_automatic_packages.sh b/.templates/ha_automatic_packages.sh index 66d11ba73e..5fedbefe72 100755 --- a/.templates/ha_automatic_packages.sh +++ b/.templates/ha_automatic_packages.sh @@ -184,7 +184,7 @@ done if [ "$PACKMANAGER" = "apt" ]; then apt-get update > /dev/null; fi if [ "$PACKMANAGER" = "pacman" ]; then pacman -Sy > /dev/null; fi if [ "$PACKMANAGER" = "apk" ] && [ -f /etc/apk/repositories ] && ! grep -q "community" /etc/apk/repositories; then - ALPINE_VER=$(cat /etc/alpine-release 2>/dev/null | cut -d. -f1,2) + ALPINE_VER=$(cat /etc/alpine-release 2> /dev/null | cut -d. -f1,2) if [ -n "$ALPINE_VER" ]; then echo "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VER}/community" >> /etc/apk/repositories apk update > /dev/null @@ -228,7 +228,7 @@ for files in "/etc/services.d" "/etc/cont-init.d"; do if ! ls $files 1> /dev/null 2>&1; then continue; fi # Bashio - if grep -q -rnw "${files}" -e 'bashio' 2>/dev/null && [ ! -f "/usr/bin/bashio" ]; then + if grep -q -rnw "${files}" -e 'bashio' 2> /dev/null && [ ! -f "/usr/bin/bashio" ]; then [ "$VERBOSE" = true ] && echo "install bashio" BASHIO_VERSION="v0.17.5" mkdir -p /tmp/bashio diff --git a/.templates/ha_entrypoint.sh b/.templates/ha_entrypoint.sh index 0f02dd1ade..03d5f257a6 100755 --- a/.templates/ha_entrypoint.sh +++ b/.templates/ha_entrypoint.sh @@ -7,13 +7,13 @@ PID1=false if [ "$$" -eq 1 ]; then - PID1=true - echo "Starting as entrypoint" - if [ -d /command ]; then - ln -sf /command/* /usr/bin/ 2>/dev/null || true - fi + PID1=true + echo "Starting as entrypoint" + if [ -d /command ]; then + ln -sf /command/* /usr/bin/ 2> /dev/null || true + fi else - echo "Starting custom scripts" + echo "Starting custom scripts" fi ########################################## @@ -28,40 +28,40 @@ fi # mid-write by the move -- only PID 1 is signalled, and bash runs a trap at a command # boundary, so whatever external command is in flight reaps first. terminate() { - local local_pid - # Best-effort, so errexit must not apply: this runs with `set -e` in force (validate_shebang - # leaves it on), and under errexit the first failing command aborts the handler and exits with - # its status, skipping the child-kill loop and the `exit 0` below. Every command in the body - # here is already guarded, but add-ons patch this function at build time -- postgres_15 and - # postgres_17 sed an unguarded `pg_ctl ... stop` in right after the echo -- and that command - # fails whenever the stop arrives before the database is up. - set +e - echo "Termination signal received, forwarding to subprocesses..." - if command -v pgrep >/dev/null 2>&1; then - while read -r pid; do - [ -n "$pid" ] || continue - echo "Terminating child PID $pid" - kill -TERM "$pid" 2>/dev/null || echo "Failed to terminate PID $pid" - done < <(pgrep -P "$$" || true) - else - for p in /proc/[0-9]*/; do - local_pid="${p#/proc/}" - local_pid="${local_pid%/}" - if [ "$local_pid" -ne 1 ] && grep -q "^PPid:[[:space:]]*$$" "/proc/$local_pid/status" 2>/dev/null; then - echo "Terminating child PID $local_pid" - kill -TERM "$local_pid" 2>/dev/null || echo "Failed to terminate PID $local_pid" - fi - done - fi - wait || true - echo "All subprocesses terminated. Exiting." - exit 0 + local local_pid + # Best-effort, so errexit must not apply: this runs with `set -e` in force (validate_shebang + # leaves it on), and under errexit the first failing command aborts the handler and exits with + # its status, skipping the child-kill loop and the `exit 0` below. Every command in the body + # here is already guarded, but add-ons patch this function at build time -- postgres_15 and + # postgres_17 sed an unguarded `pg_ctl ... stop` in right after the echo -- and that command + # fails whenever the stop arrives before the database is up. + set +e + echo "Termination signal received, forwarding to subprocesses..." + if command -v pgrep > /dev/null 2>&1; then + while read -r pid; do + [ -n "$pid" ] || continue + echo "Terminating child PID $pid" + kill -TERM "$pid" 2> /dev/null || echo "Failed to terminate PID $pid" + done < <(pgrep -P "$$" || true) + else + for p in /proc/[0-9]*/; do + local_pid="${p#/proc/}" + local_pid="${local_pid%/}" + if [ "$local_pid" -ne 1 ] && grep -q "^PPid:[[:space:]]*$$" "/proc/$local_pid/status" 2> /dev/null; then + echo "Terminating child PID $local_pid" + kill -TERM "$local_pid" 2> /dev/null || echo "Failed to terminate PID $local_pid" + fi + done + fi + wait || true + echo "All subprocesses terminated. Exiting." + exit 0 } # Only when this script is PID 1. Under Docker's own init the entrypoint is an ordinary # child and the signal handling above belongs to that init, not to us. if $PID1; then - trap terminate SIGTERM SIGINT + trap terminate SIGTERM SIGINT fi ########################################## @@ -69,31 +69,37 @@ fi ########################################## pick_exec_dir() { - # Prefer locations that are commonly exec-capable in containers - # and writable. Avoid /tmp because it may be mounted noexec. - local d - for d in /dev/shm /run /var/run /mnt /root /; do - if [ -d "$d" ] && [ -w "$d" ]; then - # Create a tiny test executable to confirm "exec" works - local t="${d%/}/.exec_test_$$" - printf '#!/bin/sh\necho ok\n' >"$t" 2>/dev/null || { rm -f "$t" 2>/dev/null || true; continue; } - chmod 700 "$t" 2>/dev/null || { rm -f "$t" 2>/dev/null || true; continue; } - if "$t" >/dev/null 2>&1; then - rm -f "$t" 2>/dev/null || true - echo "$d" - return 0 - fi - rm -f "$t" 2>/dev/null || true - fi - done - return 1 + # Prefer locations that are commonly exec-capable in containers + # and writable. Avoid /tmp because it may be mounted noexec. + local d + for d in /dev/shm /run /var/run /mnt /root /; do + if [ -d "$d" ] && [ -w "$d" ]; then + # Create a tiny test executable to confirm "exec" works + local t="${d%/}/.exec_test_$$" + printf '#!/bin/sh\necho ok\n' > "$t" 2> /dev/null || { + rm -f "$t" 2> /dev/null || true + continue + } + chmod 700 "$t" 2> /dev/null || { + rm -f "$t" 2> /dev/null || true + continue + } + if "$t" > /dev/null 2>&1; then + rm -f "$t" 2> /dev/null || true + echo "$d" + return 0 + fi + rm -f "$t" 2> /dev/null || true + fi + done + return 1 } EXEC_DIR="$(pick_exec_dir || true)" if [ -z "${EXEC_DIR:-}" ]; then - echo "ERROR: Could not find an exec-capable writable directory (e.g., /dev/shm,/run)." - echo "Your environment likely mounts all writable dirs as noexec; shebang validation cannot run safely." - exit 1 + echo "ERROR: Could not find an exec-capable writable directory (e.g., /dev/shm,/run)." + echo "Your environment likely mounts all writable dirs as noexec; shebang validation cannot run safely." + exit 1 fi ###################### @@ -101,14 +107,14 @@ fi ###################### candidate_shebangs=( - "/command/with-contenv bashio" - "/usr/bin/with-contenv bashio" - "/usr/bin/env bashio" - "/usr/bin/bashio" - "/usr/bin/bash" - "/bin/bash" - "/usr/bin/sh" - "/bin/sh" + "/command/with-contenv bashio" + "/usr/bin/with-contenv bashio" + "/usr/bin/env bashio" + "/usr/bin/bashio" + "/usr/bin/bash" + "/bin/bash" + "/usr/bin/sh" + "/bin/sh" ) SHEBANG_ERRORS=() @@ -152,68 +158,68 @@ echo "${_bv:-PROBE_OK}" ' validate_shebang() { - local candidate="$1" - local tmp out rc - local errfile msg + local candidate="$1" + local tmp out rc + local errfile msg - # shellcheck disable=SC2206 - local cmd=( $candidate ) - local exe="${cmd[0]}" + # shellcheck disable=SC2206 + local cmd=($candidate) + local exe="${cmd[0]}" - if [ ! -x "$exe" ]; then - SHEBANG_ERRORS+=(" - FAIL (not executable): #!$candidate") + if [ ! -x "$exe" ]; then + SHEBANG_ERRORS+=(" - FAIL (not executable): #!$candidate") + return 1 + fi + + tmp="${EXEC_DIR%/}/shebang_test.$$.$RANDOM" + errfile="${EXEC_DIR%/}/shebang_probe_err.$$" + { + printf '#!%s\n' "$candidate" + printf '%s\n' "$probe_script_content" + } > "$tmp" + chmod 700 "$tmp" 2> /dev/null || true + + set +e + out="$("$tmp" 2> "$errfile")" + rc=$? + set -e + + rm -f "$tmp" 2> /dev/null || true + + if [ "$rc" -eq 0 ] && [ -n "${out:-}" ] && [ "$out" != "null" ]; then + rm -f "$errfile" 2> /dev/null || true + return 0 + fi + + msg=$' - FAIL: #!'"$candidate"$'\n'" rc=$rc, stdout='${out:-}'"$'\n' + if [ -s "$errfile" ]; then + msg+=$' stderr:\n' + msg+="$(sed -n '1,8p' "$errfile")"$'\n' + else + msg+=$' stderr: \n' + fi + SHEBANG_ERRORS+=("$msg") + rm -f "$errfile" 2> /dev/null || true return 1 - fi - - tmp="${EXEC_DIR%/}/shebang_test.$$.$RANDOM" - errfile="${EXEC_DIR%/}/shebang_probe_err.$$" - { - printf '#!%s\n' "$candidate" - printf '%s\n' "$probe_script_content" - } >"$tmp" - chmod 700 "$tmp" 2>/dev/null || true - - set +e - out="$("$tmp" 2>"$errfile")" - rc=$? - set -e - - rm -f "$tmp" 2>/dev/null || true - - if [ "$rc" -eq 0 ] && [ -n "${out:-}" ] && [ "$out" != "null" ]; then - rm -f "$errfile" 2>/dev/null || true - return 0 - fi - - msg=$' - FAIL: #!'"$candidate"$'\n'" rc=$rc, stdout='${out:-}'"$'\n' - if [ -s "$errfile" ]; then - msg+=$' stderr:\n' - msg+="$(sed -n '1,8p' "$errfile")"$'\n' - else - msg+=$' stderr: \n' - fi - SHEBANG_ERRORS+=("$msg") - rm -f "$errfile" 2>/dev/null || true - return 1 } shebang="" for candidate in "${candidate_shebangs[@]}"; do - if validate_shebang "$candidate"; then - shebang="$candidate" - break - fi + if validate_shebang "$candidate"; then + shebang="$candidate" + break + fi done if [ -z "$shebang" ]; then - echo "ERROR: No valid shebang found (unable to execute bashio::addon.version via candidates)." >&2 - echo "Tried:" >&2 - printf ' - %s\n' "${candidate_shebangs[@]}" >&2 - if [ "${#SHEBANG_ERRORS[@]}" -gt 0 ]; then - echo "Probe failures:" >&2 - printf '%s\n' "${SHEBANG_ERRORS[@]}" >&2 - fi - exit 1 + echo "ERROR: No valid shebang found (unable to execute bashio::addon.version via candidates)." >&2 + echo "Tried:" >&2 + printf ' - %s\n' "${candidate_shebangs[@]}" >&2 + if [ "${#SHEBANG_ERRORS[@]}" -gt 0 ]; then + echo "Probe failures:" >&2 + printf '%s\n' "${SHEBANG_ERRORS[@]}" >&2 + fi + exit 1 fi ##################################### @@ -245,28 +251,28 @@ S6_CONTAINER_ENV="/run/s6/container_environment" # Only when this script is PID 1 -- under /init it is the stage-2 hook and stage 1 has already # written the directory -- and only where with-contenv exists to care. if $PID1 && { [ -x /command/with-contenv ] || [ -x /usr/bin/with-contenv ]; }; then - # Filled in a sibling and renamed into place, never written to live. A half-populated envdir is - # worse than an absent one: s6-envdir accepts it, so a with-contenv script starts and runs - # against an environment quietly missing SUPERVISOR_TOKEN, where an absent one stops it at its - # shebang. rename(2) means a concurrent reader -- a HEALTHCHECK can run alongside PID 1 -- sees - # the directory either absent or complete, never mid-dump. - # - # Cleared rather than written over: /run is not a tmpfs here, so an image layer can persist - # entries, and writing name by name would merge into them and leave variables PID 1 does not - # have, a stale SUPERVISOR_TOKEN among them. A failed rm has to abort the chain, because mkdir -p - # accepts a surviving symlink-to-directory and would let the dump follow it. rm does not traverse - # a symlink, but it would empty anything bind-mounted at this exact path -- not a configuration - # any add-on uses, and not one s6 would tolerate either. - if rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" && mkdir -p "$S6_CONTAINER_ENV.tmp" && - s6-dumpenv -- "$S6_CONTAINER_ENV.tmp" && mv "$S6_CONTAINER_ENV.tmp" "$S6_CONTAINER_ENV"; then - echo "Populated $S6_CONTAINER_ENV for with-contenv" - else - # Leaves the directory absent, which is exactly how this fails today -- so the failure mode is - # unchanged, not newly degraded. Never fatal, a read-only /run must still let the add-on boot, - # but never silent either, since the shebang failure it leaves behind says nothing on its own. - rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" 2>/dev/null || true - echo -e "\e[38;5;214m$(date) WARNING: could not populate $S6_CONTAINER_ENV; scripts with a with-contenv shebang will fail at their shebang, as they did before this was attempted\e[0m" - fi + # Filled in a sibling and renamed into place, never written to live. A half-populated envdir is + # worse than an absent one: s6-envdir accepts it, so a with-contenv script starts and runs + # against an environment quietly missing SUPERVISOR_TOKEN, where an absent one stops it at its + # shebang. rename(2) means a concurrent reader -- a HEALTHCHECK can run alongside PID 1 -- sees + # the directory either absent or complete, never mid-dump. + # + # Cleared rather than written over: /run is not a tmpfs here, so an image layer can persist + # entries, and writing name by name would merge into them and leave variables PID 1 does not + # have, a stale SUPERVISOR_TOKEN among them. A failed rm has to abort the chain, because mkdir -p + # accepts a surviving symlink-to-directory and would let the dump follow it. rm does not traverse + # a symlink, but it would empty anything bind-mounted at this exact path -- not a configuration + # any add-on uses, and not one s6 would tolerate either. + if rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" && mkdir -p "$S6_CONTAINER_ENV.tmp" \ + && s6-dumpenv -- "$S6_CONTAINER_ENV.tmp" && mv "$S6_CONTAINER_ENV.tmp" "$S6_CONTAINER_ENV"; then + echo "Populated $S6_CONTAINER_ENV for with-contenv" + else + # Leaves the directory absent, which is exactly how this fails today -- so the failure mode is + # unchanged, not newly degraded. Never fatal, a read-only /run must still let the add-on boot, + # but never silent either, since the shebang failure it leaves behind says nothing on its own. + rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" 2> /dev/null || true + echo -e "\e[38;5;214m$(date) WARNING: could not populate $S6_CONTAINER_ENV; scripts with a with-contenv shebang will fail at their shebang, as they did before this was attempted\e[0m" + fi fi #################################### @@ -276,21 +282,21 @@ 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 + 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 if [ -z "$BASHIO_LIB" ]; then - for f in /usr/local/lib/bashio-standalone.sh /.bashio-standalone.sh; do - if [ -f "$f" ]; then - BASHIO_LIB="$f" - break - fi - done + for f in /usr/local/lib/bashio-standalone.sh /.bashio-standalone.sh; do + if [ -f "$f" ]; then + BASHIO_LIB="$f" + break + fi + done fi ############################## @@ -316,56 +322,56 @@ fi # 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 + 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 + # 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)) + 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 + 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" + # 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 + # 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 + 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 + # Skipped when the attempt already consumed what was left, so the sleep cannot overshoot. + [ "$((deadline - SECONDS))" -gt 0 ] && sleep 1 + done } wait_for_supervisor @@ -375,97 +381,97 @@ wait_for_supervisor #################### run_one_script() { - local script="$1" + local script="$1" - echo "$script: executing" + echo "$script: executing" - if [ "$(id -u)" -eq 0 ]; then - chown "$(id -u)":"$(id -g)" "$script" || true - chmod a+x "$script" || true - else - echo -e "\e[38;5;214m$(date) WARNING: Script executed with user $(id -u):$(id -g), things can break and chown won't work\e[0m" - sed -i "s/^[[:space:]]*chown /true # chown /g" "$script" - sed -i "s/^[[:space:]]*chmod /true # chmod /g" "$script" - fi - - sed -i "1s|^.*|#!$shebang|" "$script" - chmod +x "$script" - - if [ "${ha_entry_source:-null}" = "true" ]; then - sed -i -E 's/^[[:space:]]*exit ([0-9]+)/return \1 \|\| exit \1/g' "$script" - sed -i 's/bashio::exit\.nok/return 1/g' "$script" - sed -i 's/bashio::exit\.ok/return 0/g' "$script" - # shellcheck disable=SC1090 - source "$script" || echo -e "\033[0;31mError\033[0m : $script exiting $?" - else - _run_rc=0 - "$script" || _run_rc=$? - if [ "$_run_rc" -eq 126 ] && [ -n "${BASHIO_LIB:-}" ]; then - echo "Direct exec failed (rc=126, likely E2BIG), retrying via source in subshell..." - _run_rc=0 - ( - # shellcheck disable=SC1090 - . "$BASHIO_LIB" 2>/dev/null || true - # shellcheck disable=SC1090 - . "$script" - ) || _run_rc=$? - if [ "$_run_rc" -ne 0 ]; then - echo -e "\033[0;31mError\033[0m : $script exiting $_run_rc" - fi - elif [ "$_run_rc" -ne 0 ]; then - echo -e "\033[0;31mError\033[0m : $script exiting $_run_rc" + if [ "$(id -u)" -eq 0 ]; then + chown "$(id -u)":"$(id -g)" "$script" || true + chmod a+x "$script" || true + else + echo -e "\e[38;5;214m$(date) WARNING: Script executed with user $(id -u):$(id -g), things can break and chown won't work\e[0m" + sed -i "s/^[[:space:]]*chown /true # chown /g" "$script" + sed -i "s/^[[:space:]]*chmod /true # chmod /g" "$script" fi - fi - sed -i '1a exit 0' "$script" + sed -i "1s|^.*|#!$shebang|" "$script" + chmod +x "$script" + + if [ "${ha_entry_source:-null}" = "true" ]; then + sed -i -E 's/^[[:space:]]*exit ([0-9]+)/return \1 \|\| exit \1/g' "$script" + sed -i 's/bashio::exit\.nok/return 1/g' "$script" + sed -i 's/bashio::exit\.ok/return 0/g' "$script" + # shellcheck disable=SC1090 + source "$script" || echo -e "\033[0;31mError\033[0m : $script exiting $?" + else + _run_rc=0 + "$script" || _run_rc=$? + if [ "$_run_rc" -eq 126 ] && [ -n "${BASHIO_LIB:-}" ]; then + echo "Direct exec failed (rc=126, likely E2BIG), retrying via source in subshell..." + _run_rc=0 + ( + # shellcheck disable=SC1090 + . "$BASHIO_LIB" 2> /dev/null || true + # shellcheck disable=SC1090 + . "$script" + ) || _run_rc=$? + if [ "$_run_rc" -ne 0 ]; then + echo -e "\033[0;31mError\033[0m : $script exiting $_run_rc" + fi + elif [ "$_run_rc" -ne 0 ]; then + echo -e "\033[0;31mError\033[0m : $script exiting $_run_rc" + fi + fi + + sed -i '1a exit 0' "$script" } if [ -d /etc/cont-init.d ]; then - for SCRIPTS in /etc/cont-init.d/*; do - [ -e "$SCRIPTS" ] || continue - run_one_script "$SCRIPTS" - done + for SCRIPTS in /etc/cont-init.d/*; do + [ -e "$SCRIPTS" ] || continue + run_one_script "$SCRIPTS" + done fi if $PID1; then - shopt -s nullglob - for runfile in /etc/services.d/*/run /etc/s6-overlay/s6-rc.d/*/run; do - [ -f "$runfile" ] || continue - echo "Starting: $runfile" - sed -i "1s|^.*|#!$shebang|" "$runfile" - chmod +x "$runfile" - ( - restart_count=0 - max_restarts=5 - while true; do - _svc_rc=0 - "$runfile" || _svc_rc=$? - if [ "$_svc_rc" -eq 126 ] && [ -n "${BASHIO_LIB:-}" ]; then - echo "Direct exec of $runfile failed (rc=126, likely E2BIG), retrying via source..." - _svc_rc=0 - ( - # shellcheck disable=SC1090 - . "$BASHIO_LIB" 2>/dev/null || true - # shellcheck disable=SC1090 - . "$runfile" - ) || _svc_rc=$? - fi - rc=$_svc_rc - if [ "$rc" -eq 0 ]; then - echo "$runfile exited cleanly (exit 0), not restarting." - break - fi - restart_count=$((restart_count + 1)) - if [ "$restart_count" -ge "$max_restarts" ]; then - echo -e "\033[0;31mERROR: $runfile has crashed $restart_count times (last exit code: $rc), giving up.\033[0m" - break - fi - echo -e "\e[38;5;214m$(date) WARNING: $runfile exited (code $rc), restarting (#${restart_count}/${max_restarts}) in 5s...\e[0m" - sleep 5 - done - ) & - done - shopt -u nullglob + shopt -s nullglob + for runfile in /etc/services.d/*/run /etc/s6-overlay/s6-rc.d/*/run; do + [ -f "$runfile" ] || continue + echo "Starting: $runfile" + sed -i "1s|^.*|#!$shebang|" "$runfile" + chmod +x "$runfile" + ( + restart_count=0 + max_restarts=5 + while true; do + _svc_rc=0 + "$runfile" || _svc_rc=$? + if [ "$_svc_rc" -eq 126 ] && [ -n "${BASHIO_LIB:-}" ]; then + echo "Direct exec of $runfile failed (rc=126, likely E2BIG), retrying via source..." + _svc_rc=0 + ( + # shellcheck disable=SC1090 + . "$BASHIO_LIB" 2> /dev/null || true + # shellcheck disable=SC1090 + . "$runfile" + ) || _svc_rc=$? + fi + rc=$_svc_rc + if [ "$rc" -eq 0 ]; then + echo "$runfile exited cleanly (exit 0), not restarting." + break + fi + restart_count=$((restart_count + 1)) + if [ "$restart_count" -ge "$max_restarts" ]; then + echo -e "\033[0;31mERROR: $runfile has crashed $restart_count times (last exit code: $rc), giving up.\033[0m" + break + fi + echo -e "\e[38;5;214m$(date) WARNING: $runfile exited (code $rc), restarting (#${restart_count}/${max_restarts}) in 5s...\e[0m" + sleep 5 + done + ) & + done + shopt -u nullglob fi ###################### @@ -473,18 +479,18 @@ fi ###################### if $PID1; then - echo " " - echo -e "\033[0;32mEverything started!\033[0m" + echo " " + echo -e "\033[0;32mEverything started!\033[0m" - while :; do - sleep infinity & - wait $! - done + while :; do + sleep infinity & + wait $! + done else - echo " " - echo -e "\033[0;32mStarting the upstream container\033[0m" - echo " " - if [ -f /docker-mods ]; then - exec /docker-mods - fi + echo " " + echo -e "\033[0;32mStarting the upstream container\033[0m" + echo " " + if [ -f /docker-mods ]; then + exec /docker-mods + fi fi diff --git a/CLAUDE.md b/CLAUDE.md index 699ba82bdc..01962cda51 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,7 +147,7 @@ manual actions with precedence. Prompts live in `.github/prompts/`, shared shell in `.github/scripts/`. | Workflow | Model | Trigger | Role | -|---|---|---|---| +| --- | --- | --- | --- | | `on_issues_ai_triage.yaml` | Sonnet-low | issue opened (+ author reply, daily catch-up) | Tier 1: classify, dedupe, answer, ask for info; label `ai-triage` for real add-on bugs | | `daily_ai_fix.yaml` | Opus 5-xhigh | daily 03:00 | Tier 2: diagnose the `ai-triage` batch; small+confident → ready PR (`ai:fixed`); else write a plan (`ai:plan-pending`) | | `on_issue_approved.yaml` | Opus 5-high | maintainer adds `ai:approved` | Tier 3: execute the approved plan → ready PR | @@ -176,7 +176,7 @@ prompt-only, not machine-enforced. ## Linting Rules | Tool | Config | Key ignores | -|------|--------|------------| +| ------ | -------- | ------------ | | Hadolint | `.hadolint.yaml` | DL3002, DL3006-9, DL3018 (no pinning required) | | ShellCheck | `.shellcheckrc` | SC2002 | | Markdownlint | `.markdownlint.yaml` | MD013 (line length), MD025, MD033, MD041 | diff --git a/addons_updater/README.md b/addons_updater/README.md index 830019047d..bbc3f4b74d 100644 --- a/addons_updater/README.md +++ b/addons_updater/README.md @@ -109,7 +109,7 @@ never a default. ### Addon version numbering -The `version` written in the addon `config.yaml` is the one Home Assistant compares to decide whether an update is available. Home Assistant hides the update when it can order both versions and the new one is not strictly newer (`1.2.3` -> `1.2.3-2` is a semver pre-release, so it is *older*), and it cannot order tags such as `version-bf9e0b4f` or `ubuntu-2026-06-01` at all. +The `version` written in the addon `config.yaml` is the one Home Assistant compares to decide whether an update is available. Home Assistant hides the update when it can order both versions and the new one is not strictly newer (`1.2.3` -> `1.2.3-2` is a semver pre-release, so it is _older_), and it cannot order tags such as `version-bf9e0b4f` or `ubuntu-2026-06-01` at all. The addon version is therefore derived from the upstream tag: diff --git a/aurral/CHANGELOG.md b/aurral/CHANGELOG.md index db3d160121..76b8524917 100644 --- a/aurral/CHANGELOG.md +++ b/aurral/CHANGELOG.md @@ -1,32 +1,32 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.8.0 (2026-09-05) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.7.0 (2026-08-29) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.5.1 (2026-08-22) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.4.0 (2026-08-13) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.1.0 (2026-08-08) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.0.3 (2026-08-01) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.0.2 (2026-08-01) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 2.0.0 (2026-07-22) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 1.76.52 (2026-07-11) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) - + ## 1.76.51 (2026-06-17) - Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases) ## 1.76.49 (2026-06-05) diff --git a/aurral/README.md b/aurral/README.md index db6a9464a8..30a5936ce7 100644 --- a/aurral/README.md +++ b/aurral/README.md @@ -6,7 +6,7 @@ This addon is based on the docker image ## Configuration | Option | Default | Description | -|---|---|---| +| --- | --- | --- | | `download_folder` | `/share/aurral/downloads` | Path where Aurral writes flow downloads. Must be under `/share`. | | `weekly_flow_folder` | `weekly-flow` | Subfolder name appended to `download_folder` for weekly flow files. The full path will be `download_folder/weekly_flow_folder`. | diff --git a/autobrr/CHANGELOG.md b/autobrr/CHANGELOG.md index 9b02f17ba6..5fd7fdf10d 100644 --- a/autobrr/CHANGELOG.md +++ b/autobrr/CHANGELOG.md @@ -1,19 +1,19 @@ - + ## 1.86.0 (2026-09-12) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) - + ## 1.85.0 (2026-08-29) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) - + ## 1.84.0 (2026-08-15) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) - + ## 1.83.0 (2026-08-01) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) - + ## 1.82.1 (2026-07-16) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) - + ## 1.81.0 (2026-07-04) - Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases) ## 1.80.0 (2026-06-05) diff --git a/autobrr/README.md b/autobrr/README.md index a486ba8e0b..379c66a449 100644 --- a/autobrr/README.md +++ b/autobrr/README.md @@ -67,7 +67,7 @@ Default credentials: `admin` / `password` (change after first login). ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/bazarr/README.md b/bazarr/README.md index c7b2a4df35..849411c337 100644 --- a/bazarr/README.md +++ b/bazarr/README.md @@ -44,7 +44,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/bazarr/rootfs/etc/services.d/nginx/run b/bazarr/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/bentopdf/README.md b/bentopdf/README.md index 614080cac0..37ac6d47d1 100644 --- a/bentopdf/README.md +++ b/bentopdf/README.md @@ -17,7 +17,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Organize & Edit | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | Merge PDF | Split PDF | Organize PDF | | Delete Pages | Extract Pages | Reverse Pages | | Rotate PDF | Rotate Custom | Crop PDF | @@ -28,7 +28,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Convert TO PDF | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | Word to PDF | Excel to PDF | PowerPoint to PDF | | Image to PDF | JPG to PDF | PNG to PDF | | BMP to PDF | TIFF to PDF | WEBP to PDF | @@ -44,7 +44,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Convert FROM PDF | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | PDF to DOCX | PDF to Excel | PDF to JPG | | PDF to PNG | PDF to BMP | PDF to TIFF | | PDF to WEBP | PDF to SVG | PDF to Text | @@ -54,7 +54,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Security & Metadata | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | Encrypt PDF | Decrypt PDF | Change Permissions | | Remove Restrictions | Sign PDF | Digital Sign PDF | | Validate Signature | Edit Metadata | View Metadata | @@ -64,7 +64,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Enhance & Process | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | Compress PDF | OCR PDF | Deskew PDF | | Rasterize PDF | Linearize PDF | PDF to PDF/A | | Adjust Colors | Invert Colors | Text Color | @@ -76,7 +76,7 @@ A privacy-first PDF toolkit running entirely in your browser — no uploads, no ### Forms & More | Tool | Tool | Tool | -|------|------|------| +| ------ | ------ | ------ | | Form Creator | Form Filler | Table of Contents | | Bookmark | PDF Editor | Extract Images | | Extract Tables | Extract Attachments | Edit Attachments | diff --git a/bentopdf/rootfs/etc/s6-overlay/s6-rc.d/bentopdf/run b/bentopdf/rootfs/etc/s6-overlay/s6-rc.d/bentopdf/run old mode 100644 new mode 100755 diff --git a/birdnet-go-dev/README.md b/birdnet-go-dev/README.md index dcb76ec7ef..48987fba28 100644 --- a/birdnet-go-dev/README.md +++ b/birdnet-go-dev/README.md @@ -75,7 +75,7 @@ Requires a second model to confirm the **first** detection of each bird species The first detection of a species in a day is the weakest evidence the pipeline produces, and it is the one that creates a "new species today" entry. Asking two models to agree on just that one detection removes most spurious new-species entries without slowing anything else down. -**Off by default.** Turn it on in the web UI under *Settings → Filters → First Daily Detection Consensus*, or in `config.yaml`: +**Off by default.** Turn it on in the web UI under _Settings → Filters → First Daily Detection Consensus_, or in `config.yaml`: ```yaml realtime: @@ -89,7 +89,7 @@ It deliberately does **nothing** in these cases, all of which keep today's singl - you run only one bird model (the default) — a second opinion does not exist, so the rule can never trigger - the species is not a bird — bats and the non-bird sound classes Perch reports (insects, amphibians, mammals, `power_tool`, and so on) -- the species is not known to *every* active bird model analyzing that audio source — a species only one of them can name could never reach two confirmations. With several sources running different model combinations, this is decided per source, not add-on-wide +- the species is not known to _every_ active bird model analyzing that audio source — a species only one of them can name could never reach two confirmations. With several sources running different model combinations, this is decided per source, not add-on-wide - a dynamic threshold has actually lowered the bar for that species, meaning you asked for a more permissive gate - the taxonomy or the database cannot be consulted — it fails open and accepts the detection diff --git a/birdnet-go-dev/merge-prs.sh b/birdnet-go-dev/merge-prs.sh index daa08b2033..7e56012853 100755 --- a/birdnet-go-dev/merge-prs.sh +++ b/birdnet-go-dev/merge-prs.sh @@ -33,7 +33,10 @@ TARGET_DIR="" while [ "$#" -gt 0 ]; do case "$1" in --check) CHECK_ONLY=1 ;; - -*) echo "unknown option: $1" >&2; exit 64 ;; + -*) + echo "unknown option: $1" >&2 + exit 64 + ;; *) # Last-one-wins would silently clone into the wrong directory if a caller ever # appended an argument; the pre-flag script used "${1}", so refuse rather than @@ -184,7 +187,7 @@ if [ "${CHECK_ONLY}" = "1" ]; then fi echo "!!! CHECK FAILED: ${#conflicting[@]} PR(s) would break the add-on build" >&2 for entry in "${conflicting[@]}"; do - IFS='|' read -r number scope files title <<<"${entry}" + IFS='|' read -r number scope files title <<< "${entry}" echo "!!! CONFLICT pr=#${number} conflicts-with=${scope} files=${files} title=${title}" >&2 done echo "!!! conflicts-with=main -> the PR is stale against main; merge main into its branch and resolve there." >&2 diff --git a/birdnet-go-dev/rootfs/etc/cont-init.d/01-structure.sh b/birdnet-go-dev/rootfs/etc/cont-init.d/01-structure.sh index 4c11de5822..86c0432be2 100755 --- a/birdnet-go-dev/rootfs/etc/cont-init.d/01-structure.sh +++ b/birdnet-go-dev/rootfs/etc/cont-init.d/01-structure.sh @@ -43,8 +43,8 @@ if [ ! -f "$CONFIG_LOCATION" ]; then # config. (We can't remove the file and continue — subsequent yq calls # under set -e would abort the init script.) if ! curl -fL -s -S \ - https://raw.githubusercontent.com/tphakala/birdnet-go/refs/heads/main/internal/conf/config.yaml \ - -o "$CONFIG_LOCATION"; then + https://raw.githubusercontent.com/tphakala/birdnet-go/refs/heads/main/internal/conf/config.yaml \ + -o "$CONFIG_LOCATION"; then bashio::log.warning "Could not download default config.yaml; seeding an empty document so addon defaults can populate it" echo '{}' > "$CONFIG_LOCATION" fi @@ -190,5 +190,5 @@ LOG_DIR="/config/logs" if [ -d "$LOG_DIR" ]; then bashio::log.info "Trimming log files older than ${LOG_MAX_AGE_DAYS} days in ${LOG_DIR}" ln -sf "$LOG_DIR" /logs - find "$LOG_DIR" -type f -name "*.log*" -mtime +"$LOG_MAX_AGE_DAYS" -delete 2>/dev/null || true + find "$LOG_DIR" -type f -name "*.log*" -mtime +"$LOG_MAX_AGE_DAYS" -delete 2> /dev/null || true fi diff --git a/birdnet-go-dev/rootfs/etc/cont-init.d/33-mariadb.sh b/birdnet-go-dev/rootfs/etc/cont-init.d/33-mariadb.sh index 29d1e30772..6d953ae289 100755 --- a/birdnet-go-dev/rootfs/etc/cont-init.d/33-mariadb.sh +++ b/birdnet-go-dev/rootfs/etc/cont-init.d/33-mariadb.sh @@ -44,8 +44,8 @@ if ! bashio::config.true 'mariadb_auto_config'; then # Only revert if config.yaml points at the HA MariaDB host we would have # written — a mysql block pointing at a different host was set manually. # shellcheck disable=SC2016 - CURRENT_MYSQL_HOST="$(yq -r '.output.mysql.host // empty' "$CONFIG_LOCATION" 2>/dev/null || true)" - if yq -e '.output.mysql.enabled == true' "$CONFIG_LOCATION" >/dev/null 2>&1 \ + CURRENT_MYSQL_HOST="$(yq -r '.output.mysql.host // empty' "$CONFIG_LOCATION" 2> /dev/null || true)" + if yq -e '.output.mysql.enabled == true' "$CONFIG_LOCATION" > /dev/null 2>&1 \ && [ "${CURRENT_MYSQL_HOST}" = "${MYSQL_HOST}" ]; then yq -i -y \ '.output.mysql.enabled = false @@ -71,7 +71,7 @@ bashio::log.green "---" # Resolve MariaDB hostname to IPv4: on HAOS >=17.3 the Supervisor network # gained IPv6, but the MariaDB addon only grants its user from the IPv4 # subnet. Fall back to the raw hostname if resolution fails. -MYSQL_HOST_RESOLVED="$(getent ahostsv4 "${MYSQL_HOST}" 2>/dev/null | awk '{print $1; exit}')" +MYSQL_HOST_RESOLVED="$(getent ahostsv4 "${MYSQL_HOST}" 2> /dev/null | awk '{print $1; exit}')" MYSQL_HOST_RESOLVED="${MYSQL_HOST_RESOLVED:-${MYSQL_HOST}}" if [ "${MYSQL_HOST_RESOLVED}" != "${MYSQL_HOST}" ]; then bashio::log.blue "Resolved ${MYSQL_HOST} -> ${MYSQL_HOST_RESOLVED} (forcing IPv4)" diff --git a/birdnet-go/CHANGELOG.md b/birdnet-go/CHANGELOG.md index f270370e66..4655223f36 100644 --- a/birdnet-go/CHANGELOG.md +++ b/birdnet-go/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 20260827 (2026-08-29) - Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases) - Upstream tag : 20260823 @@ -7,13 +7,13 @@ - Minor bugs fixed ## 20260716.1 (26-08-2026) - Minor bugs fixed - + ## 20260716.1 (2026-08-26) - Rebuild after syncing the alexbelgium/birdnet-go fork with upstream tphakala/birdnet-go and resolving conflicts on all open pull requests ## 20260716 (2026-07-16) - Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases) - + ## 20260712 (2026-07-13) - Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases) ## nightly-20260615-4 (09-07-2026) @@ -23,8 +23,8 @@ ## nightly-20260615-2 (05-07-2026) - Minor bugs fixed - - Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774) - - MQTT auto-config now also enables BirdNET-Go's native Home Assistant MQTT auto-discovery: detection sensors appear in Home Assistant automatically with no manual YAML (existing UI/config.yaml edits are preserved) +- Fix detections/database not persisting across restarts on a fresh install: upstream's default `config.yaml` ships `output.sqlite.path: birdnet.db` (relative) explicitly, so the missing-only (`//=`) seeding introduced previously never rewrote it to an absolute path. A relative path resolves against the app's ephemeral working directory, so the database was silently recreated empty on every restart. Any relative `output.sqlite.path` is now rewritten to live under the persistent `/config` on startup; values already set to an absolute path are left untouched. (https://github.com/tphakala/birdnet-go/discussions/3774) +- MQTT auto-config now also enables BirdNET-Go's native Home Assistant MQTT auto-discovery: detection sensors appear in Home Assistant automatically with no manual YAML (existing UI/config.yaml edits are preserved) - MQTT auto-config seeds `realtime.mqtt.retain: true` (only when unset) so sensor states survive Home Assistant restarts - Added supervisor watchdog (tcp://[HOST]:[PORT:8080]) so the add-on is automatically restarted if BirdNET-Go stops responding - Added backup_exclude for rotated logs, making Home Assistant backups smaller (SQLite journals are kept so hot backups stay consistent) diff --git a/birdnet-go/rootfs/etc/cont-init.d/01-structure.sh b/birdnet-go/rootfs/etc/cont-init.d/01-structure.sh index c0e3493064..4c374b230e 100755 --- a/birdnet-go/rootfs/etc/cont-init.d/01-structure.sh +++ b/birdnet-go/rootfs/etc/cont-init.d/01-structure.sh @@ -43,8 +43,8 @@ if [ ! -f "$CONFIG_LOCATION" ]; then # config. (We can't remove the file and continue — subsequent yq calls # under set -e would abort the init script.) if ! curl -fL -s -S \ - https://raw.githubusercontent.com/tphakala/birdnet-go/refs/heads/main/internal/conf/config.yaml \ - -o "$CONFIG_LOCATION"; then + https://raw.githubusercontent.com/tphakala/birdnet-go/refs/heads/main/internal/conf/config.yaml \ + -o "$CONFIG_LOCATION"; then bashio::log.warning "Could not download default config.yaml; seeding an empty document so addon defaults can populate it" echo '{}' > "$CONFIG_LOCATION" fi @@ -186,5 +186,5 @@ LOG_DIR="/config/logs" if [ -d "$LOG_DIR" ]; then bashio::log.info "Trimming log files older than ${LOG_MAX_AGE_DAYS} days in ${LOG_DIR}" ln -sf "$LOG_DIR" /logs - find "$LOG_DIR" -type f -name "*.log*" -mtime +"$LOG_MAX_AGE_DAYS" -delete 2>/dev/null || true + find "$LOG_DIR" -type f -name "*.log*" -mtime +"$LOG_MAX_AGE_DAYS" -delete 2> /dev/null || true fi diff --git a/birdnet-pi-zach/DOCS.md b/birdnet-pi-zach/DOCS.md index 5d4b7aa2bb..bd4e98efee 100644 --- a/birdnet-pi-zach/DOCS.md +++ b/birdnet-pi-zach/DOCS.md @@ -9,9 +9,9 @@ Here are some example tests I did (the whole threads are really interesting also **My recommendation:** -- **Best entry system (< 50 €):** Boya By-lm40 (30 €) + deadcat (10 €) -- **Best middle end system (< 150 €):** Clippy EM272 TRS/TRRS (55 €) + Rode AI micro trs/trrs to usb (70 €) + Rycote deadcat (27 €) -- **Best high end system (<400 €):** Clippy EM272 XLR (85 €) or LOM Ucho Pro (75 €) + Focusrite Scarlet 2i2 4th Gen (200 €) + Bubblebee Pro Extreme deadcat (45 €) +- **Best entry system (< 50 €):** Boya By-lm40 (30 €) + deadcat (10 €) +- **Best middle end system (< 150 €):** Clippy EM272 TRS/TRRS (55 €) + Rode AI micro trs/trrs to usb (70 €) + Rycote deadcat (27 €) +- **Best high end system (<400 €):** Clippy EM272 XLR (85 €) or LOM Ucho Pro (75 €) + Focusrite Scarlet 2i2 4th Gen (200 €) + Bubblebee Pro Extreme deadcat (45 €) **Sources for high end microphones in Europe:** diff --git a/birdnet-pi-zach/rootfs/helpers/birdnet_to_mqtt.py b/birdnet-pi-zach/rootfs/helpers/birdnet_to_mqtt.py index b075e0e231..2e5beaeec0 100644 --- a/birdnet-pi-zach/rootfs/helpers/birdnet_to_mqtt.py +++ b/birdnet-pi-zach/rootfs/helpers/birdnet_to_mqtt.py @@ -112,6 +112,7 @@ def automatic_mqtt_publish(file, detection, path): mqttc.publish(mqtt_topic, json_bird, 1) log.info("Posted to MQTT: ok") + # Create MQTT client using legacy callback API when available for # compatibility with paho-mqtt >= 2.0 callback_api = getattr(mqtt, "CallbackAPIVersion", None) diff --git a/birdnet-pi/DOCS.md b/birdnet-pi/DOCS.md index 5d4b7aa2bb..bd4e98efee 100644 --- a/birdnet-pi/DOCS.md +++ b/birdnet-pi/DOCS.md @@ -9,9 +9,9 @@ Here are some example tests I did (the whole threads are really interesting also **My recommendation:** -- **Best entry system (< 50 €):** Boya By-lm40 (30 €) + deadcat (10 €) -- **Best middle end system (< 150 €):** Clippy EM272 TRS/TRRS (55 €) + Rode AI micro trs/trrs to usb (70 €) + Rycote deadcat (27 €) -- **Best high end system (<400 €):** Clippy EM272 XLR (85 €) or LOM Ucho Pro (75 €) + Focusrite Scarlet 2i2 4th Gen (200 €) + Bubblebee Pro Extreme deadcat (45 €) +- **Best entry system (< 50 €):** Boya By-lm40 (30 €) + deadcat (10 €) +- **Best middle end system (< 150 €):** Clippy EM272 TRS/TRRS (55 €) + Rode AI micro trs/trrs to usb (70 €) + Rycote deadcat (27 €) +- **Best high end system (<400 €):** Clippy EM272 XLR (85 €) or LOM Ucho Pro (75 €) + Focusrite Scarlet 2i2 4th Gen (200 €) + Bubblebee Pro Extreme deadcat (45 €) **Sources for high end microphones in Europe:** diff --git a/birdnet-pi/rootfs/helpers/birdnet_to_mqtt.py b/birdnet-pi/rootfs/helpers/birdnet_to_mqtt.py index b075e0e231..2e5beaeec0 100644 --- a/birdnet-pi/rootfs/helpers/birdnet_to_mqtt.py +++ b/birdnet-pi/rootfs/helpers/birdnet_to_mqtt.py @@ -112,6 +112,7 @@ def automatic_mqtt_publish(file, detection, path): mqttc.publish(mqtt_topic, json_bird, 1) log.info("Posted to MQTT: ok") + # Create MQTT client using legacy callback API when available for # compatibility with paho-mqtt >= 2.0 callback_api = getattr(mqtt, "CallbackAPIVersion", None) diff --git a/birdnet-pipy/CHANGELOG.md b/birdnet-pipy/CHANGELOG.md index 5dbfbcd9da..c317f582f6 100644 --- a/birdnet-pipy/CHANGELOG.md +++ b/birdnet-pipy/CHANGELOG.md @@ -1,20 +1,20 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 0.8.8 (2026-08-22) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.8.6 (2026-08-13) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.8.5 (2026-08-01) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.8.4 (2026-07-13) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.8.3 (2026-07-11) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.8.2.1 (2026-07-05) - Re-tag of 0.8.2-1 with no content change. Home Assistant compares add-on versions with semver semantics, where a `-N` suffix counts as a *pre-release* and sorts **below** the base version — so users already on 0.8.2 saw the 0.8.2-1 nginx fix as "Up-to-date" with the Update button disabled. Four-segment `0.8.2.1` sorts above both `0.8.2` and `0.8.2-1` (and below the next upstream `0.8.3`), so the update becomes installable everywhere. @@ -24,7 +24,7 @@ ## 0.8.2 (2026-07-04) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) - + ## 0.7.5 (2026-06-23) - Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases) ## 0.7.4 (2026-06-01) diff --git a/birdnet-pipy/config.yaml b/birdnet-pipy/config.yaml index b639b43c8b..9515b97e1a 100644 --- a/birdnet-pipy/config.yaml +++ b/birdnet-pipy/config.yaml @@ -73,7 +73,7 @@ map: name: BirdNET-PiPy options: env_vars: [] - ICECAST_PASSWORD: '' + ICECAST_PASSWORD: "" data_location: /config/data panel_icon: mdi:bird ports: diff --git a/birdnet-pipy/rootfs/etc/cont-init.d/01-structure.sh b/birdnet-pipy/rootfs/etc/cont-init.d/01-structure.sh index 6c102d6485..9372045d99 100755 --- a/birdnet-pipy/rootfs/etc/cont-init.d/01-structure.sh +++ b/birdnet-pipy/rootfs/etc/cont-init.d/01-structure.sh @@ -7,11 +7,11 @@ DATA_LOCATION="$(bashio::config 'data_location' || true)" DATA_LOCATION="${DATA_LOCATION:-$DEFAULT_LOCATION}" case "${DATA_LOCATION}" in - /config/*|/share/*|/data/*) ;; - *) - bashio::log.warning "Invalid data_location '${DATA_LOCATION}', falling back to ${DEFAULT_LOCATION}" - DATA_LOCATION="${DEFAULT_LOCATION}" - ;; + /config/* | /share/* | /data/*) ;; + *) + bashio::log.warning "Invalid data_location '${DATA_LOCATION}', falling back to ${DEFAULT_LOCATION}" + DATA_LOCATION="${DEFAULT_LOCATION}" + ;; esac LEGACY1="/config/birdnet-pipy/data" @@ -19,14 +19,14 @@ LEGACY2="/data" mkdir -p "${DATA_LOCATION}" -if [ -z "$(ls -A "${DATA_LOCATION}" 2>/dev/null || true)" ]; then - if [ -d "${LEGACY1}" ] && [ -n "$(ls -A "${LEGACY1}" 2>/dev/null || true)" ]; then - bashio::log.notice "Migrating legacy data from ${LEGACY1} to ${DATA_LOCATION}" - cp -a "${LEGACY1}/." "${DATA_LOCATION}/" || true - elif [ -d "${LEGACY2}" ] && [ "${LEGACY2}" != "${DATA_LOCATION}" ] && [ -n "$(ls -A "${LEGACY2}" 2>/dev/null || true)" ]; then - bashio::log.notice "Migrating legacy data from ${LEGACY2} to ${DATA_LOCATION}" - cp -a "${LEGACY2}/." "${DATA_LOCATION}/" || true - fi +if [ -z "$(ls -A "${DATA_LOCATION}" 2> /dev/null || true)" ]; then + if [ -d "${LEGACY1}" ] && [ -n "$(ls -A "${LEGACY1}" 2> /dev/null || true)" ]; then + bashio::log.notice "Migrating legacy data from ${LEGACY1} to ${DATA_LOCATION}" + cp -a "${LEGACY1}/." "${DATA_LOCATION}/" || true + elif [ -d "${LEGACY2}" ] && [ "${LEGACY2}" != "${DATA_LOCATION}" ] && [ -n "$(ls -A "${LEGACY2}" 2> /dev/null || true)" ]; then + bashio::log.notice "Migrating legacy data from ${LEGACY2} to ${DATA_LOCATION}" + cp -a "${LEGACY2}/." "${DATA_LOCATION}/" || true + fi fi mkdir -p "${DATA_LOCATION}/config" "${DATA_LOCATION}/clips" "${DATA_LOCATION}/logs" "${DATA_LOCATION}/cache" || true diff --git a/birdnet-pipy/rootfs/etc/cont-init.d/10-config.sh b/birdnet-pipy/rootfs/etc/cont-init.d/10-config.sh index 3c5a51dc58..499e322790 100755 --- a/birdnet-pipy/rootfs/etc/cont-init.d/10-config.sh +++ b/birdnet-pipy/rootfs/etc/cont-init.d/10-config.sh @@ -7,10 +7,10 @@ CFG_DIR="${DATA_DIR}/config" SETTINGS="${CFG_DIR}/user_settings.json" if [ -f "$SETTINGS" ]; then - if ! grep -q 'detection' "$SETTINGS"; then - bak="${SETTINGS}.bak" - [ -e "$bak" ] && bak="${SETTINGS}.bak.$(date -u +%Y%m%dT%H%M%SZ)" - mv -f -- "$SETTINGS" "$bak" - echo "WARNING: Erroneous file detected: '$SETTINGS' did not contain 'detection' and was renamed to '$bak'." >&2 - fi + if ! grep -q 'detection' "$SETTINGS"; then + bak="${SETTINGS}.bak" + [ -e "$bak" ] && bak="${SETTINGS}.bak.$(date -u +%Y%m%dT%H%M%SZ)" + mv -f -- "$SETTINGS" "$bak" + echo "WARNING: Erroneous file detected: '$SETTINGS' did not contain 'detection' and was renamed to '$bak'." >&2 + fi fi diff --git a/birdnet-pipy/rootfs/etc/cont-init.d/15-audio_perms.sh b/birdnet-pipy/rootfs/etc/cont-init.d/15-audio_perms.sh index 3752d6d86d..723001cc7c 100755 --- a/birdnet-pipy/rootfs/etc/cont-init.d/15-audio_perms.sh +++ b/birdnet-pipy/rootfs/etc/cont-init.d/15-audio_perms.sh @@ -3,21 +3,21 @@ set -euo pipefail SND_GID="" -if [ -e /dev/snd ] && command -v stat >/dev/null 2>&1; then - SND_GID="$(stat -c '%g' /dev/snd 2>/dev/null || true)" +if [ -e /dev/snd ] && command -v stat > /dev/null 2>&1; then + SND_GID="$(stat -c '%g' /dev/snd 2> /dev/null || true)" fi -if [ -n "${SND_GID}" ] && getent group audio >/dev/null 2>&1; then - current_gid="$(getent group audio | cut -d: -f3 || true)" - if [ -n "${current_gid}" ] && [ "${current_gid}" != "${SND_GID}" ]; then - groupmod -g "${SND_GID}" audio 2>/dev/null || true - fi +if [ -n "${SND_GID}" ] && getent group audio > /dev/null 2>&1; then + current_gid="$(getent group audio | cut -d: -f3 || true)" + if [ -n "${current_gid}" ] && [ "${current_gid}" != "${SND_GID}" ]; then + groupmod -g "${SND_GID}" audio 2> /dev/null || true + fi fi for u in root nginx www-data icecast2; do - if id "${u}" >/dev/null 2>&1; then - usermod -aG audio "${u}" 2>/dev/null || true - fi + if id "${u}" > /dev/null 2>&1; then + usermod -aG audio "${u}" 2> /dev/null || true + fi done # Create /run/pulse/native symlink if the PulseAudio socket is elsewhere @@ -25,21 +25,21 @@ done PULSE_SOCK="${PULSE_SERVER:-}" PULSE_SOCK="${PULSE_SOCK#unix:}" if [ -n "${PULSE_SOCK}" ] && [ -S "${PULSE_SOCK}" ] && [ ! -S /run/pulse/native ]; then - mkdir -p /run/pulse - ln -sf "${PULSE_SOCK}" /run/pulse/native + mkdir -p /run/pulse + ln -sf "${PULSE_SOCK}" /run/pulse/native fi # Copy PulseAudio cookie for the icecast2 user so it can authenticate -if id icecast2 >/dev/null 2>&1; then - ICECAST_HOME="$(getent passwd icecast2 | cut -d: -f6)" - if [ -n "${ICECAST_HOME}" ]; then - for cookie in /config/.config/pulse/cookie /root/.config/pulse/cookie; do - if [ -f "${cookie}" ]; then - mkdir -p "${ICECAST_HOME}/.config/pulse" - cp "${cookie}" "${ICECAST_HOME}/.config/pulse/cookie" - chown icecast2 "${ICECAST_HOME}/.config/pulse" "${ICECAST_HOME}/.config/pulse/cookie" - break - fi - done - fi +if id icecast2 > /dev/null 2>&1; then + ICECAST_HOME="$(getent passwd icecast2 | cut -d: -f6)" + if [ -n "${ICECAST_HOME}" ]; then + for cookie in /config/.config/pulse/cookie /root/.config/pulse/cookie; do + if [ -f "${cookie}" ]; then + mkdir -p "${ICECAST_HOME}/.config/pulse" + cp "${cookie}" "${ICECAST_HOME}/.config/pulse/cookie" + chown icecast2 "${ICECAST_HOME}/.config/pulse" "${ICECAST_HOME}/.config/pulse/cookie" + break + fi + done + fi fi diff --git a/birdnet-pipy/rootfs/etc/services.d/api/run b/birdnet-pipy/rootfs/etc/services.d/api/run old mode 100644 new mode 100755 index 27ac1b2eaf..05291fde8a --- a/birdnet-pipy/rootfs/etc/services.d/api/run +++ b/birdnet-pipy/rootfs/etc/services.d/api/run @@ -10,10 +10,10 @@ cd /app # and core.main reach the API locally, satisfying the wait_for in nginx/run + # main/run). Heavy admin jobs cooperatively yield to keep this worker live. exec gunicorn wsgi:application \ - --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker \ - --workers 1 \ - --bind 127.0.0.1:5002 \ - --timeout 120 \ - --graceful-timeout 30 \ - --keep-alive 65 \ - --error-logfile - + --worker-class geventwebsocket.gunicorn.workers.GeventWebSocketWorker \ + --workers 1 \ + --bind 127.0.0.1:5002 \ + --timeout 120 \ + --graceful-timeout 30 \ + --keep-alive 65 \ + --error-logfile - diff --git a/birdnet-pipy/rootfs/etc/services.d/icecast/run b/birdnet-pipy/rootfs/etc/services.d/icecast/run old mode 100644 new mode 100755 diff --git a/birdnet-pipy/rootfs/etc/services.d/main/run b/birdnet-pipy/rootfs/etc/services.d/main/run old mode 100644 new mode 100755 diff --git a/birdnet-pipy/rootfs/etc/services.d/model/run b/birdnet-pipy/rootfs/etc/services.d/model/run old mode 100644 new mode 100755 diff --git a/birdnet-pipy/rootfs/etc/services.d/nginx/run b/birdnet-pipy/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/bitwarden/CHANGELOG.md b/bitwarden/CHANGELOG.md index 39e5e1da0d..d02caa9fa7 100644 --- a/bitwarden/CHANGELOG.md +++ b/bitwarden/CHANGELOG.md @@ -1,10 +1,10 @@ - + ## 1.37.2 (2026-08-29) - Update to latest version from dani-garcia/bitwarden_rs (changelog : https://github.com/dani-garcia/bitwarden_rs/releases) - + ## 1.37.1 (2026-08-01) - Update to latest version from dani-garcia/bitwarden_rs (changelog : https://github.com/dani-garcia/bitwarden_rs/releases) - + ## 1.37.0 (2026-07-25) - Update to latest version from dani-garcia/bitwarden_rs (changelog : https://github.com/dani-garcia/bitwarden_rs/releases) diff --git a/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run b/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/init-nginx/run old mode 100644 new mode 100755 diff --git a/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/nginx/run old mode 100644 new mode 100755 diff --git a/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/vaultwarden/run b/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/vaultwarden/run old mode 100644 new mode 100755 index 45cf56410e..28f2283883 --- a/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/vaultwarden/run +++ b/bitwarden/rootfs/etc/s6-overlay/s6-rc.d/vaultwarden/run @@ -21,19 +21,19 @@ export ROCKET_SECRET_KEY="${secret_key}" # Find the matching log level if bashio::config.has_value 'log_level'; then case "$(bashio::string.lower "$(bashio::config 'log_level')")" in - all|trace) + all | trace) log_level="trace" ;; debug) log_level="debug" ;; - info|notice) + info | notice) log_level="info" ;; warning) log_level="warn" ;; - error|fatal) + error | fatal) log_level="error" ;; off) diff --git a/booksonic_air/README.md b/booksonic_air/README.md index 03138da482..5665fc1fa5 100644 --- a/booksonic_air/README.md +++ b/booksonic_air/README.md @@ -64,7 +64,7 @@ Default credentials are shown in the startup logs. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/browser_brave/CHANGELOG.md b/browser_brave/CHANGELOG.md index 4332f84359..1f9091d017 100644 --- a/browser_brave/CHANGELOG.md +++ b/browser_brave/CHANGELOG.md @@ -1,50 +1,50 @@ - + ## 1.95.101-ls128 (2026-09-12) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.94.121-ls127 (2026-09-05) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.94.117-ls125 (2026-08-29) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.93.138-ls124 (2026-08-22) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.93.136-ls122 (2026-08-13) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.93.134-ls121 (2026-08-08) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.93.129-ls119 (2026-08-01) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.92.144-ls117 (2026-07-25) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.92.141-ls115 (2026-07-18) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.92.140-ls113 (2026-07-16) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.92.139-ls112 (2026-07-11) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.92.134-ls109 (2026-07-04) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.91.180-ls108 (2026-06-27) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.91.175-ls106 (2026-06-20) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.91.172-ls105 (2026-06-13) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) - + ## 1.91.171-ls104 (2026-06-11) - Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases) ## 1.91.168-ls103 (2026-06-05) diff --git a/browser_chromium/CHANGELOG.md b/browser_chromium/CHANGELOG.md index 3992af0f17..f5e5f155fb 100644 --- a/browser_chromium/CHANGELOG.md +++ b/browser_chromium/CHANGELOG.md @@ -1,13 +1,13 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2026.09.05 (2026-09-05) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - Upstream tag : version-d39975b5 - + ## 2026.08.13 (2026-08-13) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - Upstream tag : version-e8713bf7 - + ## 2026.08.01.1 (2026-08-01) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - Upstream tag : version-e2e1ec9c @@ -15,25 +15,25 @@ - Version renamed from `version-bf9e0b4f`, which Home Assistant could not order and therefore could not reliably offer as an update: the previous version held no number that could order it, so the date of this change is used. The addon itself and the upstream version it tracks are unchanged - + ## version-bf9e0b4f (2026-07-25) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-ae9a7fca (2026-07-16) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-30a7c401 (2026-07-11) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-7148c2a3 (2026-07-04) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-ec868334 (2026-06-27) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-b9b82d3e (2026-06-20) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) - + ## version-cca5154d (2026-06-11) - Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases) ## version-89f88890 (2026-06-05) diff --git a/browser_chromium/rootfs/etc/cont-init.d/20-folders.sh b/browser_chromium/rootfs/etc/cont-init.d/20-folders.sh old mode 100755 new mode 100644 index 242d8446dd..f95c497f33 --- a/browser_chromium/rootfs/etc/cont-init.d/20-folders.sh +++ b/browser_chromium/rootfs/etc/cont-init.d/20-folders.sh @@ -7,7 +7,6 @@ set -e PUID=$(bashio::config "PUID") PGID=$(bashio::config "PGID") - # Create cache #mkdir -p /.cache #chmod 755 /.cache diff --git a/browser_chromium/rootfs/etc/usr/bin/wrapped-chromium b/browser_chromium/rootfs/etc/usr/bin/wrapped-chromium index 467bea4e74..f26f396210 100644 --- a/browser_chromium/rootfs/etc/usr/bin/wrapped-chromium +++ b/browser_chromium/rootfs/etc/usr/bin/wrapped-chromium @@ -5,12 +5,12 @@ set -Eeuo pipefail # Resolve chromium binary (LSIO uses this path) if [[ -x /usr/lib/chromium/chromium ]]; then - BIN=/usr/lib/chromium/chromium + BIN=/usr/lib/chromium/chromium elif command -v chromium >/dev/null 2>&1; then - BIN="$(command -v chromium)" + BIN="$(command -v chromium)" else - echo "ERROR: chromium binary not found" >&2 - exit 127 + echo "ERROR: chromium binary not found" >&2 + exit 127 fi # Fixed profile dir as requested @@ -19,7 +19,7 @@ mkdir -p "${USER_DATA_DIR}" # Cleanup stale locks if chromium is not already running if ! pgrep -f 'chromium' >/dev/null 2>&1; then - rm -f "${USER_DATA_DIR}/Singleton"* || true + rm -f "${USER_DATA_DIR}/Singleton"* || true fi # Detect seccomp mode (0 = disabled) @@ -27,22 +27,22 @@ seccomp_mode="$(awk -F':' '/^Seccomp:/{gsub(/[[:space:]]/,"",$2); print $2}' /pr # Base arguments (exactly what you requested + required safety flags) args=( - --remote-debugging-address=0.0.0.0 - --remote-debugging-port=9221 - --user-data-dir="${USER_DATA_DIR}" - --disable-dev-shm-usage - --no-first-run - --no-default-browser-check - --disable-background-networking - --disable-sync - --password-store=basic - "--simulate-outdated-no-au=Tue, 31 Dec 2099 23:59:59 GMT" - --start-maximized + --remote-debugging-address=0.0.0.0 + --remote-debugging-port=9221 + --user-data-dir="${USER_DATA_DIR}" + --disable-dev-shm-usage + --no-first-run + --no-default-browser-check + --disable-background-networking + --disable-sync + --password-store=basic + "--simulate-outdated-no-au=Tue, 31 Dec 2099 23:59:59 GMT" + --start-maximized ) # Container reality: sandbox usually breaks unless seccomp is fully disabled if [[ "${seccomp_mode}" != "0" ]]; then - args+=( --no-sandbox --test-type ) + args+=(--no-sandbox --test-type) fi # IMPORTANT: diff --git a/browserless_chrome/CHANGELOG.md b/browserless_chrome/CHANGELOG.md index 10487a2e41..540fea33ff 100644 --- a/browserless_chrome/CHANGELOG.md +++ b/browserless_chrome/CHANGELOG.md @@ -1,41 +1,41 @@ - + ## 2.56.7 (2026-09-12) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.56.3 (2026-09-05) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.56.0 (2026-08-22) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.55.4 (2026-08-13) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.55.3 (2026-08-08) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.55.2 (2026-08-01) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.55.0 (2026-07-16) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.54.2 (2026-07-04) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.54.1 (2026-06-23) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.54.0 (2026-06-20) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.52.2 (2026-06-17) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.52.1 (2026-06-13) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) - + ## 2.52.0 (2026-06-11) - Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases) ## 2.51.0 (2026-06-05) diff --git a/calibre/CHANGELOG.md b/calibre/CHANGELOG.md index 4b54e0eecd..6c196d010e 100644 --- a/calibre/CHANGELOG.md +++ b/calibre/CHANGELOG.md @@ -1,17 +1,17 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 9.14.0 (2026-08-29) - Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases) - + ## 9.13.0 (2026-08-08) - Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases) - + ## 9.12.0 (2026-08-01) - Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases) - + ## 9.11.0 (2026-07-04) - Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases) - + ## 9.10.0 (2026-06-27) - Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases) diff --git a/calibre/README.md b/calibre/README.md index 8ebe6ad969..56e7f2a40a 100644 --- a/calibre/README.md +++ b/calibre/README.md @@ -62,7 +62,7 @@ Please read the upstream container documentation for further info: https://githu ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/calibre_web/CHANGELOG.md b/calibre_web/CHANGELOG.md index 6ebb9f219e..37cdc0e85e 100644 --- a/calibre_web/CHANGELOG.md +++ b/calibre_web/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 0.6.27.4 (2026-09-04) - Fix: Kobo sync could not be enabled, failing with "Kepubify binary not found" even when the path was set by hand. The LinuxServer base image installs the converter as `/usr/bin/kepubify` with `curl -o`, which leaves it mode 0644 and gives it a name calibre-web does not accept : `binary_helper.py` only takes `kepubify-linux-64bit` or `kepubify-linux-32bit`, and only when `os.access(X_OK)` passes. The addon now makes the binary executable and publishes it as `/opt/kepubify/kepubify-linux-64bit`, the directory calibre-web's own autodetection already probes, so the path is filled in without any manual step (https://github.com/alexbelgium/hassio-addons/issues/3040) - Fix: on installs created before that change, calibre-web had already run its autodetection once, found nothing usable and stored an empty path, and it never retries. An empty path is now reset so calibre-web detects the converter itself at the next start. A path set by hand is left alone diff --git a/calibre_web/README.md b/calibre_web/README.md index 9de33bf06b..089cfb2bf2 100644 --- a/calibre_web/README.md +++ b/calibre_web/README.md @@ -62,7 +62,7 @@ Default password: admin123 ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | @@ -99,7 +99,7 @@ This addon supports mounting both local drives and remote SMB shares: ### Optional Calibre-Web features -Calibre-Web documents optional extras that a manual installation adds with `pip install calibreweb[metadata]` and similar. **You do not need to install anything here**: the LinuxServer base image this add-on builds on installs Calibre-Web's `requirements.txt` *and* its full `optional-requirements.txt` into the application's virtualenv, so the gdrive, gmail, goodreads, ldap, oauth, metadata, comics and kobo dependencies are all present already. Running `pip install calibreweb[...]` inside the container is not a supported way to enable them: it installs the PyPI distribution of Calibre-Web over an installation that already has those dependencies, and it can disturb the versions the base image pinned. It is also thrown away, because the Supervisor recreates the add-on container on restart. +Calibre-Web documents optional extras that a manual installation adds with `pip install calibreweb[metadata]` and similar. **You do not need to install anything here**: the LinuxServer base image this add-on builds on installs Calibre-Web's `requirements.txt` _and_ its full `optional-requirements.txt` into the application's virtualenv, so the gdrive, gmail, goodreads, ldap, oauth, metadata, comics and kobo dependencies are all present already. Running `pip install calibreweb[...]` inside the container is not a supported way to enable them: it installs the PyPI distribution of Calibre-Web over an installation that already has those dependencies, and it can disturb the versions the base image pinned. It is also thrown away, because the Supervisor recreates the add-on container on restart. Optional features are switched on in the Calibre-Web web interface, not in the add-on options, under `Admin` -> `Basic Configuration` -> `Feature Configuration` (for example `Enable Uploads`, `Enable Kobo sync`, `Use Goodreads`). diff --git a/calibre_web/rootfs/etc/cont-init.d/80-configuration.sh b/calibre_web/rootfs/etc/cont-init.d/80-configuration.sh index 1edd958d91..15c5b48acb 100755 --- a/calibre_web/rootfs/etc/cont-init.d/80-configuration.sh +++ b/calibre_web/rootfs/etc/cont-init.d/80-configuration.sh @@ -30,16 +30,16 @@ else # duplicates it can leave behind are entries calibre-web skips or already trusts. # The column only exists once calibre-web 0.6.27+ has migrated app.db and cont-init runs # before calibre-web, so a failure here is not fatal : the next start applies it. - trusted_ips_error=$(sqlite3 /config/app.db "update settings set config_reverse_proxy_trusted_ips='127.0.0.1,::1,::ffff:127.0.0.1,172.30.32.0/23,::ffff:172.30.32.0/119,'||coalesce(config_reverse_proxy_trusted_ips,'') where coalesce(config_reverse_proxy_trusted_ips,'') not like '%::ffff:172.30.32.0/119%'" 2>&1) || - bashio::log.warning "Could not set the ingress trusted ip list, it will be applied at next start (${trusted_ips_error})" + trusted_ips_error=$(sqlite3 /config/app.db "update settings set config_reverse_proxy_trusted_ips='127.0.0.1,::1,::ffff:127.0.0.1,172.30.32.0/23,::ffff:172.30.32.0/119,'||coalesce(config_reverse_proxy_trusted_ips,'') where coalesce(config_reverse_proxy_trusted_ips,'') not like '%::ffff:172.30.32.0/119%'" 2>&1) \ + || bashio::log.warning "Could not set the ingress trusted ip list, it will be applied at next start (${trusted_ips_error})" # Calibre-web autodetects kepubify only while this setting is still NULL. On every install # that predates the Dockerfile fix above, that detection already ran, found nothing usable and # stored an empty string, so it is never retried. Put an empty value back to NULL and # calibre-web detects /opt/kepubify itself when it starts, a few seconds after this runs. # A path the user set by hand is not empty and is left alone. - kepubify_error=$(sqlite3 /config/app.db "update settings set config_kepubifypath = NULL where config_kepubifypath = ''" 2>&1) || - bashio::log.warning "Could not reset the kepubify path, it will be applied at next start (${kepubify_error})" + kepubify_error=$(sqlite3 /config/app.db "update settings set config_kepubifypath = NULL where config_kepubifypath = ''" 2>&1) \ + || bashio::log.warning "Could not reset the kepubify path, it will be applied at next start (${kepubify_error})" fi bashio::log.info "Default username:password is admin:admin123" diff --git a/changedetection.io/CHANGELOG.md b/changedetection.io/CHANGELOG.md index aba5b8f251..e9ff7ea80c 100644 --- a/changedetection.io/CHANGELOG.md +++ b/changedetection.io/CHANGELOG.md @@ -1,10 +1,10 @@ - + ## 0.60.4 (2026-09-12) - Update to latest version from linuxserver/docker-changedetection.io (changelog : https://github.com/linuxserver/docker-changedetection.io/releases) - + ## 0.60.3 (2026-09-05) - Update to latest version from linuxserver/docker-changedetection.io (changelog : https://github.com/linuxserver/docker-changedetection.io/releases) - + ## 0.55.8 (2026-07-16) - Update to latest version from linuxserver/docker-changedetection.io (changelog : https://github.com/linuxserver/docker-changedetection.io/releases) diff --git a/changedetection.io/README.md b/changedetection.io/README.md index d3bcbbdcae..7b99b738d8 100644 --- a/changedetection.io/README.md +++ b/changedetection.io/README.md @@ -52,7 +52,7 @@ You can add a shortcut pointing to your Changedetection.io instance with the fol ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/changedetection.io/rootfs/etc/services.d/nginx/run b/changedetection.io/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/claude_desktop/CHANGELOG.md b/claude_desktop/CHANGELOG.md index 9e044a6ea0..099194fdb9 100644 --- a/claude_desktop/CHANGELOG.md +++ b/claude_desktop/CHANGELOG.md @@ -1,21 +1,21 @@ - + ## 07308545.8 (2026-09-12) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.4+claude1.52386.3 - + ## 07308545.7 (2026-09-12) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.4+claude1.49585.0 - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 07308545.6 (2026-09-05) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.3+claude1.40609.1 - + ## 07308545.5 (2026-08-29) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.2+claude1.37937.3 - + ## 07308545.4 (2026-08-22) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.2+claude1.32885.1 @@ -80,7 +80,7 @@ ## 07308545.1 (17-08-2026) - Minor bugs fixed - + ## 07308543.1 (17-08-2026) - Fix the "For your security, sign in again" prompt recurring on every restart again. The v1.37 `safeStorage` patch (`86-claude_safestorage.sh` / `claude-safestorage-patch.js`) only knew how @@ -103,11 +103,11 @@ ## 07308545 (2026-08-15) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.2+claude1.30096.1 - + ## 07308544 (2026-08-13) - Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases) - Upstream tag : v3.2.2+claude1.28929.0 - + ## 07308543 (2026-08-08) - Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases) - Upstream tag : debiantrixie-version-07308543 @@ -194,10 +194,10 @@ ## 2026.08.02 (02-08-2026) - Minor bugs fixed - + ## ubunturesolute-version-3a10bef7 (2026-08-01) - Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases) - + ## kali-version-e963b19b (2026-08-01) - Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases) ## 1.37 (28-07-2026) @@ -208,7 +208,7 @@ - **`gnome-keyring` stays out of the image.** Re-adding it would reintroduce the first-boot keyring password prompt that blocks Claude Desktop from launching at all. This route needs no keyring, no D-Bus Secret Service, no daemon and no password, so no prompt can appear. The trade-off is unchanged from v1.35 and is inherent to the `basic` backend: its key is fixed rather than gated by a keyring, so any process that can read the persistent `$HOME/.config/Claude` profile can recover the stored credentials. - **One-time step after upgrading.** The previously stored session is already stale, so a single sign-in is still needed once after this update; it then persists across restarts. - + ## 1.36.4 (28-07-2026) - Fix Selkies dying with a Rust `RuntimeDirNotSet` unwrap panic just after `Data WebSocket Server listening on port 8081`, and the data websocket then being proxied to the wrong port. Upstream relies on s6-rc ordering: `init-selkies-config` publishes `XDG_RUNTIME_DIR` and `CUSTOM_WS_PORT` into the s6 envdir and `svc-selkies` starts afterwards. The add-on entrypoint replaces s6-overlay and starts every `s6-rc.d` run script in parallel with no dependency graph, so Selkies can snapshot the envdir before that oneshot has written to it -- which is why it bound port 8081 (its own default) instead of the 8082 nginx proxies to, and why its Wayland compositor found no runtime directory to bind a socket in. `20-folders.sh` now exports both variables inside each run script, where no start ordering can lose them, and corrects the base image's `$HOME/.XDG` override where that write happens instead of appending a correction after the `exit 0` that the oneshot-tolerance block adds -- which meant the correction never ran on any boot after the first. @@ -219,7 +219,7 @@ ## 1.36.2 (28-07-2026) - Minor bugs fixed - + ## 1.36.1 (27-07-2026) - Fix the Codex CLI install failing on every boot with `Verified Codex installation failed; Codex is unavailable this boot`, leaving `install_codex_cli` permanently non-functional. The download, its SHA-256 verification, and the extraction all succeeded; the chain broke at the final step, which validates the candidate binary by running `--version` as the `abc` runtime user. `mktemp -d` creates its directory `0700 root:root`, and `abc` cannot traverse a root-only directory, so executing the staged binary failed with `unable to exec: Permission denied` (exit 126) before it could be moved into place. Reproduced and fixed by making the staging directory traversable (`chmod 0755`) immediately after `mktemp`; verified on a live add-on container, where the same probe goes from exit 126 to success once the mode is widened. Nothing secret is staged there — the public release archive and the extracted binary, both world-readable upstream artifacts — and the existing `cleanup()` trap still removes the directory on exit. The validation deliberately keeps running as `abc` rather than root, so the binary is exercised as the identity that will actually run it. @@ -249,10 +249,10 @@ ## 1.33 (22-07-2026) - Add cowork virtualization support: `qemu-system-x86` and `ovmf` (Bookworm main, installed via apt) plus `virtiofsd` for sharing the workspace into the sandbox microVM. `virtiofsd` is only packaged for Debian trixie/sid, not Bookworm or bookworm-backports, and its trixie `.deb` links a newer GLIBC than this add-on's Bookworm runtime — so it now gets built from the pinned crates.io release (`1.14.0`) in a dedicated `virtiofsd-builder` stage, the same GLIBC-safe pattern already used for `rtk` and `tokensave`. Its build deps (`libseccomp-dev`, `libcap-ng-dev`, `pkg-config`, `clang`, `libclang-dev`) live only in that builder stage; only the runtime shared libs (`libseccomp2`, `libcap-ng0`) ship in the final image. The built binary is validated with `--version` at build time alongside `rtk`/`tokensave`, so a GLIBC/ABI mismatch fails the image build instead of surfacing at container start. Docker itself is deliberately not installed: this base image already ships Docker-in-Docker (`docker-ce`/`containerd.io` from Docker's own apt repo, started via the pre-existing `START_DOCKER` env var) — an initial attempt to also `apt-get install docker.io` broke the build, since Debian's package pulls in `containerd`/`runc`, which apt refuses to install alongside the base image's already-installed `containerd.io` (`Conflicts`). - + ## ubunturesolute-version-8208e985 (2026-07-21) - Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases) - + ## kali-version-9ad48e7a (2026-07-18) - Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases) ## 1.32 (17-07-2026) diff --git a/claude_desktop/rootfs/defaults/autostart b/claude_desktop/rootfs/defaults/autostart index aa1fab8596..7b337c2134 100644 --- a/claude_desktop/rootfs/defaults/autostart +++ b/claude_desktop/rootfs/defaults/autostart @@ -55,9 +55,9 @@ # shared memory into ordinary files for no benefit. If the size cannot be determined, keep the # flag — the crash it prevents is worse than the overhead it costs. SHM_FLAGS="--disable-dev-shm-usage" -SHM_KB="$(df -k /dev/shm 2> /dev/null | awk 'NR==2 {print $2}')" +SHM_KB="$(df -k /dev/shm 2>/dev/null | awk 'NR==2 {print $2}')" if [ -n "$SHM_KB" ] && [ "$SHM_KB" -ge 262144 ]; then - SHM_FLAGS="" + SHM_FLAGS="" fi # SHM_FLAGS must stay unquoted so it expands to a separate argument, or to nothing at all. diff --git a/claude_desktop/rootfs/etc/cont-init.d/81-codex_cli.sh b/claude_desktop/rootfs/etc/cont-init.d/81-codex_cli.sh index 988483dfd6..dba942085c 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/81-codex_cli.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/81-codex_cli.sh @@ -141,7 +141,7 @@ release_info="" if curl -fsSL --retry 3 --retry-delay 2 --connect-timeout 10 --max-time 30 \ -o "$release_metadata" "$CODEX_RELEASE_API"; then release_info="$( - CODEX_ASSET="$CODEX_ASSET" python3 - "$release_metadata" <<'PY' 2> /dev/null || true + CODEX_ASSET="$CODEX_ASSET" python3 - "$release_metadata" << 'PY' 2> /dev/null || true import json import os import re @@ -249,7 +249,7 @@ fi { printf '#!/usr/bin/env bash\n' printf 'CODEX_REAL=%q\n' "$CODEX_REAL" - cat <<'SH' + cat << 'SH' RUNTIME_HOME="$(getent passwd abc | cut -d: -f6)" if [ -z "$RUNTIME_HOME" ]; then echo "codex: unable to resolve the abc runtime home" >&2 @@ -338,8 +338,7 @@ ln -sfn "$CODEX_BIN" "$CODEX_LINK" CODEX_SANDBOX_MODE="$(bashio::config 'codex_sandbox_mode' 'danger-full-access')" run_as_runtime_user mkdir -p "$RUNTIME_HOME/.codex" CODEX_SANDBOX_MODE="$CODEX_SANDBOX_MODE" RUNTIME_HOME="$RUNTIME_HOME" \ - run_as_runtime_user python3 - <<'PY' \ - || bashio::log.warning "Unable to update the managed Codex configuration block" + run_as_runtime_user python3 - << 'PY' || bashio::log.warning "Unable to update the managed Codex configuration block" import os import re import tomllib diff --git a/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh index b27aae7efa..3bb664ec36 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh @@ -30,7 +30,7 @@ manage_claude_md_block() { fi elif [ -f "$CLAUDE_MD" ] && grep -qF "$begin" "$CLAUDE_MD"; then bashio::log.info "Removing ${name} guidance from CLAUDE.md" - CLAUDE_MD="$CLAUDE_MD" BLOCK_NAME="$name" python3 - <<'PY' || bashio::log.warning "Unable to remove the ${name} guidance automatically" + CLAUDE_MD="$CLAUDE_MD" BLOCK_NAME="$name" python3 - << 'PY' || bashio::log.warning "Unable to remove the ${name} guidance automatically" import os import re from pathlib import Path @@ -58,7 +58,7 @@ PY manage_settings_hook() { # manage_settings_hook HOOK_EVENT="$1" HOOK_MATCHER="$2" HOOK_COMMAND="$3" HOOK_ACTION="$4" \ - python3 - <<'PY' || bashio::log.warning "Unable to update the $1 hook for '$3'" + python3 - << 'PY' || bashio::log.warning "Unable to update the $1 hook for '$3'" import json import os from pathlib import Path @@ -389,14 +389,14 @@ bashio::log.info "MCP servers for Claude Desktop: ${MCP_SERVERS_DESKTOP}" bashio::log.info "MCP servers for Claude Code: ${MCP_SERVERS_CODE}" HEADROOM_ENABLED="$HEADROOM_ENABLED" HEADROOM_BIN="$(command -v headroom || echo headroom)" \ - HEADROOM_HF_HOME="${HOME}/.headroom/hf" \ - TOKENSAVE_ENABLED="$TOKENSAVE_ENABLED" TOKENSAVE_BIN="$(command -v tokensave || echo tokensave)" \ - CODEX_ENABLED="$CODEX_ENABLED" CODEX_BIN="$CODEX_BIN" CODEX_SANDBOX_MODE="$CODEX_SANDBOX_MODE" \ - HA_MCP_ENABLED="$HA_MCP_ENABLED" HA_MCP_URL="$HA_MCP_URL" HA_MCP_TOKEN="$HA_MCP_TOKEN" \ - MCP_PROXY_BIN="$(command -v mcp-proxy || echo mcp-proxy)" \ - MCP_SERVERS_DESKTOP="$MCP_SERVERS_DESKTOP" MCP_SERVERS_CODE="$MCP_SERVERS_CODE" \ - CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" CLAUDE_CODE_CONFIG="$CLAUDE_CODE_CONFIG" \ - python3 - <<'PY' || bashio::log.warning "Unable to update the MCP server registrations automatically" +HEADROOM_HF_HOME="${HOME}/.headroom/hf" \ +TOKENSAVE_ENABLED="$TOKENSAVE_ENABLED" TOKENSAVE_BIN="$(command -v tokensave || echo tokensave)" \ +CODEX_ENABLED="$CODEX_ENABLED" CODEX_BIN="$CODEX_BIN" CODEX_SANDBOX_MODE="$CODEX_SANDBOX_MODE" \ +HA_MCP_ENABLED="$HA_MCP_ENABLED" HA_MCP_URL="$HA_MCP_URL" HA_MCP_TOKEN="$HA_MCP_TOKEN" \ +MCP_PROXY_BIN="$(command -v mcp-proxy || echo mcp-proxy)" \ +MCP_SERVERS_DESKTOP="$MCP_SERVERS_DESKTOP" MCP_SERVERS_CODE="$MCP_SERVERS_CODE" \ +CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" CLAUDE_CODE_CONFIG="$CLAUDE_CODE_CONFIG" \ + python3 - << 'PY' || bashio::log.warning "Unable to update the MCP server registrations automatically" import json import os from pathlib import Path @@ -607,7 +607,7 @@ PY # Guide Claude to actually use the Headroom compression tools so the MCP integration produces # real savings when transparent proxying is unavailable. if $HEADROOM_ENABLED; then - manage_claude_md_block headroom add <<'MD' + manage_claude_md_block headroom add << 'MD' ## Headroom context compression A local Headroom proxy (127.0.0.1:8787) backs the `headroom` MCP tools. To save context tokens: @@ -636,7 +636,7 @@ if $HEADROOM_ENABLED && bashio::config.true 'headroom_wrap_claude_code'; then else HEADROOM_ROUTE_ACTION="remove" fi -HEADROOM_ROUTE_ACTION="$HEADROOM_ROUTE_ACTION" python3 - <<'PY' || bashio::log.warning "Unable to manage the Claude Code proxy routing env" +HEADROOM_ROUTE_ACTION="$HEADROOM_ROUTE_ACTION" python3 - << 'PY' || bashio::log.warning "Unable to manage the Claude Code proxy routing env" import json import os from pathlib import Path @@ -703,7 +703,7 @@ manage_settings_hook PostToolUse "Bash|Grep|Glob|WebFetch" "$HEADROOM_HOOK_CMD" # Tell Claude Code that it can configure Home Assistant over the Core API via the shipped # `ha-cli` helper (no /config filesystem mount needed). if bashio::config.true 'enable_ha_api_helper'; then - manage_claude_md_block ha-api-helper add <<'MD' + manage_claude_md_block ha-api-helper add << 'MD' ## Configuring Home Assistant You can configure this Home Assistant instance through its Core API using the `ha-cli` @@ -731,7 +731,7 @@ fi # Registering the MCP server is not enough on its own: without guidance the model rarely reaches # for a second agent, the same gap the Headroom block above exists to close. if $CODEX_ENABLED; then - manage_claude_md_block codex add <<'MD' + manage_claude_md_block codex add << 'MD' ## Delegating to ChatGPT Codex The `codex` MCP server runs OpenAI's Codex agent locally, signed in with the user's ChatGPT diff --git a/claude_desktop/rootfs/etc/cont-init.d/83-claude_permissions.sh b/claude_desktop/rootfs/etc/cont-init.d/83-claude_permissions.sh index ecb016a544..2b94e1d5d3 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/83-claude_permissions.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/83-claude_permissions.sh @@ -19,7 +19,7 @@ esac # only while it still holds one of those managed values — a defaultMode the user set by hand # is never deleted. Ownership of the written file is reconciled by 84-claude_runtime_ownership.sh. mkdir -p "$(dirname "$SETTINGS_PATH")" -PERMISSION_MODE="$PERMISSION_MODE" SETTINGS_PATH="$SETTINGS_PATH" python3 - <<'PY' +PERMISSION_MODE="$PERMISSION_MODE" SETTINGS_PATH="$SETTINGS_PATH" python3 - << 'PY' import json import os from pathlib import Path diff --git a/claude_desktop/rootfs/etc/cont-init.d/86-claude_safestorage.sh b/claude_desktop/rootfs/etc/cont-init.d/86-claude_safestorage.sh index 19306a1160..3dfffcb68e 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/86-claude_safestorage.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/86-claude_safestorage.sh @@ -36,7 +36,7 @@ fi # archive stays unpatched, so every later boot would retry under a new pid and strand another # copy until the container runs out of space. cleanup_tmp() { - find "$(dirname "$ASAR")" -maxdepth 1 -name ".$(basename "$ASAR").addon-tmp.*" -delete 2>/dev/null || true + find "$(dirname "$ASAR")" -maxdepth 1 -name ".$(basename "$ASAR").addon-tmp.*" -delete 2> /dev/null || true } if output=$(timeout 120 node "$PATCHER" "$ASAR" 2>&1); then diff --git a/claude_desktop/rootfs/usr/local/bin/claude-tools-doctor.sh b/claude_desktop/rootfs/usr/local/bin/claude-tools-doctor.sh index 93aa5551c2..1d8c66b9ae 100755 --- a/claude_desktop/rootfs/usr/local/bin/claude-tools-doctor.sh +++ b/claude_desktop/rootfs/usr/local/bin/claude-tools-doctor.sh @@ -45,7 +45,7 @@ if [ "$(bashio::config 'permission_mode')" = "bypass" ]; then fi section "Claude Code permission state" -RUNTIME_HOME="$RUNTIME_HOME" python3 - <<'PY' +RUNTIME_HOME="$RUNTIME_HOME" python3 - << 'PY' import json import os from pathlib import Path @@ -66,7 +66,7 @@ else: PY section "MCP registrations (environment values redacted)" -RUNTIME_HOME="$RUNTIME_HOME" python3 - <<'PY' +RUNTIME_HOME="$RUNTIME_HOME" python3 - << 'PY' import json import os from pathlib import Path @@ -104,7 +104,7 @@ for path in paths: PY section "Claude Code hooks" -RUNTIME_HOME="$RUNTIME_HOME" python3 - <<'PY' +RUNTIME_HOME="$RUNTIME_HOME" python3 - << 'PY' import json import os from pathlib import Path diff --git a/claude_desktop/rootfs/usr/local/bin/codex-login b/claude_desktop/rootfs/usr/local/bin/codex-login old mode 100755 new mode 100644 diff --git a/claude_desktop/rootfs/usr/local/bin/ha-cli b/claude_desktop/rootfs/usr/local/bin/ha-cli old mode 100755 new mode 100644 index 6d521ef256..bc2962141d --- a/claude_desktop/rootfs/usr/local/bin/ha-cli +++ b/claude_desktop/rootfs/usr/local/bin/ha-cli @@ -96,7 +96,7 @@ def resolve_endpoint(): def _url(base, path): path = path.lstrip("/") if path.startswith("api/"): - path = path[len("api/"):] + path = path[len("api/") :] return base.rstrip("/") + "/" + path @@ -133,7 +133,9 @@ def rest(method, base, token, path, body=None): if body is not None: data = json.dumps(body).encode("utf-8") headers["Content-Type"] = "application/json" - req = urllib.request.Request(_url(base, path), data=data, method=method, headers=headers) + req = urllib.request.Request( + _url(base, path), data=data, method=method, headers=headers + ) try: with urllib.request.urlopen(req, timeout=30) as resp: text = resp.read().decode("utf-8") @@ -141,7 +143,9 @@ def rest(method, base, token, path, body=None): detail = exc.read().decode("utf-8", "replace").strip() sys.exit(f"ha-cli: HTTP {exc.code} {exc.reason} on {method} {path}\n{detail}") except urllib.error.URLError as exc: - sys.exit(f"ha-cli: cannot reach Home Assistant ({exc.reason}) on {method} {path}") + sys.exit( + f"ha-cli: cannot reach Home Assistant ({exc.reason}) on {method} {path}" + ) try: return json.loads(text) if text.strip() else None except ValueError: @@ -214,7 +218,9 @@ def main(argv): sys.exit("usage: ha-cli call [BODY]") domain, service = args[0].split(".", 1) body = _read_body(args[1]) if len(args) > 1 else None - _print(rest("POST", rest_base, token, f"services/{domain}/{service}", body or {})) + _print( + rest("POST", rest_base, token, f"services/{domain}/{service}", body or {}) + ) elif cmd == "states": path = f"states/{args[0]}" if args else "states" _print(rest("GET", rest_base, token, path)) diff --git a/claude_desktop/rootfs/usr/local/bin/headroom-posttooluse-compress.py b/claude_desktop/rootfs/usr/local/bin/headroom-posttooluse-compress.py old mode 100755 new mode 100644 index 987bef0114..1e84645814 --- a/claude_desktop/rootfs/usr/local/bin/headroom-posttooluse-compress.py +++ b/claude_desktop/rootfs/usr/local/bin/headroom-posttooluse-compress.py @@ -76,7 +76,11 @@ def main() -> int: # Find big string/array fields before paying the headroom import cost. def is_string_array(value): - return isinstance(value, list) and len(value) > ARRAY_KEEP and all(isinstance(v, str) for v in value) + return ( + isinstance(value, list) + and len(value) > ARRAY_KEEP + and all(isinstance(v, str) for v in value) + ) if isinstance(response, str): string_candidates = ["__whole__"] if len(response) >= MIN_CHARS else [] @@ -85,10 +89,14 @@ def main() -> int: string_candidates = [ key for key, value in response.items() - if key not in SKIP_KEYS and isinstance(value, str) and len(value) >= MIN_CHARS + if key not in SKIP_KEYS + and isinstance(value, str) + and len(value) >= MIN_CHARS ] array_candidates = [ - key for key, value in response.items() if key not in SKIP_KEYS and is_string_array(value) + key + for key, value in response.items() + if key not in SKIP_KEYS and is_string_array(value) ] else: string_candidates = [] diff --git a/cleanuparr/CHANGELOG.md b/cleanuparr/CHANGELOG.md index 32183418a8..426e1e6236 100644 --- a/cleanuparr/CHANGELOG.md +++ b/cleanuparr/CHANGELOG.md @@ -1,18 +1,18 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - Fix persistent data directory: the entrypoint now writes to the `/config` mount (the container-side path of the `app_config` map) instead of an unmounted `/app_configs/cleanuparr` path, so Cleanuparr's data survives container recreation. - + ## 2.10.5 (2026-08-13) - Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases) - + ## 2.10.3 (2026-08-08) - Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases) - + ## 2.10.2 (2026-08-01) - Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases) - + ## 2.9.16 (2026-07-11) - Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases) - + ## 2.9.14 (2026-06-20) - Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases) diff --git a/cleanuparr/README.md b/cleanuparr/README.md index 9637f0adec..40e11128b5 100644 --- a/cleanuparr/README.md +++ b/cleanuparr/README.md @@ -23,7 +23,7 @@ Integrations supported: ## Configuration | Option | Description | -|--------|-------------| +| -------- | ------------- | | `TZ` | Timezone (e.g. `Europe/Paris`). Defaults to `Europe/London`. | | `PUID` | User ID to run the process as. Defaults to `0` (root). | | `PGID` | Group ID to run the process as. Defaults to `0` (root). | diff --git a/cleanuparr/config.yaml b/cleanuparr/config.yaml index c24532f1c2..282434a037 100644 --- a/cleanuparr/config.yaml +++ b/cleanuparr/config.yaml @@ -1,8 +1,7 @@ arch: - aarch64 - amd64 -description: - Automatically removes stuck and unwanted downloads from your *arr and download clients +description: Automatically removes stuck and unwanted downloads from your *arr and download clients devices: - /dev/dri - /dev/dri/card0 diff --git a/cleanuparr/rootfs/ha_entrypoint.sh b/cleanuparr/rootfs/ha_entrypoint.sh index 34b3ab62ec..7b621bc970 100755 --- a/cleanuparr/rootfs/ha_entrypoint.sh +++ b/cleanuparr/rootfs/ha_entrypoint.sh @@ -33,7 +33,7 @@ mkdir -p "$HA_DATA_DIR" # Symlink /app/config → HA persistent storage # The .NET app uses /app/config, NOT /config at the filesystem root if [ -d /app/config ] && [ ! -L /app/config ]; then - cp -rn /app/config/. "$HA_DATA_DIR/" 2>/dev/null || true + cp -rn /app/config/. "$HA_DATA_DIR/" 2> /dev/null || true rm -rf /app/config fi ln -sfn "$HA_DATA_DIR" /app/config diff --git a/cloudcommander/CHANGELOG.md b/cloudcommander/CHANGELOG.md index f673299eb5..821e42607a 100644 --- a/cloudcommander/CHANGELOG.md +++ b/cloudcommander/CHANGELOG.md @@ -1,14 +1,14 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 19.20.5 (2026-09-05) - Update to latest version from coderaiser/cloudcmd (changelog : https://github.com/coderaiser/cloudcmd/releases) - + ## 19.20.4 (2026-08-29) - Update to latest version from coderaiser/cloudcmd (changelog : https://github.com/coderaiser/cloudcmd/releases) - + ## 19.20.0 (2026-08-08) - Update to latest version from coderaiser/cloudcmd (changelog : https://github.com/coderaiser/cloudcmd/releases) - + ## 19.19.1 (2026-06-17) - Update to latest version from coderaiser/cloudcmd (changelog : https://github.com/coderaiser/cloudcmd/releases) diff --git a/cloudcommander/README.md b/cloudcommander/README.md index 218bbe19f9..2bc851e07e 100644 --- a/cloudcommander/README.md +++ b/cloudcommander/README.md @@ -40,7 +40,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CUSTOM_OPTIONS` | str | | Custom CLI options (e.g., `--name Homeassistant`) | | `DROPBOX_TOKEN` | str | | Dropbox integration token (see https://cloudcmd.io/) | | `localdisks` | str | | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) | diff --git a/cloudcommander/rootfs/etc/cont-init.d/99-run.sh b/cloudcommander/rootfs/etc/cont-init.d/99-run.sh index 82831dc125..1d45195385 100755 --- a/cloudcommander/rootfs/etc/cont-init.d/99-run.sh +++ b/cloudcommander/rootfs/etc/cont-init.d/99-run.sh @@ -103,7 +103,7 @@ for candidate in "${cloudcmd_candidates[@]}"; do fi done -if [ -z "$CLOUDCMD_BIN" ] && command -v cloudcmd >/dev/null 2>&1; then +if [ -z "$CLOUDCMD_BIN" ] && command -v cloudcmd > /dev/null 2>&1; then CLOUDCMD_BIN=$(command -v cloudcmd) fi diff --git a/codex/CHANGELOG.md b/codex/CHANGELOG.md index ae25c4ccb7..758f8563a4 100644 --- a/codex/CHANGELOG.md +++ b/codex/CHANGELOG.md @@ -1,41 +1,41 @@ - + ## 2.3.0 (2026-09-12) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.2.11 (2026-08-29) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.8 (2026-08-22) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.6 (2026-08-08) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.5 (2026-08-02) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.4 (2026-08-01) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.2 (2026-07-25) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.2.0 (2026-07-16) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.1.2 (2026-07-11) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.1.0 (2026-07-04) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.0.3 (2026-06-23) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 2.0.0 (2026-06-20) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) - + ## 1.12.8 (2026-06-17) - Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases) diff --git a/codex/README.md b/codex/README.md index 4aed0f8bfc..e4042572bf 100644 --- a/codex/README.md +++ b/codex/README.md @@ -62,7 +62,7 @@ You can place the user folder from the theme/skeleton in /share/codex/www/user, ## Options | Option | Description | Default | Example | -|--------|-------------|---------|---------| +| -------- | ------------- | --------- | --------- | | `PGID` | Group ID for file permissions | `0` | `1000` | | `PUID` | User ID for file permissions | `0` | `1000` | | `TZ` | Timezone in long format | - | `America/Los_Angeles` | diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index d762f0b21b..1ea832d8e9 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -1,11 +1,11 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 26.04.3.2.1 (2026-09-05) - Update to latest version from collabora/code - + ## 26.04.3.1.1 (2026-08-13) - Update to latest version from collabora/code - + ## 26.04.2.4.1 (2026-07-26) - Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. collabora/code stays the tracked upstream image in build.json, but is now a build stage whose payload is copied onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh - build.json names the architecture explicitly again (`collabora/code:latest-amd64` and `collabora/code:latest-arm64`). The builder never passes `--platform`, so the tag is the only thing that decides which binaries land in the addon @@ -20,16 +20,16 @@ - Added `aliasgroup2` and `aliasgroup3` for additional Nextcloud servers - `cert_domain` is now a string (it is a certificate common name) and is passed to Collabora - Documented the above, and corrected the README which asked for two backslashes where Collabora expects one - + ## 1.3.0 (2026-07-16) - Update to latest version from CollaboraOnline/online (changelog : https://github.com/CollaboraOnline/online/releases) - + ## 1.2.2 (2026-07-04) - Update to latest version from CollaboraOnline/online (changelog : https://github.com/CollaboraOnline/online/releases) - + ## 1.2.1 (2026-06-27) - Update to latest version from CollaboraOnline/online (changelog : https://github.com/CollaboraOnline/online/releases) - + ## 1.2.0 (2026-06-23) - Update to latest version from CollaboraOnline/online (changelog : https://github.com/CollaboraOnline/online/releases) diff --git a/collabora/README.md b/collabora/README.md index 50bbd122a6..9e77ac070b 100644 --- a/collabora/README.md +++ b/collabora/README.md @@ -51,7 +51,7 @@ Webui can be found at `https://homeassistant:9980/browser/dist/admin/admin.html` ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `aliasgroup1` | str | | External address of the **Nextcloud** server allowed to use this Collabora (e.g. `https://nextcloud_domain\.com:443`) | | `aliasgroup2` | str | | A second Nextcloud server, same format as `aliasgroup1` | | `aliasgroup3` | str | | A third Nextcloud server, same format as `aliasgroup1` | diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index 95b0d33240..35abfcd2c9 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -27,9 +27,9 @@ normalise_wopi_host() { # server_name is a literal "hostname[:port]", not a regex and not a URL normalise_server_name() { local value="$1" - value="${value//\\/}" # never escaped, drop backslashes if any were copied over - value="${value#*://}" # strip the scheme - value="${value%%/*}" # strip any path + value="${value//\\/}" # never escaped, drop backslashes if any were copied over + value="${value#*://}" # strip the scheme + value="${value%%/*}" # strip any path printf '%s' "$value" } diff --git a/comicarr/CHANGELOG.md b/comicarr/CHANGELOG.md index ce8b390d1b..553a1a6e52 100644 --- a/comicarr/CHANGELOG.md +++ b/comicarr/CHANGELOG.md @@ -1,10 +1,10 @@ - + ## 0.38.15 (2026-09-12) - Update to latest version from frankieramirez/comicarr (changelog : https://github.com/frankieramirez/comicarr/releases) - + ## 0.38.7 (2026-09-05) - Update to latest version from frankieramirez/comicarr (changelog : https://github.com/frankieramirez/comicarr/releases) - + ## 0.38.2 (2026-08-29) - Update to latest version from frankieramirez/comicarr (changelog : https://github.com/frankieramirez/comicarr/releases) ## 0.34.0 (20-08-2026) diff --git a/comicarr/README.md b/comicarr/README.md index 554004500c..ea6dcadd15 100644 --- a/comicarr/README.md +++ b/comicarr/README.md @@ -30,7 +30,7 @@ database. ## Configuration | Option | Description | -|--------|-------------| +| -------- | ------------- | | `PUID` / `PGID` | Ownership applied to the add-on configuration directory. Defaults to `0` (root). See the note below before changing it. | | `TZ` | Timezone, e.g. `Europe/Paris`. | | `localdisks` | Local disks to mount, e.g. `sda1` or a disk label. | diff --git a/comicarr/rootfs/etc/services.d/comicarr/run b/comicarr/rootfs/etc/services.d/comicarr/run old mode 100644 new mode 100755 diff --git a/comicarr/rootfs/etc/services.d/nginx/run b/comicarr/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/comixed/README.md b/comixed/README.md index b6b16725c2..826a45a43b 100644 --- a/comixed/README.md +++ b/comixed/README.md @@ -60,7 +60,7 @@ Initial setup requires creating an admin account through the web interface. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `TZ` | str | | Timezone (e.g., `Europe/London`) | | `localdisks` | str | | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) | | `networkdisks` | str | | SMB shares to mount (e.g., `//SERVER/SHARE`) | diff --git a/elasticsearch/CHANGELOG.md b/elasticsearch/CHANGELOG.md index ffda699463..a706bd5a68 100644 --- a/elasticsearch/CHANGELOG.md +++ b/elasticsearch/CHANGELOG.md @@ -1,12 +1,12 @@ - + ## 8.19.21 (2026-09-05) - Update to latest version from elastic/elasticsearch (changelog : https://github.com/elastic/elasticsearch/releases) - + ## 8.19.20 (2026-08-13) - Update to latest version from elastic/elasticsearch (changelog : https://github.com/elastic/elasticsearch/releases) ## 8.19.19.2 (21-07-2026) - Minor bugs fixed - + ## 8.19.19 (2026-07-21) - Update to latest version from elastic/elasticsearch (changelog : https://github.com/elastic/elasticsearch/releases) ## 8.19.18-3 (14-07-2026) diff --git a/elasticsearch/rootfs/usr/local/bin/addon-init.sh b/elasticsearch/rootfs/usr/local/bin/addon-init.sh index eae765ed58..4e6e324de2 100755 --- a/elasticsearch/rootfs/usr/local/bin/addon-init.sh +++ b/elasticsearch/rootfs/usr/local/bin/addon-init.sh @@ -46,17 +46,17 @@ fi # 1 Export user env_vars # ############################ -if [ -f "$OPTIONS_JSON" ] && command -v jq >/dev/null 2>&1; then +if [ -f "$OPTIONS_JSON" ] && command -v jq > /dev/null 2>&1; then while IFS= read -r pair; do - name=$(jq -r '.name // empty' <<<"$pair") - value=$(jq -r '.value // empty' <<<"$pair") + name=$(jq -r '.name // empty' <<< "$pair") + value=$(jq -r '.value // empty' <<< "$pair") if [[ $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then echo "Setting env variable from options: $name" export "$name"="$value" elif [ -n "$name" ]; then echo "WARNING: ignoring invalid env_vars name: $name" fi - done < <(jq -c '.env_vars[]?' "$OPTIONS_JSON" 2>/dev/null || true) + done < <(jq -c '.env_vars[]?' "$OPTIONS_JSON" 2> /dev/null || true) fi ################################## @@ -82,7 +82,7 @@ data_version="" if [ -f "$VERSION_MARKER" ]; then data_version="$(head -n 1 "$VERSION_MARKER" | tr -cd '0-9.')" -elif [ -d "$PERSISTENT_HOME/data" ] && [ -n "$(ls -A "$PERSISTENT_HOME/data" 2>/dev/null)" ]; then +elif [ -d "$PERSISTENT_HOME/data" ] && [ -n "$(ls -A "$PERSISTENT_HOME/data" 2> /dev/null)" ]; then # Existing data without a marker: only 7.17.9 was ever shipped before markers data_version="7.17.9" fi @@ -128,7 +128,7 @@ mkdir -p "$PERSISTENT_HOME" for dir in "data" "config"; do if [ ! -L "$ES_HOME/$dir" ]; then if [ -d "$ES_HOME/$dir" ]; then - cp -rn "$ES_HOME/$dir" "$PERSISTENT_HOME" 2>/dev/null || true + cp -rn "$ES_HOME/$dir" "$PERSISTENT_HOME" 2> /dev/null || true rm -rf "${ES_HOME:?}/$dir" fi mkdir -p "$PERSISTENT_HOME/$dir" @@ -139,7 +139,7 @@ done # Make the persisted files usable by the elasticsearch user (uid 1000), # which the official entrypoint drops to when started as root if [ "$(id -u)" -eq 0 ]; then - chown -R 1000:0 "$PERSISTENT_HOME/data" "$PERSISTENT_HOME/config" 2>/dev/null || true + chown -R 1000:0 "$PERSISTENT_HOME/data" "$PERSISTENT_HOME/config" 2> /dev/null || true fi echo "Data location: $PERSISTENT_HOME (persistent). Please wait while elasticsearch starts..." @@ -156,9 +156,9 @@ if [ "$data_version" != "$current_version" ]; then # Check the HTTP status directly instead of curl -f: a 401 means # Elasticsearch is up and answering (security just requires # auth), so it must count as healthy too, not as a failure. - status=$(curl -A "HealthCheck: Docker/1.0" -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:9200" 2>/dev/null || true) + status=$(curl -A "HealthCheck: Docker/1.0" -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:9200" 2> /dev/null || true) if [ "$status" = "200" ] || [ "$status" = "401" ]; then - echo "$current_version" >"$VERSION_MARKER" + echo "$current_version" > "$VERSION_MARKER" echo "Elasticsearch $current_version started successfully; data version recorded." exit 0 fi diff --git a/emby/CHANGELOG.md b/emby/CHANGELOG.md index e325a2e721..e99658de6f 100644 --- a/emby/CHANGELOG.md +++ b/emby/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 4.10.0.40 (2026-09-12) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. diff --git a/emby/README.md b/emby/README.md index 69f6461031..1f569f0126 100644 --- a/emby/README.md +++ b/emby/README.md @@ -43,7 +43,7 @@ Webui can be found at `:8096`, or within Home Assistant through Ingress ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/emby/rootfs/etc/services.d/nginx/run b/emby/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/emby_beta/CHANGELOG.md b/emby_beta/CHANGELOG.md index 24e5f503ce..61a55437aa 100644 --- a/emby_beta/CHANGELOG.md +++ b/emby_beta/CHANGELOG.md @@ -1,47 +1,47 @@ - + ## 4.10.0.40 (2026-09-12) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 4.10.0.30 (2026-09-05) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.29 (2026-08-29) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.28 (2026-08-29) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.26 (2026-08-22) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.25 (2026-08-15) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.24 (2026-08-13) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.22 (2026-08-01) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.21 (2026-08-01) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.20 (2026-07-18) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.19 (2026-07-13) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.18 (2026-07-11) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.17 (2026-07-04) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.16 (2026-06-27) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) - + ## 4.10.0.15 (2026-06-20) - Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases) ## 4.10.0.14 (2026-06-05) diff --git a/enedisgateway2mqtt/README.md b/enedisgateway2mqtt/README.md index 00c408d63e..a674127a88 100644 --- a/enedisgateway2mqtt/README.md +++ b/enedisgateway2mqtt/README.md @@ -48,7 +48,7 @@ Initial setup requires starting the addon once to initialize configuration templ ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CONFIG_LOCATION` | str | `/config/myelectricaldata/config.yaml` | Path to configuration file | | `TZ` | str | `Europe/Paris` | Timezone (e.g., `Europe/London`) | | `mqtt_autodiscover` | bool | `true` | Enable MQTT autodiscovery | diff --git a/enedisgateway2mqtt_dev/README.md b/enedisgateway2mqtt_dev/README.md index 6169e58538..b0897a9415 100644 --- a/enedisgateway2mqtt_dev/README.md +++ b/enedisgateway2mqtt_dev/README.md @@ -48,7 +48,7 @@ Initial setup requires starting the addon once to initialize configuration templ ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CONFIG_LOCATION` | str | `/config/myelectricaldata/config.yaml` | Path to configuration file | | `TZ` | str | `Europe/Paris` | Timezone (e.g., `Europe/London`) | | `mqtt_autodiscover` | bool | `true` | Enable MQTT autodiscovery | diff --git a/ente/CHANGELOG.md b/ente/CHANGELOG.md index 53c239aeab..a70dd491e7 100644 --- a/ente/CHANGELOG.md +++ b/ente/CHANGELOG.md @@ -1,13 +1,13 @@ - + ## 4.4.28 (2026-09-12) - Update to latest version from ente/ente (changelog : https://github.com/ente/ente/releases) - Upstream tag : 1.7.29 - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 4.4.27 (2026-09-05) - Update to latest version from ente/ente (changelog : https://github.com/ente/ente/releases) - Upstream tag : 1.7.28 - + ## 4.4.26 (2026-08-13) - Update to latest version from ente/ente (changelog : https://github.com/ente/ente/releases) - Upstream tag : 1.3.61 diff --git a/ente/README.md b/ente/README.md index 49700af629..f13f35ebec 100644 --- a/ente/README.md +++ b/ente/README.md @@ -52,7 +52,7 @@ Webui can be found at . ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ENTE_ENDPOINT_URL` | str | `http://homeassistant.local:8280` | The URL where Ente API will be accessible (used by web UI) | | `DB_PASSWORD` | str | `ente` | Database password for internal PostgreSQL | | `USE_EXTERNAL_DB` | bool | `false` | Use external PostgreSQL database | @@ -63,7 +63,7 @@ Webui can be found at . If you want to use an external PostgreSQL database, set `USE_EXTERNAL_DB: true` and configure: | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `DB_HOSTNAME` | str | PostgreSQL server hostname | | `DB_PORT` | int | PostgreSQL server port (default: 5432) | | `DB_USERNAME` | str | PostgreSQL username | diff --git a/epicgamesfree/CHANGELOG.md b/epicgamesfree/CHANGELOG.md index 28559320ff..e568b64b1a 100644 --- a/epicgamesfree/CHANGELOG.md +++ b/epicgamesfree/CHANGELOG.md @@ -1,24 +1,24 @@ - + ## 2026.09.12 (2026-09-12) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-09-12 - + ## 2026.09.11 (2026-09-11) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-09-11 - + ## 2026.08.29 (2026-08-29) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-08-29 - + ## 2026.08.13 (2026-08-13) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-08-13 - + ## 2026.08.04 (2026-08-04) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-08-04 - + ## 2026.08.01 (2026-08-01) - Update to latest version from charlocharlie/epicgames-freegames - Upstream tag : debian-2026-08-01 @@ -26,16 +26,16 @@ - Version renamed from `debian-2026-07-27`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## debian-2026-07-27 (2026-07-27) - Update to latest version from charlocharlie/epicgames-freegames - + ## debian-2026-07-24 (2026-07-24) - Update to latest version from charlocharlie/epicgames-freegames - + ## debian-2026-07-20 (2026-07-20) - Update to latest version from charlocharlie/epicgames-freegames - + ## debian-2026-06-21 (2026-06-21) - Update to latest version from charlocharlie/epicgames-freegames ## debian-2026-06-05 (2026-06-05) diff --git a/epicgamesfree/README.md b/epicgamesfree/README.md index 213cf5029c..d4f7adde67 100644 --- a/epicgamesfree/README.md +++ b/epicgamesfree/README.md @@ -88,7 +88,7 @@ Create `/config/addons_config/epicgamesfree/config.json`: ### Configuration Options | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `accounts` | array | List of Epic Games accounts | | `cronSchedule` | string | Cron schedule to claim games (default: `0 */6 * * *`) | | `runOnStartup` | boolean | Run a claim cycle when the add-on starts | diff --git a/epicgamesfree/rootfs/etc/cont-init.d/99-run.sh b/epicgamesfree/rootfs/etc/cont-init.d/99-run.sh index ad08ca1c9f..ed694a74c0 100755 --- a/epicgamesfree/rootfs/etc/cont-init.d/99-run.sh +++ b/epicgamesfree/rootfs/etc/cont-init.d/99-run.sh @@ -22,7 +22,7 @@ if bashio::config.true 'disable_cron'; then fi if bashio::command.exists service; then - service cron stop >/dev/null 2>&1 || true + service cron stop > /dev/null 2>&1 || true fi fi diff --git a/filebrowser/CHANGELOG.md b/filebrowser/CHANGELOG.md index 79ab01d154..dcafd17c4e 100644 --- a/filebrowser/CHANGELOG.md +++ b/filebrowser/CHANGELOG.md @@ -1,6 +1,6 @@ - Breaking change : only appears in ingress for admins - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.63.23 (2026-08-01) - Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases) ## 2.63.18.5 (26-07-2026) @@ -9,7 +9,7 @@ - Minor bugs fixed ## 2.63.18.3 (22-07-2026) - New flag added : follow_external_symlinks - + ## 2.63.18 (2026-07-04) - Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases) @@ -18,10 +18,10 @@ ## 2.63.16 (2026-06-23) - Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases) - + ## 2.63.15 (2026-06-17) - Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases) - + ## 2.63.14 (2026-06-11) - Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases) ## 2.63.12 (2026-06-05) diff --git a/filebrowser/README.md b/filebrowser/README.md index 72ca385926..0abf48e6cb 100644 --- a/filebrowser/README.md +++ b/filebrowser/README.md @@ -59,7 +59,7 @@ The web UI can be found at `:8071` or through the Home Assistant sideba ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ssl` | bool | `false` | Enable HTTPS for web interface | | `certfile` | str | `fullchain.pem` | SSL certificate file (in `/ssl/`) | | `keyfile` | str | `privkey.pem` | SSL private key file (in `/ssl/`) | diff --git a/filebrowser_quantum/CHANGELOG.md b/filebrowser_quantum/CHANGELOG.md index 799d506877..6d8517bc08 100644 --- a/filebrowser_quantum/CHANGELOG.md +++ b/filebrowser_quantum/CHANGELOG.md @@ -1,9 +1,9 @@ - Breaking change : only appears in ingress for admins - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.5.6 (2026-09-05) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) - + ## 1.5.3.3 (2026-08-31) - Complete the iOS companion app fix from 1.5.3.2. The "no preview available" screen -- what you get for a `.zip`, `.bin` or anything else FileBrowser @@ -17,7 +17,7 @@ fixed for the download button in the file list. Links the app opens without a link element, such as the public-share sidebar download, are still affected. - + ## 1.5.3.2 (2026-08-30) - Fix Download in the Home Assistant iOS companion app (iOS 17 and later), where a file opened and showed its content with no way to save it. @@ -28,7 +28,7 @@ own download link. "Open file" still opens, and the public-share sidebar's own download button is not covered. Desktop browsers already downloaded these and are unchanged, as is direct access on port 8071. - + ## 1.5.3.1 (2026-08-29) - Fix "open parent directory" in Tools -> File Size Analyzer under Home Assistant ingress. FileBrowser opened the parent folder in a new tab, which @@ -39,13 +39,13 @@ item" action on search results, which open a new tab the same way. Download and preview popups, links pointing out of the add-on, and direct access on port 8071 are unchanged. - + ## 1.5.3 (2026-08-29) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) - + ## 1.5.2 (2026-08-22) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) - + ## 1.5.1.2 (2026-08-19) - Fix direct access on port 8071, which was broken in 1.5.1.1: the root redirect pointed at the container-internal port 8072 instead of the @@ -62,12 +62,12 @@ ## 1.5.1 (2026-08-08) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) - + ## 1.5.0 (2026-07-22) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) ## 1.4.0-2 (16-07-2026) - Minor bugs fixed - + ## 1.4.0 (2026-06-20) - Update to latest version from gtsteffaniak/filebrowser (changelog : https://github.com/gtsteffaniak/filebrowser/releases) ## 1.3.3-8 (2026-06-03) diff --git a/filebrowser_quantum/README.md b/filebrowser_quantum/README.md index 3087838ee0..1094ea6c12 100644 --- a/filebrowser_quantum/README.md +++ b/filebrowser_quantum/README.md @@ -57,7 +57,7 @@ The web UI can be found at `:8071` or through the Home Assistant sideba ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `auth_method` | list | `password` | Authentication method (`password`, `noauth`, `proxy`, `oidc`) | | `default_user_scope` | str | `/` | The root filesystem path used as the FileBrowser source and as the default scope for all users. Must be an existing absolute directory path (e.g. `/share`, `/media`). | | `localdisks` | str | _(optional)_ | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) | diff --git a/filebrowser_quantum/rootfs/etc/cont-init.d/99-run.sh b/filebrowser_quantum/rootfs/etc/cont-init.d/99-run.sh index 8e6f1de671..753e1cec2b 100755 --- a/filebrowser_quantum/rootfs/etc/cont-init.d/99-run.sh +++ b/filebrowser_quantum/rootfs/etc/cont-init.d/99-run.sh @@ -88,10 +88,10 @@ bashio::log.info "Updating FileBrowser config..." # --- Server (hardcoded values) --- bashio::log.info "... set server" -yq e -i ".server.port = 8080" "$FILEBROWSER_CONFIG" -yq e -i ".server.listen = \"0.0.0.0\"" "$FILEBROWSER_CONFIG" +yq e -i ".server.port = 8080" "$FILEBROWSER_CONFIG" +yq e -i ".server.listen = \"0.0.0.0\"" "$FILEBROWSER_CONFIG" yq e -i ".server.database = \"/config/database.db\"" "$FILEBROWSER_CONFIG" -yq e -i ".server.cacheDir = \"/cache\"" "$FILEBROWSER_CONFIG" +yq e -i ".server.cacheDir = \"/cache\"" "$FILEBROWSER_CONFIG" # --- Default user scope / source path --- bashio::log.info "... set default user scope" @@ -110,8 +110,8 @@ if [ ! -d "$DEFAULT_USER_SCOPE" ]; then fi bashio::log.info "... set source path and defaultUserScope to ${DEFAULT_USER_SCOPE}" -yq e -i ".server.sources[0].path = \"${DEFAULT_USER_SCOPE}\"" "$FILEBROWSER_CONFIG" -yq e -i ".server.sources[0].name = \"Default\"" "$FILEBROWSER_CONFIG" +yq e -i ".server.sources[0].path = \"${DEFAULT_USER_SCOPE}\"" "$FILEBROWSER_CONFIG" +yq e -i ".server.sources[0].name = \"Default\"" "$FILEBROWSER_CONFIG" yq e -i ".server.sources[0].config.defaultUserScope = \"${DEFAULT_USER_SCOPE}\"" "$FILEBROWSER_CONFIG" # --- Base URL (from env or config) --- @@ -124,28 +124,28 @@ AUTH_METHOD=$(bashio::config 'auth_method' 'password') bashio::log.info "... authentication method set to $AUTH_METHOD" case "$AUTH_METHOD" in noauth) - yq e -i ".auth.methods.noauth = true" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.noauth = true" "$FILEBROWSER_CONFIG" yq e -i ".auth.methods.password.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" ;; password) - yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.password.enabled = true" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.password.enabled = true" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" ;; proxy) - yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" yq e -i ".auth.methods.password.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.proxy.enabled = true" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.proxy.enabled = true" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.oidc.enabled = false" "$FILEBROWSER_CONFIG" ;; oidc) - yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.noauth = false" "$FILEBROWSER_CONFIG" yq e -i ".auth.methods.password.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" - yq e -i ".auth.methods.oidc.enabled = true" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.proxy.enabled = false" "$FILEBROWSER_CONFIG" + yq e -i ".auth.methods.oidc.enabled = true" "$FILEBROWSER_CONFIG" ;; *) bashio::log.fatal "Unknown auth_method: $AUTH_METHOD" diff --git a/fireflyiii/CHANGELOG.md b/fireflyiii/CHANGELOG.md index dac872bcb6..eda280d598 100644 --- a/fireflyiii/CHANGELOG.md +++ b/fireflyiii/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 6.6.6 (2026-07-04) - Update to latest version from firefly-iii/firefly-iii (changelog : https://github.com/firefly-iii/firefly-iii/releases) diff --git a/fireflyiii/README.md b/fireflyiii/README.md index d467f97967..fbe9228f1c 100644 --- a/fireflyiii/README.md +++ b/fireflyiii/README.md @@ -45,7 +45,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `APP_KEY` | str | `CHANGEME_32_CHARS_EuC5dfn3LAPzeO` | **CRITICAL**: 32-character encryption key - change before first run! | | `CONFIG_LOCATION` | str | `/config/addons_config/fireflyiii/config.yaml` | Location of additional config file | | `DB_CONNECTION` | list | `sqlite_internal` | Database type (sqlite_internal/mariadb_addon/mysql/pgsql) | diff --git a/fireflyiii/rootfs/etc/cont-init.d/99-run.sh b/fireflyiii/rootfs/etc/cont-init.d/99-run.sh index bb4757f4fa..e2ef45bbe7 100755 --- a/fireflyiii/rootfs/etc/cont-init.d/99-run.sh +++ b/fireflyiii/rootfs/etc/cont-init.d/99-run.sh @@ -132,7 +132,7 @@ case $(bashio::config 'DB_CONNECTION') in export DB_PORT && bashio::log.blue "DB_PORT=$DB_PORT" export DB_DATABASE && bashio::log.blue "DB_DATABASE=$DB_DATABASE" export DB_USERNAME && bashio::log.blue "DB_USERNAME=$DB_USERNAME" - export DB_PASSWORD # do not log password + export DB_PASSWORD # do not log password bashio::log.warning "Firefly-iii is using the Maria DB addon" bashio::log.warning "Please ensure this is included in your backups" diff --git a/fireflyiii_data_importer/CHANGELOG.md b/fireflyiii_data_importer/CHANGELOG.md index 47d7584638..471f00d609 100644 --- a/fireflyiii_data_importer/CHANGELOG.md +++ b/fireflyiii_data_importer/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.3.4 (2026-07-04) - Update to latest version from firefly-iii/data-importer (changelog : https://github.com/firefly-iii/data-importer/releases) ## 2.3.2-5 (28-05-2026) diff --git a/fireflyiii_data_importer/README.md b/fireflyiii_data_importer/README.md index b879dd8952..61449d9baa 100644 --- a/fireflyiii_data_importer/README.md +++ b/fireflyiii_data_importer/README.md @@ -49,7 +49,7 @@ For complete setup documentation, see: https://docs.firefly-iii.org/data-importe ### Options | Option | Type | Required | Description | -|--------|------|----------|-------------| +| -------- | ------ | ---------- | ------------- | | `FIREFLY_III_URL` | str | Yes | URL to your Firefly III instance | | `FIREFLY_III_ACCESS_TOKEN` | str | Yes | Personal Access Token from Firefly III | | `CONFIG_LOCATION` | str | Yes | Location for configuration files | diff --git a/fireflyiii_data_importer/rootfs/templates/cronupdate b/fireflyiii_data_importer/rootfs/templates/cronupdate index eff247b11b..5794b32de9 100644 --- a/fireflyiii_data_importer/rootfs/templates/cronupdate +++ b/fireflyiii_data_importer/rootfs/templates/cronupdate @@ -4,19 +4,27 @@ set -e # Source environment variables for cron context # shellcheck disable=SC1091 -if [ -f /etc/environment ]; then set -a; . /etc/environment; set +a; fi +if [ -f /etc/environment ]; then + set -a + . /etc/environment + set +a +fi # shellcheck disable=SC1091 -if [ -f /.env ]; then set -a; . /.env; set +a; fi +if [ -f /.env ]; then + set -a + . /.env + set +a +fi # Use persisted value instead of calling bashio::config (www-data may lack API access) CONFIGSOURCE="${CONFIG_LOCATION}" ( -echo "[INFO] Running update according to defined schedule. Files located in $CONFIGSOURCE/import_files will be imported." -if [ "${SILENT_MODE}" = "true" ]; then - echo "[WARNING] Silent mode activated. Only errors will be shown. Please disable in addon options if you need to debug" - sudo -Eu www-data /usr/local/bin/php /var/www/html/artisan importer:auto-import "$CONFIGSOURCE/import_files" >/dev/null -else - sudo -Eu www-data /usr/local/bin/php /var/www/html/artisan importer:auto-import "$CONFIGSOURCE/import_files" -fi + echo "[INFO] Running update according to defined schedule. Files located in $CONFIGSOURCE/import_files will be imported." + if [ "${SILENT_MODE}" = "true" ]; then + echo "[WARNING] Silent mode activated. Only errors will be shown. Please disable in addon options if you need to debug" + sudo -Eu www-data /usr/local/bin/php /var/www/html/artisan importer:auto-import "$CONFIGSOURCE/import_files" >/dev/null + else + sudo -Eu www-data /usr/local/bin/php /var/www/html/artisan importer:auto-import "$CONFIGSOURCE/import_files" + fi ) &>/proc/1/fd/1 diff --git a/flaresolverr/CHANGELOG.md b/flaresolverr/CHANGELOG.md index c2afa20e13..53045bb383 100644 --- a/flaresolverr/CHANGELOG.md +++ b/flaresolverr/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.5.2 (2026-09-12) - Update to latest version from FlareSolverr/FlareSolverr (changelog : https://github.com/FlareSolverr/FlareSolverr/releases) diff --git a/flexget/CHANGELOG.md b/flexget/CHANGELOG.md index 8f32d3e88a..ab8c9a29e7 100644 --- a/flexget/CHANGELOG.md +++ b/flexget/CHANGELOG.md @@ -1,38 +1,38 @@ - + ## 3.20.9 (2026-09-05) - Update to latest version from wiserain/flexget - Upstream tag : ci - + ## 3.20.8 (2026-08-29) - Update to latest version from wiserain/flexget - + ## 3.20.6 (2026-08-22) - Update to latest version from wiserain/flexget - + ## 3.20.5 (2026-08-15) - Update to latest version from wiserain/flexget - + ## 3.20.2 (2026-08-08) - Update to latest version from wiserain/flexget - + ## 3.19.31 (2026-08-01) - Update to latest version from wiserain/flexget - + ## 3.19.30 (2026-07-25) - Update to latest version from wiserain/flexget - + ## 3.19.28 (2026-07-22) - Update to latest version from wiserain/flexget - + ## 3.19.26 (2026-07-04) - Update to latest version from wiserain/flexget - + ## 3.19.25 (2026-06-23) - Update to latest version from wiserain/flexget - + ## 3.19.24 (2026-06-20) - Update to latest version from wiserain/flexget - + ## 3.19.23 (2026-06-06) - Update to latest version from wiserain/flexget diff --git a/flexget/README.md b/flexget/README.md index 52b0099bdb..8feac217ed 100644 --- a/flexget/README.md +++ b/flexget/README.md @@ -69,7 +69,7 @@ Default password: `homeassistant123` (change via addon options). ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `WebuiPass` | str | `homeassistant123` | Web interface password | diff --git a/free_games_claimer/CHANGELOG.md b/free_games_claimer/CHANGELOG.md index 8314c6f919..9600ad96be 100644 --- a/free_games_claimer/CHANGELOG.md +++ b/free_games_claimer/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 2.1.1 (2026-09-12) - Update to latest version from P-Adamiec/Free-Games-Claimer-Remaster (changelog : https://github.com/P-Adamiec/Free-Games-Claimer-Remaster/releases) - Upstream tag : 1.9 diff --git a/free_games_claimer/README.md b/free_games_claimer/README.md index 0dcb0a2256..de233ce5db 100644 --- a/free_games_claimer/README.md +++ b/free_games_claimer/README.md @@ -52,7 +52,7 @@ interaction. Set `VNC_PASSWORD` in `config.env` to protect the VNC session. ## Add-on options | Option | Default | Description | -|--------|---------|-------------| +| -------- | --------- | ------------- | | `CONFIG_LOCATION` | `/config/config.env` | Persistent environment configuration file | | `RUN_ONCE` | `true` | Run all selected claimers once, then stop the add-on as previous releases did | | `STORES` | empty | Optional comma-separated override, such as `epic,prime,gog,steam` | diff --git a/free_games_claimer/rootfs/etc/cont-init.d/99-run.sh b/free_games_claimer/rootfs/etc/cont-init.d/99-run.sh index 7dac64162d..11589c9a50 100755 --- a/free_games_claimer/rootfs/etc/cont-init.d/99-run.sh +++ b/free_games_claimer/rootfs/etc/cont-init.d/99-run.sh @@ -84,19 +84,19 @@ legacy_commands_to_stores() { for command in "${command_list[@]}"; do normalized="${command,,}" case "${normalized}" in - *epic-games*|*epicgames*|*" epic"*|epic*) + *epic-games* | *epicgames* | *" epic"* | epic*) selected="$(append_store "epic" "${selected}")" ;; - *prime-gaming*|*primegaming*|*" prime"*|prime*|*" amazon"*|amazon*) + *prime-gaming* | *primegaming* | *" prime"* | prime* | *" amazon"* | amazon*) selected="$(append_store "prime" "${selected}")" ;; - *steam-games*|*" steam"*|steam*) + *steam-games* | *" steam"* | steam*) selected="$(append_store "steam" "${selected}")" ;; *gamerpower*) selected="$(append_store "gamerpower" "${selected}")" ;; - *" gog"*|gog*) + *" gog"* | gog*) selected="$(append_store "gog" "${selected}")" ;; esac diff --git a/free_games_claimer/rootfs/templates/config.env b/free_games_claimer/rootfs/templates/config.env index 39a538959b..7b306017a6 100644 --- a/free_games_claimer/rootfs/templates/config.env +++ b/free_games_claimer/rootfs/templates/config.env @@ -2,14 +2,14 @@ # Complete option reference: # https://github.com/P-Adamiec/Free-Games-Claimer-Remaster#configuration +HEIGHT=720 +LOGIN_TIMEOUT=180 +NOVNC_PORT=6080 # Browser and noVNC SHOW=1 -WIDTH=1280 -HEIGHT=720 TIMEOUT=60 -LOGIN_TIMEOUT=180 VNC_LOGIN_TIMEOUT=180 -NOVNC_PORT=6080 +WIDTH=1280 # Public address used in notification links when behind a reverse proxy. # VNC_URL=https://fgc.example.tld diff --git a/free_games_claimer/rootfs/usr/local/bin/migrate_vogler_data.py b/free_games_claimer/rootfs/usr/local/bin/migrate_vogler_data.py index 1c11bf6e3b..c259fae314 100644 --- a/free_games_claimer/rootfs/usr/local/bin/migrate_vogler_data.py +++ b/free_games_claimer/rootfs/usr/local/bin/migrate_vogler_data.py @@ -47,8 +47,10 @@ def normalize_timestamp(value: Any) -> str: return parsed.isoformat(sep=" ", timespec="seconds") except ValueError: pass - return datetime.now(timezone.utc).replace(tzinfo=None).isoformat( - sep=" ", timespec="seconds" + return ( + datetime.now(timezone.utc) + .replace(tzinfo=None) + .isoformat(sep=" ", timespec="seconds") ) @@ -99,8 +101,7 @@ def iter_records(store: str, payload: Any, source: Path) -> Iterator[dict[str, A def ensure_schema(connection: sqlite3.Connection) -> None: - connection.executescript( - """ + connection.executescript(""" CREATE TABLE IF NOT EXISTS claimed_games ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, store VARCHAR(32) NOT NULL, @@ -117,8 +118,7 @@ def ensure_schema(connection: sqlite3.Connection) -> None: CREATE INDEX IF NOT EXISTS ix_claimed_games_store ON claimed_games (store); CREATE INDEX IF NOT EXISTS ix_claimed_games_user ON claimed_games (user); CREATE INDEX IF NOT EXISTS ix_claimed_games_game_id ON claimed_games (game_id); - """ - ) + """) def migrate() -> int: @@ -209,7 +209,12 @@ def migrate() -> int: connection.commit() log(f"Imported {source_imported} record(s) from {source}") - except (OSError, ValueError, json.JSONDecodeError, sqlite3.Error) as err: + except ( + OSError, + ValueError, + json.JSONDecodeError, + sqlite3.Error, + ) as err: connection.rollback() message = f"Failed to import {source}: {err}" errors.append(message) diff --git a/gazpar2mqtt/CHANGELOG.md b/gazpar2mqtt/CHANGELOG.md index a8ccdaa1eb..c23818568e 100644 --- a/gazpar2mqtt/CHANGELOG.md +++ b/gazpar2mqtt/CHANGELOG.md @@ -7,7 +7,7 @@ ## 0.8.2 (2025-12-24) - Update to latest version from yukulehe/gazpar2mqtt -## (2025-12-23) +## (2025-12-23) - Update to latest version from yukulehe/gazpar2mqtt - The Home Assistant project has deprecated support for the armv7, armhf and i386 architectures. Support wil be fully dropped in the upcoming Home Assistant 2025.12 release diff --git a/gazpar2mqtt/README.md b/gazpar2mqtt/README.md index 5501537b5c..ff55bc7d78 100644 --- a/gazpar2mqtt/README.md +++ b/gazpar2mqtt/README.md @@ -48,7 +48,7 @@ This addon fetches gas consumption data from GRDF (French gas utility) and publi ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CONFIG_LOCATION` | str | `/config/gazpar2mqtt/config.yaml` | Path to configuration file | | `TZ` | str | `Europe/Paris` | Timezone (e.g., `Europe/London`) | | `mqtt_autodiscover` | bool | `true` | Enable MQTT autodiscovery | diff --git a/gitea/CHANGELOG.md b/gitea/CHANGELOG.md index 3b4af6f1f5..40e604c6a6 100644 --- a/gitea/CHANGELOG.md +++ b/gitea/CHANGELOG.md @@ -1,19 +1,19 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.27.3 (2026-09-05) - Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases) - + ## 1.27.2 (2026-08-15) - Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases) - + ## 1.27.1 (2026-08-01) - Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases) - + ## 1.27.0 (2026-07-22) - Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases) ## 1.27 (15-07-2026) - Fix the Gitea add-on HEALTHCHECK to work correctly when SSL is enabled, so Home Assistant can accurately report the add-on's health status regardless of whether the instance uses HTTP or HTTPS. - + ## 1.26.4 (2026-06-23) - Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases) ## 1.26.2-3 (19-06-2026) diff --git a/gitea/README.md b/gitea/README.md index 63d0059579..2456358de7 100644 --- a/gitea/README.md +++ b/gitea/README.md @@ -42,7 +42,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ssl` | bool | `false` | Enable HTTPS for the web interface | | `certfile` | str | `fullchain.pem` | SSL certificate file (must be located in /ssl) | | `keyfile` | str | `privkey.pem` | SSL key file (must be located in /ssl) | diff --git a/grampsweb/CHANGELOG.md b/grampsweb/CHANGELOG.md index 9fb3849703..9229d1188a 100644 --- a/grampsweb/CHANGELOG.md +++ b/grampsweb/CHANGELOG.md @@ -1,20 +1,20 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 26.8.1 (2026-08-29) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) - + ## 26.8.0 (2026-08-15) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) - + ## 26.7.1 (2026-08-01) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) - + ## 26.7.0 (2026-07-22) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) - + ## 26.6.2 (2026-06-23) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) - + ## 26.6.1 (2026-06-11) - Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases) ## 26.6.0 (2026-06-05) diff --git a/grampsweb/README.md b/grampsweb/README.md index 965eb1061d..7779f22e3d 100644 --- a/grampsweb/README.md +++ b/grampsweb/README.md @@ -53,7 +53,7 @@ Webui can be found at . ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CELERY_NUM_WORKERS` | int | `2` | Number of Celery workers for background tasks | | `GUNICORN_NUM_WORKERS` | int | `8` | Number of Gunicorn workers for web requests | | `GRAMPSWEB_SECRET_KEY` | str | - | Secret key for session security (auto-generated if not set) | @@ -65,7 +65,7 @@ Webui can be found at . ### Email Configuration (Optional) | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `GRAMPSWEB_EMAIL_HOST` | str | SMTP server hostname | | `GRAMPSWEB_EMAIL_PORT` | int | SMTP server port | | `GRAMPSWEB_EMAIL_USE_SSL` | bool | Use SSL encryption (for port 465) | diff --git a/grav/CHANGELOG.md b/grav/CHANGELOG.md index 14c37f8dd3..a733d2c8b2 100644 --- a/grav/CHANGELOG.md +++ b/grav/CHANGELOG.md @@ -1,40 +1,40 @@ - + ## 2.1.2 (2026-09-12) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.24 (2026-09-05) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.21 (2026-08-29) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.20 (2026-08-22) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.19 (2026-08-15) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.18 (2026-08-13) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.17 (2026-08-08) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.14 (2026-08-01) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.12 (2026-07-22) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.6 (2026-07-04) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.3 (2026-06-27) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 2.0.0 (2026-06-23) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) - + ## 1.7.53 (2026-06-17) - Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases) diff --git a/grav/README.md b/grav/README.md index d835e4153a..aad75bd6a8 100644 --- a/grav/README.md +++ b/grav/README.md @@ -66,7 +66,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `1000` | Group ID for file permissions | | `PUID` | int | `1000` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/guacamole/CHANGELOG.md b/guacamole/CHANGELOG.md index 763216cae6..3b91a7f494 100644 --- a/guacamole/CHANGELOG.md +++ b/guacamole/CHANGELOG.md @@ -8,7 +8,7 @@ ## 1.6.0-bullseye (2025-12-24) - Update to latest version from abesnier/guacamole -## (2025-12-23) +## (2025-12-23) - Update to latest version from abesnier/guacamole - Added support for configuring extra environment variables via the `env_vars` add-on option alongside config.yaml. See https://github.com/alexbelgium/hassio-addons/wiki/Add-Environment-variables-to-your-Addon-2 for details. diff --git a/guacamole/README.md b/guacamole/README.md index cca3b73a8d..5775c1227c 100644 --- a/guacamole/README.md +++ b/guacamole/README.md @@ -44,7 +44,7 @@ The default username is `guacadmin` with password `guacadmin`. It is strongly re ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `EXTENSIONS` | str | `auth-totp` | Guacamole extensions to enable (e.g., `auth-totp`, `history-recording-storage`) | | `recording_search_path` | str | `/config/recordings` | Directory added to `guacamole.properties` as the `recording-search-path` used by the history recording storage extension | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/immich/CHANGELOG.md b/immich/CHANGELOG.md index 5ad32af0ab..a0c4ebc6d9 100644 --- a/immich/CHANGELOG.md +++ b/immich/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.2.0.2 (2026-09-12) - Update to latest version from ghcr.io/imagegenius/immich:3 - Upstream tag : 3.2.0 @@ -8,17 +8,17 @@ - The add-on is now rebuilt whenever imagegenius repoints `:3`, whether the immich version changed or the image was simply republished - The add-on has never built immich itself, it builds on top of the imagegenius rebuild, which lags immich upstream by days to weeks. Version `3.2.0` was published while the image still contained immich `3.1.0` (issue #3060), and nothing would have rebuilt the add-on once the image caught up - This build still contains immich **3.1.0**, and will be updated automatically once imagegenius publishes a newer image. Home Assistant cannot offer a version lower than the one installed, so the add-on number stays in the `3.2.0.x` range until the image moves past 3.2.0 - + ## 3.2.0 (2026-09-12) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.1.0.1 (2026-08-17) - Fix `password authentication failed for user` when `DB_PASSWORD` contains special characters. Passwords are now URI-encoded before being used in the psql connection string, and SQL-escaped before being used in `CREATE`/`ALTER USER` statements - + ## 3.1.0 (2026-08-01) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - + ## 3.0.3 (2026-07-22) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) ## 3.0.1-2 (04-07-2026) diff --git a/immich/README.md b/immich/README.md index 0d875d37ca..7cca87a0a8 100644 --- a/immich/README.md +++ b/immich/README.md @@ -51,7 +51,7 @@ Webui can be found at `:8080`. PostgreSQL can be either internal or ext ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `data_location` | str | `/share/immich` | Path where Immich data is stored | | `library_location` | str | | Path to photo/video library | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/immich_cuda/CHANGELOG.md b/immich_cuda/CHANGELOG.md index 0bf5d91218..91441d8cb0 100644 --- a/immich_cuda/CHANGELOG.md +++ b/immich_cuda/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.2.0.2 (2026-09-12) - Update to latest version from ghcr.io/imagegenius/immich:3-cuda - Upstream tag : 3.2.0 @@ -8,14 +8,14 @@ - The add-on is now rebuilt whenever imagegenius repoints `:3-cuda`, whether the immich version changed or the image was simply republished - The add-on has never built immich itself, it builds on top of the imagegenius rebuild, which lags immich upstream by days to weeks. Version `3.2.0` was published while the image still contained immich `3.1.0` (issue #3060), and nothing would have rebuilt the add-on once the image caught up - This build still contains immich **3.1.0**, and will be updated automatically once imagegenius publishes a newer image. Home Assistant cannot offer a version lower than the one installed, so the add-on number stays in the `3.2.0.x` range until the image moves past 3.2.0 - + ## 3.2.0 (2026-09-12) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.1.0 (2026-08-01) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - + ## 3.0.3 (2026-07-22) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) ## 3.0.1-2 (04-07-2026) diff --git a/immich_cuda/README.md b/immich_cuda/README.md index 16f31b6066..3d474b748c 100644 --- a/immich_cuda/README.md +++ b/immich_cuda/README.md @@ -58,7 +58,7 @@ Webui can be found at `:8080`. PostgreSQL can be either internal or ext ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `data_location` | str | `/share/immich` | Path where Immich data is stored | | `library_location` | str | | Path to photo/video library | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/immich_cuda/config.yaml b/immich_cuda/config.yaml index b214aeea0f..bdc2529e1a 100644 --- a/immich_cuda/config.yaml +++ b/immich_cuda/config.yaml @@ -2,8 +2,7 @@ arch: - amd64 backup_exclude: - "**/machine-learning/*" -description: - Self-hosted photo and video backup solution directly from your mobile phone +description: Self-hosted photo and video backup solution directly from your mobile phone devices: - /dev/dri - /dev/dxg diff --git a/immich_frame/CHANGELOG.md b/immich_frame/CHANGELOG.md index 04825ede2e..72d2320438 100644 --- a/immich_frame/CHANGELOG.md +++ b/immich_frame/CHANGELOG.md @@ -1,8 +1,8 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.0.37.0 (2026-07-22) - Update to latest version from immichFrame/ImmichFrame (changelog : https://github.com/immichFrame/ImmichFrame/releases) - + ## 1.0.34.0 (2026-07-04) - Update to latest version from immichFrame/ImmichFrame (changelog : https://github.com/immichFrame/ImmichFrame/releases) diff --git a/immich_frame/README.md b/immich_frame/README.md index f4d2eba5d5..afa75feb62 100644 --- a/immich_frame/README.md +++ b/immich_frame/README.md @@ -42,7 +42,7 @@ Webui can be found at `:8171`. #### Connection | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `ImmichServerUrl` | str | URL of your Immich server (e.g., `http://homeassistant:3001`). Used for single-account setup. | | `ApiKey` | str | Immich API key for authentication. Used for single-account setup. | | `Accounts` | list | List of Immich accounts for multi-account support. Each entry requires `ImmichServerUrl` and `ApiKey`, plus optional per-account filters (see below). | @@ -53,7 +53,7 @@ Webui can be found at `:8171`. These top-level options map to ImmichFrame's `General` settings and control the display behavior: | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `Interval` | int | 45 | Image display interval in seconds | | `TransitionDuration` | float | 2 | Transition duration in seconds | | `ShowClock` | bool | true | Display the current time | @@ -91,7 +91,7 @@ These top-level options map to ImmichFrame's `General` settings and control the These options can be set within each `Accounts` entry to control which images are shown: | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `Albums` | str | Comma-separated album UUIDs | | `ExcludedAlbums` | str | Comma-separated excluded album UUIDs | | `People` | str | Comma-separated people UUIDs | diff --git a/immich_frame/rootfs/etc/cont-init.d/99-run.sh b/immich_frame/rootfs/etc/cont-init.d/99-run.sh index f1583ef9e5..4bbd7bac21 100755 --- a/immich_frame/rootfs/etc/cont-init.d/99-run.sh +++ b/immich_frame/rootfs/etc/cont-init.d/99-run.sh @@ -9,13 +9,13 @@ if [ -L /config/Config ]; then bashio::log.info "Migrating legacy /config/Config symlink to real directory" mkdir -p /config/Config.migrate # Copy contents from the symlink target into the new real directory - cp -a /config/Config/. /config/Config.migrate/ 2>/dev/null || true + cp -a /config/Config/. /config/Config.migrate/ 2> /dev/null || true rm -f /config/Config mv /config/Config.migrate /config/Config fi if [ -d /app/Config ] && [ ! -L /app/Config ]; then - cp -n /app/Config/* /config/Config/ 2>/dev/null || true + cp -n /app/Config/* /config/Config/ 2> /dev/null || true rm -rf /app/Config fi if [ ! -e /app/Config ]; then @@ -35,10 +35,10 @@ in_list() { [[ "$2" == *" $1 "* ]]; } # Helper: read a value from options.json handling booleans and nulls correctly config_val() { - jq -r "($1) as \$v | if \$v == null then \"\" else (\$v | tostring) end" /data/options.json 2>/dev/null + jq -r "($1) as \$v | if \$v == null then \"\" else (\$v | tostring) end" /data/options.json 2> /dev/null } config_has() { - jq -e "($1) != null" /data/options.json >/dev/null 2>&1 + jq -e "($1) != null" /data/options.json > /dev/null 2>&1 } # Helper: write a YAML key-value pair with proper formatting @@ -82,7 +82,7 @@ yaml_kv() { declare -A GENERAL_ENVS declare -A ACCOUNT_ENVS -ENV_COUNT=$(jq '.env_vars // [] | length' /data/options.json 2>/dev/null || echo 0) +ENV_COUNT=$(jq '.env_vars // [] | length' /data/options.json 2> /dev/null || echo 0) if [ "$ENV_COUNT" -gt 0 ]; then bashio::log.info "Processing ${ENV_COUNT} env_var(s) for Settings.yaml" fi @@ -91,7 +91,7 @@ for idx in $(seq 0 $((ENV_COUNT - 1))); do EVALUE=$(jq -r ".env_vars[${idx}].value // \"\"" /data/options.json) [ -z "$ENAME" ] && continue [ -z "$EVALUE" ] && continue - [ "$ENAME" = "TZ" ] && continue # TZ is a system env var, not an ImmichFrame setting + [ "$ENAME" = "TZ" ] && continue # TZ is a system env var, not an ImmichFrame setting if in_list "$ENAME" "$ACCOUNT_KEYS"; then ACCOUNT_ENVS["$ENAME"]="$EVALUE" @@ -121,7 +121,10 @@ ACCOUNT_SCHEMA_OPTS="Albums ExcludedAlbums People Tags ShowFavorites ShowMemorie for opt in $GENERAL_SCHEMA_OPTS; do if config_has ".$opt"; then - $GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; } + $GENERAL_STARTED || { + echo "General:" + GENERAL_STARTED=true + } yaml_kv " " "$opt" "$(config_val ".$opt")" fi done @@ -129,13 +132,16 @@ ACCOUNT_SCHEMA_OPTS="Albums ExcludedAlbums People Tags ShowFavorites ShowMemorie # Add general env_vars (skip if already set via schema option) for key in "${!GENERAL_ENVS[@]}"; do if ! config_has ".$key"; then - $GENERAL_STARTED || { echo "General:"; GENERAL_STARTED=true; } + $GENERAL_STARTED || { + echo "General:" + GENERAL_STARTED=true + } yaml_kv " " "$key" "${GENERAL_ENVS[$key]}" fi done # -- Accounts section -- - ACCOUNT_COUNT=$(jq '.Accounts // [] | length' /data/options.json 2>/dev/null || echo 0) + ACCOUNT_COUNT=$(jq '.Accounts // [] | length' /data/options.json 2> /dev/null || echo 0) if [ "$ACCOUNT_COUNT" -gt 0 ]; then bashio::log.info "Configuring ${ACCOUNT_COUNT} account(s) from Accounts list" diff --git a/immich_noml/CHANGELOG.md b/immich_noml/CHANGELOG.md index 70db0023c8..5234f322a2 100644 --- a/immich_noml/CHANGELOG.md +++ b/immich_noml/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.2.0.2 (2026-09-12) - Update to latest version from ghcr.io/imagegenius/immich:3-noml - Upstream tag : 3.2.0 @@ -8,14 +8,14 @@ - The add-on is now rebuilt whenever imagegenius repoints `:3-noml`, whether the immich version changed or the image was simply republished - The add-on has never built immich itself, it builds on top of the imagegenius rebuild, which lags immich upstream by days to weeks. Version `3.2.0` was published while the image still contained immich `3.1.0` (issue #3060), and nothing would have rebuilt the add-on once the image caught up - This build still contains immich **3.1.0**, and will be updated automatically once imagegenius publishes a newer image. Home Assistant cannot offer a version lower than the one installed, so the add-on number stays in the `3.2.0.x` range until the image moves past 3.2.0 - + ## 3.2.0 (2026-09-12) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.1.0 (2026-08-01) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - + ## 3.0.3 (2026-07-22) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) ## 3.0.1-2 (04-07-2026) diff --git a/immich_noml/README.md b/immich_noml/README.md index 75d3622720..39fad14c1f 100644 --- a/immich_noml/README.md +++ b/immich_noml/README.md @@ -62,7 +62,7 @@ Webui can be found at `:8080`. PostgreSQL can be either internal or ext ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `data_location` | str | `/share/immich` | Path where Immich data is stored | | `library_location` | str | | Path to photo/video library | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/immich_noml/config.yaml b/immich_noml/config.yaml index ecc914eff5..d18c72beff 100644 --- a/immich_noml/config.yaml +++ b/immich_noml/config.yaml @@ -3,8 +3,7 @@ arch: - amd64 backup_exclude: - "**/machine-learning/*" -description: - Self-hosted photo and video backup solution directly from your mobile phone +description: Self-hosted photo and video backup solution directly from your mobile phone devices: - /dev/dri - /dev/dxg diff --git a/immich_openvino/CHANGELOG.md b/immich_openvino/CHANGELOG.md index 7fd7683698..019a0fd361 100644 --- a/immich_openvino/CHANGELOG.md +++ b/immich_openvino/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.2.0.2 (2026-09-12) - Update to latest version from ghcr.io/imagegenius/immich:3-openvino - Upstream tag : 3.2.0 @@ -8,14 +8,14 @@ - The add-on is now rebuilt whenever imagegenius repoints `:3-openvino`, whether the immich version changed or the image was simply republished - The add-on has never built immich itself, it builds on top of the imagegenius rebuild, which lags immich upstream by days to weeks. Version `3.2.0` was published while the image still contained immich `3.1.0` (issue #3060), and nothing would have rebuilt the add-on once the image caught up - This build still contains immich **3.1.0**, and will be updated automatically once imagegenius publishes a newer image. Home Assistant cannot offer a version lower than the one installed, so the add-on number stays in the `3.2.0.x` range until the image moves past 3.2.0 - + ## 3.2.0 (2026-09-12) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.1.0 (2026-08-01) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) - + ## 3.0.3 (2026-07-22) - Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases) ## 3.0.1-2 (04-07-2026) diff --git a/immich_openvino/README.md b/immich_openvino/README.md index 7e361de524..5a1f2e099a 100644 --- a/immich_openvino/README.md +++ b/immich_openvino/README.md @@ -59,7 +59,7 @@ Webui can be found at `:8080`. PostgreSQL can be either internal or ext ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `data_location` | str | `/share/immich` | Path where Immich data is stored | | `library_location` | str | | Path to photo/video library | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/immich_power_tools/CHANGELOG.md b/immich_power_tools/CHANGELOG.md index 8a032cf798..881eae388f 100644 --- a/immich_power_tools/CHANGELOG.md +++ b/immich_power_tools/CHANGELOG.md @@ -1,8 +1,8 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 0.23.0 (2026-09-05) - Update to latest version from varun-raj/immich-power-tools (changelog : https://github.com/varun-raj/immich-power-tools/releases) - + ## 0.22.0 (2026-07-04) - Update to latest version from varun-raj/immich-power-tools (changelog : https://github.com/varun-raj/immich-power-tools/releases) diff --git a/immich_power_tools/README.md b/immich_power_tools/README.md index b83195646b..d6f16e3c69 100644 --- a/immich_power_tools/README.md +++ b/immich_power_tools/README.md @@ -48,7 +48,7 @@ Webui can be found at `:8001`. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `IMMICH_URL` | str | **Required** | Internal Immich server URL (e.g., `http://homeassistant:3001`) | | `EXTERNAL_IMMICH_URL` | str | **Required** | External Immich server URL for browser access | | `IMMICH_API_KEY` | str | **Required** | Immich API key for authentication | diff --git a/inadyn/README.md b/inadyn/README.md index 4da00962fc..080208871b 100644 --- a/inadyn/README.md +++ b/inadyn/README.md @@ -57,7 +57,7 @@ For detailed configuration information, see the [official documentation](https:/ ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `verify_address` | bool | | Verify the IP address with check IP service | | `fake_address` | bool | | Use fake address for testing | | `allow_ipv6` | bool | | Enable IPv6 support | @@ -73,7 +73,7 @@ For detailed configuration information, see the [official documentation](https:/ Each provider in the `providers` list supports these options: | Option | Type | Description | -|--------|------|-------------| +| -------- | ------ | ------------- | | `provider` | str | Provider name or custom identifier | | `custom_provider` | bool | Whether this is a custom provider | | `username` | str | Username or token for authentication | diff --git a/jackett/CHANGELOG.md b/jackett/CHANGELOG.md index 7249ae633d..472c069710 100644 --- a/jackett/CHANGELOG.md +++ b/jackett/CHANGELOG.md @@ -1,61 +1,61 @@ - + ## 0.24.2572 (2026-09-12) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2558 (2026-09-12) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2531 (2026-09-05) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2486 (2026-08-29) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2434 (2026-08-22) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2406 (2026-08-15) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2404 (2026-08-13) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2342 (2026-08-08) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2315 (2026-08-02) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2307 (2026-08-01) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2304 (2026-08-01) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2265 (2026-07-25) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2251 (2026-07-22) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2166 (2026-07-04) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2125 (2026-06-27) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2110 (2026-06-23) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2080 (2026-06-20) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2075 (2026-06-17) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2051 (2026-06-13) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) - + ## 0.24.2045 (2026-06-11) - Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases) ## 0.24.2021 (2026-06-05) diff --git a/jackett/README.md b/jackett/README.md index 645890b5c7..abab13493e 100644 --- a/jackett/README.md +++ b/jackett/README.md @@ -43,7 +43,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/jellyfin/CHANGELOG.md b/jellyfin/CHANGELOG.md index 702b35eb62..bc109fdfa7 100644 --- a/jellyfin/CHANGELOG.md +++ b/jellyfin/CHANGELOG.md @@ -1,8 +1,8 @@ - + ## 12.0 (2026-09-12) - Update to latest version from linuxserver/docker-jellyfin (changelog : https://github.com/linuxserver/docker-jellyfin/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 10.11.11 (2026-06-11) - Update to latest version from linuxserver/docker-jellyfin (changelog : https://github.com/linuxserver/docker-jellyfin/releases) diff --git a/jellyfin/README.md b/jellyfin/README.md index 947bdfbc96..d600efaa83 100644 --- a/jellyfin/README.md +++ b/jellyfin/README.md @@ -40,7 +40,7 @@ Webui can be found at `:8096` or through the sidebar using Ingress. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/jellyfin/config.yaml b/jellyfin/config.yaml index 276b7ef0d6..289e44ccd9 100644 --- a/jellyfin/config.yaml +++ b/jellyfin/config.yaml @@ -71,7 +71,7 @@ devices: - /dev/nvme1 - /dev/nvme2 environment: - ATTACHED_DEVICES_PERMS: '/dev/dri /dev/dvb /dev/vchiq /dev/vc-mem /dev/video1? -type c -o -type f' + ATTACHED_DEVICES_PERMS: "/dev/dri /dev/dvb /dev/vchiq /dev/vc-mem /dev/video1? -type c -o -type f" host_dbus: true host_network: true image: ghcr.io/alexbelgium/jellyfin-{arch} diff --git a/jellyfin/rootfs/etc/cont-init.d/25-video_fix.sh b/jellyfin/rootfs/etc/cont-init.d/25-video_fix.sh index 68e981ac48..3a4021a0e1 100755 --- a/jellyfin/rootfs/etc/cont-init.d/25-video_fix.sh +++ b/jellyfin/rootfs/etc/cont-init.d/25-video_fix.sh @@ -10,7 +10,7 @@ log() { } # Ensure user exists -if ! id "$JELLYFIN_USER" >/dev/null 2>&1; then +if ! id "$JELLYFIN_USER" > /dev/null 2>&1; then log "User $JELLYFIN_USER not found — skipping" exit 0 fi @@ -21,17 +21,17 @@ if [ -d /dev/dri ]; then for dev in /dev/dri/renderD*; do if [ -e "$dev" ]; then log "Setting permissions on $dev" - chmod 666 "$dev" 2>/dev/null || log "chmod failed on $dev" + chmod 666 "$dev" 2> /dev/null || log "chmod failed on $dev" fi done # Detect the actual GID of the render device - RENDER_GID="$(stat -c '%g' /dev/dri/renderD128 2>/dev/null || true)" + RENDER_GID="$(stat -c '%g' /dev/dri/renderD128 2> /dev/null || true)" if [ -z "$RENDER_GID" ]; then # Fallback: try any render device for dev in /dev/dri/renderD*; do if [ -e "$dev" ]; then - RENDER_GID="$(stat -c '%g' "$dev" 2>/dev/null || true)" + RENDER_GID="$(stat -c '%g' "$dev" 2> /dev/null || true)" break fi done @@ -46,7 +46,7 @@ if [ -d /dev/dri ]; then if [ -z "$GROUP_NAME" ]; then GROUP_NAME="render${RENDER_GID}" log "Creating group $GROUP_NAME with GID $RENDER_GID" - if ! groupadd -g "$RENDER_GID" "$GROUP_NAME" 2>/dev/null; then + if ! groupadd -g "$RENDER_GID" "$GROUP_NAME" 2> /dev/null; then log "Group creation failed (probably already exists). Continuing." fi else @@ -58,7 +58,7 @@ if [ -d /dev/dri ]; then log "User $JELLYFIN_USER already in group $GROUP_NAME" else log "Adding user $JELLYFIN_USER to group $GROUP_NAME" - if ! usermod -aG "$GROUP_NAME" "$JELLYFIN_USER" 2>/dev/null; then + if ! usermod -aG "$GROUP_NAME" "$JELLYFIN_USER" 2> /dev/null; then log "usermod failed — probably read-only user or already applied" fi fi diff --git a/jellyfin/rootfs/etc/services.d/nginx/run b/jellyfin/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/joal/README.md b/joal/README.md index b70d8c0ade..bbaa9509da 100644 --- a/joal/README.md +++ b/joal/README.md @@ -43,7 +43,7 @@ Configuration details are available in the addon logs. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `secret_token` | str | `lrMY24Byhx` | Authentication token for the web interface | | `ui_path` | str | `joal` | Web UI path | | `run_duration` | str | `12h` | Runtime duration (e.g., 5s, 2m, 12h, 5d) | diff --git a/joplin/CHANGELOG.md b/joplin/CHANGELOG.md index cac6d5c3d3..2c73f8a17e 100644 --- a/joplin/CHANGELOG.md +++ b/joplin/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.7.2 (2026-09-12) - Update to latest version from etechonomy/joplin-server (changelog : https://github.com/etechonomy/joplin-server/releases) diff --git a/joplin/README.md b/joplin/README.md index 87ac12d478..3bd47cb0c0 100644 --- a/joplin/README.md +++ b/joplin/README.md @@ -43,7 +43,7 @@ Webui can be found at `:22300`. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `APP_BASE_URL` | str | `http://your_domain:port` | Base public URL where the service will be running | | `data_location` | str | `/config/addons_config/joplin` | Path where Joplin data is stored | | `DB_CLIENT` | str | | Database client type. Only `pg` (PostgreSQL) is supported. MariaDB/MySQL is NOT supported. | diff --git a/joplin/rootfs/etc/cont-init.d/99-run.sh b/joplin/rootfs/etc/cont-init.d/99-run.sh index b59858b917..3cd1d7f0c9 100755 --- a/joplin/rootfs/etc/cont-init.d/99-run.sh +++ b/joplin/rootfs/etc/cont-init.d/99-run.sh @@ -7,7 +7,7 @@ bashio::log.warning "Warning - minimum configuration recommended : 2 cpu cores a unlock_sqlite_migrations() { local db_path="$1" - if ! command -v sqlite3 >/dev/null 2>&1; then + if ! command -v sqlite3 > /dev/null 2>&1; then bashio::log.warning "sqlite3 not available; skipping SQLite migration lock check." return 0 fi @@ -20,7 +20,7 @@ unlock_sqlite_migrations() { # If the lock table doesn't exist yet, nothing to unlock local has_table - has_table="$(sqlite3 "$db_path" "SELECT 1 FROM sqlite_master WHERE type='table' AND name='knex_migrations_lock' LIMIT 1;" 2>/dev/null || true)" + has_table="$(sqlite3 "$db_path" "SELECT 1 FROM sqlite_master WHERE type='table' AND name='knex_migrations_lock' LIMIT 1;" 2> /dev/null || true)" [[ "$has_table" == "1" ]] || return 0 # Ensure the lock row exists (Knex expects one row) @@ -28,22 +28,22 @@ unlock_sqlite_migrations() { sqlite3 "$db_path" " PRAGMA busy_timeout=5000; INSERT OR IGNORE INTO knex_migrations_lock(\"index\", is_locked) VALUES (1, 0); - " >/dev/null 2>&1 || true + " > /dev/null 2>&1 || true local is_locked - is_locked="$(sqlite3 "$db_path" "PRAGMA busy_timeout=5000; SELECT is_locked FROM knex_migrations_lock WHERE \"index\"=1 LIMIT 1;" 2>/dev/null || true)" + is_locked="$(sqlite3 "$db_path" "PRAGMA busy_timeout=5000; SELECT is_locked FROM knex_migrations_lock WHERE \"index\"=1 LIMIT 1;" 2> /dev/null || true)" if [[ "$is_locked" == "1" ]]; then bashio::log.warning "Locked SQLite migration table detected, attempting to unlock." sqlite3 "$db_path" " PRAGMA busy_timeout=5000; UPDATE knex_migrations_lock SET is_locked = 0 WHERE \"index\"=1 AND is_locked=1; - " >/dev/null 2>&1 || bashio::log.warning "Failed to clear SQLite migration lock." + " > /dev/null 2>&1 || bashio::log.warning "Failed to clear SQLite migration lock." fi } unlock_postgres_migrations() { - if ! command -v psql >/dev/null 2>&1; then + if ! command -v psql > /dev/null 2>&1; then bashio::log.warning "psql not available; skipping PostgreSQL migration lock check." return 0 fi @@ -59,23 +59,26 @@ unlock_postgres_migrations() { # If table doesn't exist, skip local has_table has_table="$(psql -h "$POSTGRES_HOST" -p "$pg_port" -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -Atqc \ - "SELECT 1 FROM information_schema.tables WHERE table_name='knex_migrations_lock' LIMIT 1;" 2>/dev/null || true)" - [[ "$has_table" == "1" ]] || { unset PGPASSWORD; return 0; } + "SELECT 1 FROM information_schema.tables WHERE table_name='knex_migrations_lock' LIMIT 1;" 2> /dev/null || true)" + [[ "$has_table" == "1" ]] || { + unset PGPASSWORD + return 0 + } # Ensure row exists psql -h "$POSTGRES_HOST" -p "$pg_port" -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -Atqc \ "INSERT INTO knex_migrations_lock(\"index\", is_locked) VALUES (1, 0) ON CONFLICT (\"index\") DO NOTHING;" \ - >/dev/null 2>&1 || true + > /dev/null 2>&1 || true local is_locked is_locked="$(psql -h "$POSTGRES_HOST" -p "$pg_port" -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -Atqc \ - "SELECT is_locked FROM knex_migrations_lock WHERE \"index\"=1 LIMIT 1;" 2>/dev/null || true)" + "SELECT is_locked FROM knex_migrations_lock WHERE \"index\"=1 LIMIT 1;" 2> /dev/null || true)" if [[ "$is_locked" == "1" ]]; then bashio::log.warning "Locked PostgreSQL migration table detected, attempting to unlock." psql -h "$POSTGRES_HOST" -p "$pg_port" -U "$POSTGRES_USER" -d "$POSTGRES_DATABASE" -Atqc \ "UPDATE knex_migrations_lock SET is_locked = 0 WHERE \"index\"=1 AND is_locked=1;" \ - >/dev/null 2>&1 || bashio::log.warning "Failed to clear PostgreSQL migration lock." + > /dev/null 2>&1 || bashio::log.warning "Failed to clear PostgreSQL migration lock." fi unset PGPASSWORD diff --git a/kometa/CHANGELOG.md b/kometa/CHANGELOG.md index dc1ea65e86..21ffc8610e 100644 --- a/kometa/CHANGELOG.md +++ b/kometa/CHANGELOG.md @@ -1,19 +1,19 @@ - + ## 2.4.8 (2026-08-22) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) - + ## 2.4.7 (2026-08-13) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) - + ## 2.4.6 (2026-08-01) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) - + ## 2.4.5 (2026-07-25) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) - + ## 2.4.4 (2026-06-27) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) - + ## 2.4.3 (2026-06-23) - Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases) diff --git a/komga/README.md b/komga/README.md index bcb67e8fde..b2a862fc32 100644 --- a/komga/README.md +++ b/komga/README.md @@ -28,7 +28,7 @@ search index on first boot. ## Configuration | Option | Description | -|--------|-------------| +| -------- | ------------- | | `PUID` / `PGID` | Ownership applied to the add-on configuration directory. Defaults to `0` (root). | | `TZ` | Timezone, e.g. `Europe/Paris`. | | `localdisks` | Local disks to mount, e.g. `sda1` or a disk label. | diff --git a/librespeed/CHANGELOG.md b/librespeed/CHANGELOG.md index 30aea667fb..86c2060af1 100644 --- a/librespeed/CHANGELOG.md +++ b/librespeed/CHANGELOG.md @@ -1,11 +1,11 @@ - + ## 6.3.0 (2026-09-12) - Update to latest version from linuxserver/docker-librespeed (changelog : https://github.com/linuxserver/docker-librespeed/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 6.2.1 (2026-08-15) - Update to latest version from linuxserver/docker-librespeed (changelog : https://github.com/linuxserver/docker-librespeed/releases) - + ## 6.2.0 (2026-07-25) - Update to latest version from linuxserver/docker-librespeed (changelog : https://github.com/linuxserver/docker-librespeed/releases) diff --git a/lidarr/README.md b/lidarr/README.md index 824041f443..3efeabd1db 100644 --- a/lidarr/README.md +++ b/lidarr/README.md @@ -59,7 +59,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/linkwarden/CHANGELOG.md b/linkwarden/CHANGELOG.md index de5e146a8b..5129826d5f 100644 --- a/linkwarden/CHANGELOG.md +++ b/linkwarden/CHANGELOG.md @@ -1,11 +1,11 @@ - + ## 2.16.3 (2026-09-12) - Update to latest version from linkwarden/linkwarden (changelog : https://github.com/linkwarden/linkwarden/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.16.1 (2026-08-22) - Update to latest version from linkwarden/linkwarden (changelog : https://github.com/linkwarden/linkwarden/releases) - + ## 2.16.0 (2026-08-01) - Update to latest version from linkwarden/linkwarden (changelog : https://github.com/linkwarden/linkwarden/releases) ## 2.15.1.2 (2026-07-22) diff --git a/linkwarden/README.md b/linkwarden/README.md index 3a515f1b8a..eee3866ae1 100644 --- a/linkwarden/README.md +++ b/linkwarden/README.md @@ -43,7 +43,7 @@ You'll need to create a new user account at startup. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `NEXTAUTH_SECRET` | str | **Required** | Secret key for NextAuth.js authentication (must be filled at start) | | `NEXTAUTH_URL` | str | | Custom NextAuth URL (optional, only if Linkwarden is kept externally) | | `NEXT_PUBLIC_DISABLE_REGISTRATION` | bool | `false` | Disable new user registration | diff --git a/linkwarden/rootfs/etc/cont-init.d/99-run.sh b/linkwarden/rootfs/etc/cont-init.d/99-run.sh index ac20ed97ec..47ebc68ad7 100755 --- a/linkwarden/rootfs/etc/cont-init.d/99-run.sh +++ b/linkwarden/rootfs/etc/cont-init.d/99-run.sh @@ -32,7 +32,7 @@ fix_linkwarden_path() { # Preserve any data already written to the non-persistent path if [ -d "$resolved_path" ] && [ ! -L "$resolved_path" ]; then - if ! cp -rn "$resolved_path/." "$actual_path/" 2>/dev/null; then + if ! cp -rn "$resolved_path/." "$actual_path/" 2> /dev/null; then bashio::log.warning "Could not migrate existing data from $resolved_path to $actual_path (may be empty or a permissions issue)" fi rm -rf "$resolved_path" diff --git a/maintainerr/CHANGELOG.md b/maintainerr/CHANGELOG.md index c94ce1f9e4..d36ede0f9a 100644 --- a/maintainerr/CHANGELOG.md +++ b/maintainerr/CHANGELOG.md @@ -1,50 +1,50 @@ - + ## 3.28.0 (2026-09-12) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.26.0 (2026-09-05) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.25.0 (2026-08-29) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.24.0 (2026-08-22) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.22.1 (2026-08-13) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.22.0 (2026-08-08) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.21.1 (2026-08-02) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.21.0 (2026-08-01) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.20.1 (2026-08-01) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.18.0 (2026-07-22) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.16.0 (2026-07-04) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.15.3 (2026-06-27) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.15.2 (2026-06-23) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.15.1 (2026-06-20) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.15.0 (2026-06-11) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) - + ## 3.14.0 (2026-06-06) - Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases) diff --git a/maintainerr/config.yaml b/maintainerr/config.yaml index 1ff22e3200..f0d26b289d 100644 --- a/maintainerr/config.yaml +++ b/maintainerr/config.yaml @@ -1,8 +1,7 @@ arch: - aarch64 - amd64 -description: - Rule-based media cleanup tool for Plex, Jellyfin and Emby. Creates collections and optionally deletes unwatched content. +description: Rule-based media cleanup tool for Plex, Jellyfin and Emby. Creates collections and optionally deletes unwatched content. devices: - /dev/dri - /dev/dri/card0 diff --git a/maintainerr/rootfs/ha_entrypoint.sh b/maintainerr/rootfs/ha_entrypoint.sh index 4b99c3832a..070f2227d8 100755 --- a/maintainerr/rootfs/ha_entrypoint.sh +++ b/maintainerr/rootfs/ha_entrypoint.sh @@ -27,9 +27,9 @@ echo "[Maintainerr] Setting up data directory: $DATA_DIR" mkdir -p "$DATA_DIR" "$DATA_DIR/logs" # Copy any seed/existing data from /opt/data to /config/data (don't overwrite) -if [ -d /opt/data ] && [ "$(ls -A /opt/data 2>/dev/null)" ]; then +if [ -d /opt/data ] && [ "$(ls -A /opt/data 2> /dev/null)" ]; then echo "[Maintainerr] Copying existing files from /opt/data to $DATA_DIR..." - cp -rn /opt/data/. "$DATA_DIR/" 2>/dev/null || true + cp -rn /opt/data/. "$DATA_DIR/" 2> /dev/null || true fi # Apply permissions/ownership once so copied files are also covered diff --git a/manyfold/CHANGELOG.md b/manyfold/CHANGELOG.md index 5fbce922a9..5a1e6d829f 100644 --- a/manyfold/CHANGELOG.md +++ b/manyfold/CHANGELOG.md @@ -1,14 +1,14 @@ - + ## 0.149.0 (2026-09-12) - Update to latest version from manyfold3d/manyfold (changelog : https://github.com/manyfold3d/manyfold/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 0.148.0 (2026-08-29) - Update to latest version from manyfold3d/manyfold (changelog : https://github.com/manyfold3d/manyfold/releases) - + ## 0.147.2 (2026-08-15) - Update to latest version from manyfold3d/manyfold (changelog : https://github.com/manyfold3d/manyfold/releases) - + ## 0.147.1 (2026-08-01) - Update to latest version from manyfold3d/manyfold (changelog : https://github.com/manyfold3d/manyfold/releases) ## 0.146.1-3 (2026-07-08) @@ -18,7 +18,7 @@ ## 0.146.1-2 (06-07-2026) - Minor bugs fixed - + ## 0.146.1 (2026-07-06) - Add configurable Manyfold public hostname with Home Assistant external URL and homeassistant.local fallbacks. @@ -44,8 +44,8 @@ - Minor bugs fixed ## 0.140.1 - - Bump version to 0.140.1 - - Detailed changes https://github.com/manyfold3d/manyfold/compare/v0.139.3...v0.140.1 +- Bump version to 0.140.1 +- Detailed changes https://github.com/manyfold3d/manyfold/compare/v0.139.3...v0.140.1 ## 0.139.3 (11-05-2026) @@ -62,7 +62,7 @@ ## 0.137.0 - Bumped upstream Manyfold image to `ghcr.io/manyfold3d/manyfold-solo:0.137.0` for both `amd64` and `aarch64`. -- Add funtionalities about sharing and comment federation, some implemented in 0.136.0 +- Add funtionalities about sharing and comment federation, some implemented in 0.136.0 ## 0.135.0 diff --git a/manyfold/README.md b/manyfold/README.md index 326b1533cc..ee4a0555e9 100644 --- a/manyfold/README.md +++ b/manyfold/README.md @@ -147,7 +147,7 @@ After changing values: **How it works:** | Scenario | Behaviour | -|----------|-----------| +| ---------- | ----------- | | **New install**, option left blank | A random secret is auto-generated and saved to `/config/secret_key_base` | | **Addon update**, option still blank | The previously saved `/config/secret_key_base` is reused — no data loss | | **Option manually set** | The value from the addon options is used and saved to `/config/secret_key_base` | @@ -172,7 +172,7 @@ chmod 600 /app_configs//secret_key_base Replace `` and `` with the actual directory names (e.g. `db21ed7f_manyfold` and `088d77ac_manyfold_solo`). List them with `ls /app_configs/`. -3. Start the new addon — it will pick up the existing database and secret automatically. +1. Start the new addon — it will pick up the existing database and secret automatically. ## Notes diff --git a/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish b/manyfold/rootfs/etc/s6-overlay/s6-rc.d/manyfold/finish old mode 100755 new mode 100644 diff --git a/manyfold/run.sh b/manyfold/run.sh index c86336b5a9..01a493c87e 100755 --- a/manyfold/run.sh +++ b/manyfold/run.sh @@ -17,140 +17,140 @@ DEFAULT_MAX_FILE_EXTRACT_SIZE="1073741824" DEFAULT_PUBLIC_HOSTNAME="homeassistant.local" log() { - echo "[manyfold-addon] $*" + echo "[manyfold-addon] $*" } die() { - echo "[manyfold-addon] ERROR: $*" >&2 - exit 1 + echo "[manyfold-addon] ERROR: $*" >&2 + exit 1 } read_opt() { - local key="$1" - jq -er --arg k "$key" '.[$k] // empty' "$OPTIONS_JSON" 2>/dev/null || true + local key="$1" + jq -er --arg k "$key" '.[$k] // empty' "$OPTIONS_JSON" 2> /dev/null || true } strip_hostname() { - local raw="$1" + local raw="$1" - raw="${raw#http://}" - raw="${raw#https://}" - raw="${raw%%/*}" + raw="${raw#http://}" + raw="${raw#https://}" + raw="${raw%%/*}" - if [[ "$raw" =~ ^(\[[^]]+\])(:[0-9]+)?$ ]]; then - raw="${BASH_REMATCH[1]}" - else - raw="${raw%%:*}" - fi + if [[ "$raw" =~ ^(\[[^]]+\])(:[0-9]+)?$ ]]; then + raw="${BASH_REMATCH[1]}" + else + raw="${raw%%:*}" + fi - printf '%s\n' "$raw" + printf '%s\n' "$raw" } detect_ha_public_hostname() { - local supervisor_url="${SUPERVISOR:-http://supervisor}" - local response external_url external_host + local supervisor_url="${SUPERVISOR:-http://supervisor}" + local response external_url external_host + + if [[ -z "${SUPERVISOR_TOKEN:-}" ]] || ! command -v curl > /dev/null 2>&1; then + printf '%s\n' "$DEFAULT_PUBLIC_HOSTNAME" + return + fi + + response="$(curl -fsSL \ + -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \ + "${supervisor_url}/core/api/config" 2> /dev/null || true)" + external_url="$(jq -er '.external_url // empty' <<< "$response" 2> /dev/null || true)" + external_host="$(strip_hostname "$external_url")" + + if [[ -n "$external_host" && "$external_host" != "null" ]]; then + printf '%s\n' "$external_host" + return + fi - if [[ -z "${SUPERVISOR_TOKEN:-}" ]] || ! command -v curl >/dev/null 2>&1; then printf '%s\n' "$DEFAULT_PUBLIC_HOSTNAME" - return - fi - - response="$(curl -fsSL \ - -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \ - "${supervisor_url}/core/api/config" 2>/dev/null || true)" - external_url="$(jq -er '.external_url // empty' <<< "$response" 2>/dev/null || true)" - external_host="$(strip_hostname "$external_url")" - - if [[ -n "$external_host" && "$external_host" != "null" ]]; then - printf '%s\n' "$external_host" - return - fi - - printf '%s\n' "$DEFAULT_PUBLIC_HOSTNAME" } normalize_path() { - local raw="$1" - if command -v realpath >/dev/null 2>&1; then - realpath -m "$raw" - return - fi + local raw="$1" + if command -v realpath > /dev/null 2>&1; then + realpath -m "$raw" + return + fi - case "$raw" in - /*) printf '%s\n' "$raw" ;; - *) printf '/%s\n' "$raw" ;; - esac + case "$raw" in + /*) printf '%s\n' "$raw" ;; + *) printf '/%s\n' "$raw" ;; + esac } is_allowed_path() { - local resolved="$1" - case "$resolved" in - /share|/share/*|/media|/media/*|/config|/config/*) - return 0 - ;; - *) - return 1 - ;; - esac + local resolved="$1" + case "$resolved" in + /share | /share/* | /media | /media/* | /config | /config/*) + return 0 + ;; + *) + return 1 + ;; + esac } require_mapped_path() { - local label="$1" - local raw="$2" - local resolved + local label="$1" + local raw="$2" + local resolved - resolved="$(normalize_path "$raw")" - if ! is_allowed_path "$resolved"; then - die "${label} '${raw}' resolves to '${resolved}', which is outside /share, /media, and /config" - fi + resolved="$(normalize_path "$raw")" + if ! is_allowed_path "$resolved"; then + die "${label} '${raw}' resolves to '${resolved}', which is outside /share, /media, and /config" + fi - printf '%s\n' "$resolved" + printf '%s\n' "$resolved" } ensure_dir() { - local dir="$1" - mkdir -p "$dir" + local dir="$1" + mkdir -p "$dir" } ensure_existing_or_create() { - local label="$1" - local dir="$2" + local label="$1" + local dir="$2" - if [[ -d "$dir" ]]; then - return - fi + if [[ -d "$dir" ]]; then + return + fi - if mkdir -p "$dir" 2>/dev/null; then - return - fi + if mkdir -p "$dir" 2> /dev/null; then + return + fi - die "${label} '${dir}' does not exist and could not be created. Create it on the host or choose a writable path under /config." + die "${label} '${dir}' does not exist and could not be created. Create it on the host or choose a writable path under /config." } chown_recursive_if_writable() { - local owner="$1" - local path="$2" + local owner="$1" + local path="$2" - if [[ ! -e "$path" ]]; then - log "Skipping ownership update for ${path} (missing path)" - return - fi + if [[ ! -e "$path" ]]; then + log "Skipping ownership update for ${path} (missing path)" + return + fi - if [[ -w "$path" ]]; then - chown -R "$owner" "$path" - return - fi + if [[ -w "$path" ]]; then + chown -R "$owner" "$path" + return + fi - log "Skipping ownership update for ${path} (read-only mapping)" + log "Skipping ownership update for ${path} (read-only mapping)" } generate_secret() { - if command -v openssl >/dev/null 2>&1; then - openssl rand -hex 64 - return - fi + if command -v openssl > /dev/null 2>&1; then + openssl rand -hex 64 + return + fi - head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n' + head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n' } # Manyfold's production.rb sets config.assume_ssl = PUBLIC_HOSTNAME.present?, @@ -162,11 +162,11 @@ generate_secret() { # via an initializer dropped into whichever app directory is found, since we # still need PUBLIC_HOSTNAME set for correct link generation elsewhere. disable_assume_ssl() { - local app_dir="$1" - local initializer_dir="${app_dir}/config/initializers" - [[ -d "$initializer_dir" ]] || return 0 + local app_dir="$1" + local initializer_dir="${app_dir}/config/initializers" + [[ -d "$initializer_dir" ]] || return 0 - cat > "${initializer_dir}/manyfold_addon_disable_assume_ssl.rb" << 'EOF' + cat > "${initializer_dir}/manyfold_addon_disable_assume_ssl.rb" << 'EOF' # Added by the Manyfold Home Assistant add-on: this add-on has no # SSL-terminating reverse proxy in front of it, so assuming SSL produces # incorrect https:// redirects on a plain-HTTP port. @@ -175,73 +175,86 @@ EOF } start_manyfold() { - if [[ -x /usr/src/app/bin/docker-entrypoint.sh ]]; then - log "Starting Manyfold via /usr/src/app/bin/docker-entrypoint.sh foreman start" - cd /usr/src/app - disable_assume_ssl /usr/src/app - exec ./bin/docker-entrypoint.sh foreman start - fi - - if [[ -x /app/bin/docker-entrypoint.sh ]]; then - log "Starting Manyfold via /app/bin/docker-entrypoint.sh foreman start" - cd /app - disable_assume_ssl /app - exec ./bin/docker-entrypoint.sh foreman start - fi - - local candidate - for candidate in \ - /usr/local/bin/docker-entrypoint.sh \ - /usr/local/bin/docker-entrypoint \ - /docker-entrypoint.sh \ - /entrypoint.sh - do - if [[ -x "$candidate" ]]; then - log "Starting Manyfold via ${candidate}" - if [[ "$candidate" == *docker-entrypoint* ]]; then - exec "$candidate" foreman start - fi - exec "$candidate" + if [[ -x /usr/src/app/bin/docker-entrypoint.sh ]]; then + log "Starting Manyfold via /usr/src/app/bin/docker-entrypoint.sh foreman start" + cd /usr/src/app + disable_assume_ssl /usr/src/app + exec ./bin/docker-entrypoint.sh foreman start fi - done - if command -v docker-entrypoint >/dev/null 2>&1; then - log "Starting Manyfold via docker-entrypoint" - exec docker-entrypoint foreman start - fi + if [[ -x /app/bin/docker-entrypoint.sh ]]; then + log "Starting Manyfold via /app/bin/docker-entrypoint.sh foreman start" + cd /app + disable_assume_ssl /app + exec ./bin/docker-entrypoint.sh foreman start + fi - if [[ -d /usr/src/app ]]; then - cd /usr/src/app - disable_assume_ssl /usr/src/app - elif [[ -d /app ]]; then - cd /app - disable_assume_ssl /app - fi + local candidate + for candidate in \ + /usr/local/bin/docker-entrypoint.sh \ + /usr/local/bin/docker-entrypoint \ + /docker-entrypoint.sh \ + /entrypoint.sh; do + if [[ -x "$candidate" ]]; then + log "Starting Manyfold via ${candidate}" + if [[ "$candidate" == *docker-entrypoint* ]]; then + exec "$candidate" foreman start + fi + exec "$candidate" + fi + done - if command -v bundle >/dev/null 2>&1; then - log "Starting Manyfold via rails server fallback" - exec bundle exec rails server -b 0.0.0.0 -p 3214 - fi + if command -v docker-entrypoint > /dev/null 2>&1; then + log "Starting Manyfold via docker-entrypoint" + exec docker-entrypoint foreman start + fi - die "Could not find a known Manyfold entrypoint" + if [[ -d /usr/src/app ]]; then + cd /usr/src/app + disable_assume_ssl /usr/src/app + elif [[ -d /app ]]; then + cd /app + disable_assume_ssl /app + fi + + if command -v bundle > /dev/null 2>&1; then + log "Starting Manyfold via rails server fallback" + exec bundle exec rails server -b 0.0.0.0 -p 3214 + fi + + die "Could not find a known Manyfold entrypoint" } [[ -f "$OPTIONS_JSON" ]] || die "Missing options file at ${OPTIONS_JSON}" -PUID="$(read_opt puid)"; PUID="${PUID:-1000}" -PGID="$(read_opt pgid)"; PGID="${PGID:-1000}" -MULTIUSER="$(read_opt multiuser)"; MULTIUSER="${MULTIUSER:-true}" -LIBRARY_PATH_RAW="$(read_opt library_path)"; LIBRARY_PATH_RAW="${LIBRARY_PATH_RAW:-$DEFAULT_LIBRARY_PATH}" -THUMBNAILS_PATH_RAW="$(read_opt thumbnails_path)"; THUMBNAILS_PATH_RAW="${THUMBNAILS_PATH_RAW:-$DEFAULT_THUMBNAILS_PATH}" -LOG_LEVEL="$(read_opt log_level)"; LOG_LEVEL="${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -WEB_CONCURRENCY="$(read_opt web_concurrency)"; WEB_CONCURRENCY="${WEB_CONCURRENCY:-$DEFAULT_WEB_CONCURRENCY}" -RAILS_MAX_THREADS="$(read_opt rails_max_threads)"; RAILS_MAX_THREADS="${RAILS_MAX_THREADS:-$DEFAULT_RAILS_MAX_THREADS}" -DEFAULT_WORKER_CONCURRENCY="$(read_opt default_worker_concurrency)"; DEFAULT_WORKER_CONCURRENCY="${DEFAULT_WORKER_CONCURRENCY:-$DEFAULT_DEFAULT_WORKER_CONCURRENCY}" -PERFORMANCE_WORKER_CONCURRENCY="$(read_opt performance_worker_concurrency)"; PERFORMANCE_WORKER_CONCURRENCY="${PERFORMANCE_WORKER_CONCURRENCY:-$DEFAULT_PERFORMANCE_WORKER_CONCURRENCY}" -MAX_FILE_UPLOAD_SIZE="$(read_opt max_file_upload_size)"; MAX_FILE_UPLOAD_SIZE="${MAX_FILE_UPLOAD_SIZE:-$DEFAULT_MAX_FILE_UPLOAD_SIZE}" -MAX_FILE_EXTRACT_SIZE="$(read_opt max_file_extract_size)"; MAX_FILE_EXTRACT_SIZE="${MAX_FILE_EXTRACT_SIZE:-$DEFAULT_MAX_FILE_EXTRACT_SIZE}" -SECRET_KEY_BASE="$(read_opt secret_key_base)"; SECRET_KEY_BASE="${SECRET_KEY_BASE:-}" -PUBLIC_HOSTNAME_RAW="$(read_opt public_hostname)"; PUBLIC_HOSTNAME_RAW="${PUBLIC_HOSTNAME_RAW:-}" +PUID="$(read_opt puid)" +PUID="${PUID:-1000}" +PGID="$(read_opt pgid)" +PGID="${PGID:-1000}" +MULTIUSER="$(read_opt multiuser)" +MULTIUSER="${MULTIUSER:-true}" +LIBRARY_PATH_RAW="$(read_opt library_path)" +LIBRARY_PATH_RAW="${LIBRARY_PATH_RAW:-$DEFAULT_LIBRARY_PATH}" +THUMBNAILS_PATH_RAW="$(read_opt thumbnails_path)" +THUMBNAILS_PATH_RAW="${THUMBNAILS_PATH_RAW:-$DEFAULT_THUMBNAILS_PATH}" +LOG_LEVEL="$(read_opt log_level)" +LOG_LEVEL="${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" +WEB_CONCURRENCY="$(read_opt web_concurrency)" +WEB_CONCURRENCY="${WEB_CONCURRENCY:-$DEFAULT_WEB_CONCURRENCY}" +RAILS_MAX_THREADS="$(read_opt rails_max_threads)" +RAILS_MAX_THREADS="${RAILS_MAX_THREADS:-$DEFAULT_RAILS_MAX_THREADS}" +DEFAULT_WORKER_CONCURRENCY="$(read_opt default_worker_concurrency)" +DEFAULT_WORKER_CONCURRENCY="${DEFAULT_WORKER_CONCURRENCY:-$DEFAULT_DEFAULT_WORKER_CONCURRENCY}" +PERFORMANCE_WORKER_CONCURRENCY="$(read_opt performance_worker_concurrency)" +PERFORMANCE_WORKER_CONCURRENCY="${PERFORMANCE_WORKER_CONCURRENCY:-$DEFAULT_PERFORMANCE_WORKER_CONCURRENCY}" +MAX_FILE_UPLOAD_SIZE="$(read_opt max_file_upload_size)" +MAX_FILE_UPLOAD_SIZE="${MAX_FILE_UPLOAD_SIZE:-$DEFAULT_MAX_FILE_UPLOAD_SIZE}" +MAX_FILE_EXTRACT_SIZE="$(read_opt max_file_extract_size)" +MAX_FILE_EXTRACT_SIZE="${MAX_FILE_EXTRACT_SIZE:-$DEFAULT_MAX_FILE_EXTRACT_SIZE}" +SECRET_KEY_BASE="$(read_opt secret_key_base)" +SECRET_KEY_BASE="${SECRET_KEY_BASE:-}" +PUBLIC_HOSTNAME_RAW="$(read_opt public_hostname)" +PUBLIC_HOSTNAME_RAW="${PUBLIC_HOSTNAME_RAW:-}" [[ "$PUID" =~ ^[0-9]+$ ]] || die "puid must be a non-negative integer" [[ "$PGID" =~ ^[0-9]+$ ]] || die "pgid must be a non-negative integer" @@ -256,15 +269,15 @@ LIBRARY_PATH="$(require_mapped_path "library_path" "$LIBRARY_PATH_RAW")" THUMBNAILS_PATH="$(require_mapped_path "thumbnails_path" "$THUMBNAILS_PATH_RAW")" if [[ -n "$PUBLIC_HOSTNAME_RAW" ]]; then - PUBLIC_HOSTNAME="$(strip_hostname "$PUBLIC_HOSTNAME_RAW")" + PUBLIC_HOSTNAME="$(strip_hostname "$PUBLIC_HOSTNAME_RAW")" else - PUBLIC_HOSTNAME="$(detect_ha_public_hostname)" + PUBLIC_HOSTNAME="$(detect_ha_public_hostname)" fi [[ -n "$PUBLIC_HOSTNAME" && "$PUBLIC_HOSTNAME" != "null" ]] || PUBLIC_HOSTNAME="$DEFAULT_PUBLIC_HOSTNAME" case "$THUMBNAILS_PATH" in - /config|/config/*) ;; - *) die "thumbnails_path must resolve under /config for persistence" ;; + /config | /config/*) ;; + *) die "thumbnails_path must resolve under /config for persistence" ;; esac ensure_dir "$CONFIG_DIR" @@ -274,19 +287,19 @@ ensure_dir "$THUMBNAILS_PATH" [[ -r "$LIBRARY_PATH" ]] || die "library_path '${LIBRARY_PATH}' is not readable" if [[ -z "$SECRET_KEY_BASE" ]]; then - if [[ -s "$SECRET_FILE" ]]; then - SECRET_KEY_BASE="$(cat "$SECRET_FILE")" - log "Loaded SECRET_KEY_BASE from ${SECRET_FILE}" - else - SECRET_KEY_BASE="$(generate_secret)" + if [[ -s "$SECRET_FILE" ]]; then + SECRET_KEY_BASE="$(cat "$SECRET_FILE")" + log "Loaded SECRET_KEY_BASE from ${SECRET_FILE}" + else + SECRET_KEY_BASE="$(generate_secret)" + printf '%s' "$SECRET_KEY_BASE" > "$SECRET_FILE" + chmod 600 "$SECRET_FILE" + log "Generated and stored SECRET_KEY_BASE at ${SECRET_FILE}" + fi +else printf '%s' "$SECRET_KEY_BASE" > "$SECRET_FILE" chmod 600 "$SECRET_FILE" - log "Generated and stored SECRET_KEY_BASE at ${SECRET_FILE}" - fi -else - printf '%s' "$SECRET_KEY_BASE" > "$SECRET_FILE" - chmod 600 "$SECRET_FILE" - log "Saved provided SECRET_KEY_BASE to ${SECRET_FILE}" + log "Saved provided SECRET_KEY_BASE to ${SECRET_FILE}" fi export SECRET_KEY_BASE diff --git a/mealie/CHANGELOG.md b/mealie/CHANGELOG.md index 2274e881f6..358b3c88d2 100644 --- a/mealie/CHANGELOG.md +++ b/mealie/CHANGELOG.md @@ -1,21 +1,21 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v3.25.1 (2026-09-05) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) - Build the ingress frontend with pnpm and the upstream lockfile: upstream dropped frontend/yarn.lock in v3.24.0, so the previous yarn install resolved dependencies unpinned and the build broke on a vuetify release without "vuetify/labs/rules" - + ## v3.24.0 (2026-08-29) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) - + ## v3.23.1 (2026-08-22) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) - + ## v3.22.0 (2026-08-01) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) - + ## v3.21.0 (2026-07-25) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) - + ## v3.20.1 (2026-06-27) - Update to latest version from mealie-recipes/mealie (changelog : https://github.com/mealie-recipes/mealie/releases) diff --git a/mealie/README.md b/mealie/README.md index 18d36bcc53..1110d34acb 100644 --- a/mealie/README.md +++ b/mealie/README.md @@ -48,7 +48,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `1000` | Group ID for file permissions | | `PUID` | int | `1000` | User ID for file permissions | | `ssl` | bool | `false` | Enable HTTPS for the web interface | diff --git a/monica/README.md b/monica/README.md index 4fd9a1f065..5356808ba2 100644 --- a/monica/README.md +++ b/monica/README.md @@ -52,7 +52,7 @@ Webui can be found at `:8181`. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `database` | list | `sqlite` | Database type (sqlite/MariaDB_addon/Mysql_external) | | `APP_KEY` | str | | Application encryption key (auto-generated if empty) | | `DB_DATABASE` | str | | Database name (for external MySQL/MariaDB) | diff --git a/monica/rootfs/etc/cont-init.d/99-run.sh b/monica/rootfs/etc/cont-init.d/99-run.sh index 232ec75112..fd6c761d82 100755 --- a/monica/rootfs/etc/cont-init.d/99-run.sh +++ b/monica/rootfs/etc/cont-init.d/99-run.sh @@ -210,7 +210,7 @@ if [[ "${MEILISEARCH_LOCAL}" == true ]]; then bashio::log.error "${message}" if [ -n "${S6_SVSCANCTL_BIN}" ]; then - if ! "${S6_SVSCANCTL_BIN}" -t "${S6_SUPERVISED_DIR}" 2>/dev/null; then + if ! "${S6_SVSCANCTL_BIN}" -t "${S6_SUPERVISED_DIR}" 2> /dev/null; then bashio::log.error "Unable to signal s6-svscanctl to stop services" fi else @@ -225,7 +225,7 @@ if [[ "${MEILISEARCH_LOCAL}" == true ]]; then } meilisearch_ensure_running() { - if kill -0 "${MEILISEARCH_PID}" 2>/dev/null; then + if kill -0 "${MEILISEARCH_PID}" 2> /dev/null; then return 0 fi @@ -245,12 +245,12 @@ if [[ "${MEILISEARCH_LOCAL}" == true ]]; then } MEILISEARCH_CMD=( - env \ - MEILI_ENV="${MEILISEARCH_ENVIRONMENT}" \ - MEILI_NO_ANALYTICS="${MEILISEARCH_NO_ANALYTICS}" \ - meilisearch \ - --http-addr "${MEILISEARCH_ADDR}" \ - --db-path "${MEILISEARCH_DB_PATH}" + env + MEILI_ENV="${MEILISEARCH_ENVIRONMENT}" + MEILI_NO_ANALYTICS="${MEILISEARCH_NO_ANALYTICS}" + meilisearch + --http-addr "${MEILISEARCH_ADDR}" + --db-path "${MEILISEARCH_DB_PATH}" ) if [ -n "${MEILISEARCH_ENV_KEY}" ]; then @@ -262,7 +262,7 @@ if [[ "${MEILISEARCH_LOCAL}" == true ]]; then bashio::log.info "Waiting for Meilisearch TCP socket" for attempt in $(seq 1 30); do - if bash -c "cat < /dev/null > /dev/tcp/${MEILISEARCH_HOST}/${MEILISEARCH_PORT}" 2>/dev/null; then + if bash -c "cat < /dev/null > /dev/tcp/${MEILISEARCH_HOST}/${MEILISEARCH_PORT}" 2> /dev/null; then break fi diff --git a/mylar3/CHANGELOG.md b/mylar3/CHANGELOG.md index 18d7e03451..9b2811c901 100644 --- a/mylar3/CHANGELOG.md +++ b/mylar3/CHANGELOG.md @@ -1,7 +1,7 @@ - + ## 0.11.0 (2026-08-22) - Update to latest version from linuxserver/docker-mylar3 (changelog : https://github.com/linuxserver/docker-mylar3/releases) - + ## 0.10.0 (2026-06-17) - Update to latest version from linuxserver/docker-mylar3 (changelog : https://github.com/linuxserver/docker-mylar3/releases) diff --git a/mylar3/README.md b/mylar3/README.md index 9696fb7fa5..26dbeebad1 100644 --- a/mylar3/README.md +++ b/mylar3/README.md @@ -55,7 +55,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/navidrome/CHANGELOG.md b/navidrome/CHANGELOG.md index e1dd39c4e0..8c24e40af4 100644 --- a/navidrome/CHANGELOG.md +++ b/navidrome/CHANGELOG.md @@ -1,10 +1,10 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 0.63.2 (2026-07-18) - Update to latest version from navidrome/navidrome (changelog : https://github.com/navidrome/navidrome/releases) ## 0.62.0-2 (19-06-2026) - Minor bugs fixed - + ## 0.62.0 (2026-06-11) - Update to latest version from navidrome/navidrome (changelog : https://github.com/navidrome/navidrome/releases) diff --git a/navidrome/README.md b/navidrome/README.md index 6d031bd54b..f11d53c477 100644 --- a/navidrome/README.md +++ b/navidrome/README.md @@ -42,7 +42,7 @@ See https://www.navidrome.org/docs/usage/configuration-options/ for additional c ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `base_url` | str | `/` | Base URL to configure Navidrome behind a proxy | | `music_folder` | str | `/data/music` | Folder where your music library is stored | | `data_folder` | str | `/data` | Folder to store application data (DB) | diff --git a/netalertx/CHANGELOG.md b/netalertx/CHANGELOG.md index 1f7cf264c5..32d40c1cfc 100644 --- a/netalertx/CHANGELOG.md +++ b/netalertx/CHANGELOG.md @@ -1,11 +1,11 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 26.9.0 (2026-09-05) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) - + ## 26.8.5 (2026-08-08) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) - + ## 26.7.1 (2026-07-04) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) ## 26.6.3-1 (2026-06-19) diff --git a/netalertx_fa/CHANGELOG.md b/netalertx_fa/CHANGELOG.md index b602693d62..aecc255aae 100644 --- a/netalertx_fa/CHANGELOG.md +++ b/netalertx_fa/CHANGELOG.md @@ -1,11 +1,11 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 26.9.0 (2026-09-05) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) - + ## 26.8.5 (2026-08-08) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) - + ## 26.7.1 (2026-07-04) - Update to latest version from jokob-sk/NetAlertX (changelog : https://github.com/jokob-sk/NetAlertX/releases) ## 26.6.3-1 (2026-06-19) diff --git a/netbird-server/rootfs/etc/cont-init.d/00-config.sh b/netbird-server/rootfs/etc/cont-init.d/00-config.sh index 9d9a6d95fb..482a2319e4 100755 --- a/netbird-server/rootfs/etc/cont-init.d/00-config.sh +++ b/netbird-server/rootfs/etc/cont-init.d/00-config.sh @@ -8,19 +8,19 @@ set -euo pipefail # ============================================================================== create_or_load_secret() { - local secret_file="$1" - local generator="$2" - local generated="" + local secret_file="$1" + local generator="$2" + local generated="" - if [[ -f "$secret_file" ]]; then - cat "$secret_file" - return - fi + if [[ -f "$secret_file" ]]; then + cat "$secret_file" + return + fi - generated=$(eval "$generator") - echo "$generated" > "$secret_file" - chmod 600 "$secret_file" - echo "$generated" + generated=$(eval "$generator") + echo "$generated" > "$secret_file" + chmod 600 "$secret_file" + echo "$generated" } DATA_DIR="/config/netbird" @@ -34,20 +34,20 @@ RELAY_PORT=8084 RELAY_METRICS_PORT=9093 if [[ -z "$DOMAIN" || "$DOMAIN" == "netbird.example.com" ]]; then - result=$(bashio::api.supervisor GET /core/api/config true || true) - external_host="$(bashio::jq "$result" '.external_url' | cut -d'/' -f3 | cut -d':' -f1)" - internal_host="$(bashio::jq "$result" '.internal_url' | cut -d'/' -f3 | cut -d':' -f1)" + result=$(bashio::api.supervisor GET /core/api/config true || true) + external_host="$(bashio::jq "$result" '.external_url' | cut -d'/' -f3 | cut -d':' -f1)" + internal_host="$(bashio::jq "$result" '.internal_url' | cut -d'/' -f3 | cut -d':' -f1)" - if [[ -n "$external_host" && "$external_host" != "null" ]]; then - DOMAIN="$external_host" - bashio::log.warning "Domain not set; using Home Assistant external_url host: ${DOMAIN}" - elif [[ -n "$internal_host" && "$internal_host" != "null" ]]; then - DOMAIN="$internal_host" - bashio::log.warning "Domain not set; using Home Assistant internal_url host: ${DOMAIN}" - else - bashio::log.error "Set a valid domain in the add-on configuration (domain cannot be empty or netbird.example.com)." - bashio::exit.nok - fi + if [[ -n "$external_host" && "$external_host" != "null" ]]; then + DOMAIN="$external_host" + bashio::log.warning "Domain not set; using Home Assistant external_url host: ${DOMAIN}" + elif [[ -n "$internal_host" && "$internal_host" != "null" ]]; then + DOMAIN="$internal_host" + bashio::log.warning "Domain not set; using Home Assistant internal_url host: ${DOMAIN}" + else + bashio::log.error "Set a valid domain in the add-on configuration (domain cannot be empty or netbird.example.com)." + bashio::exit.nok + fi fi NETBIRD_PORT=443 @@ -56,11 +56,11 @@ NETBIRD_RELAY_PROTO="rels" CADDY_SECURE_DOMAIN=", ${DOMAIN}:${NETBIRD_PORT}" mkdir -p "$DATA_DIR" \ - "$DATA_DIR/management" \ - "$DATA_DIR/secrets" \ - "$DATA_DIR/dashboard" \ - "$DATA_DIR/relay" \ - "$DATA_DIR/caddy" + "$DATA_DIR/management" \ + "$DATA_DIR/secrets" \ + "$DATA_DIR/dashboard" \ + "$DATA_DIR/relay" \ + "$DATA_DIR/caddy" DATASTORE_ENC_KEY=$(create_or_load_secret "$DATA_DIR/secrets/management_datastore_key" "openssl rand -base64 32") RELAY_AUTH_SECRET=$(create_or_load_secret "$DATA_DIR/secrets/relay_auth_secret" "openssl rand -base64 32 | sed 's/=//g'") @@ -68,8 +68,8 @@ RELAY_AUTH_SECRET=$(create_or_load_secret "$DATA_DIR/secrets/relay_auth_secret" # Generate management config if missing MANAGEMENT_CONFIG="$DATA_DIR/management/management.json" if [[ ! -f "$MANAGEMENT_CONFIG" ]]; then - bashio::log.info "Generating management config at ${MANAGEMENT_CONFIG}." - cat < "$MANAGEMENT_CONFIG" + bashio::log.info "Generating management config at ${MANAGEMENT_CONFIG}." + cat << CONFIG > "$MANAGEMENT_CONFIG" { "Stuns": [ { @@ -99,14 +99,14 @@ if [[ ! -f "$MANAGEMENT_CONFIG" ]]; then } CONFIG else - bashio::log.info "Using existing management config at ${MANAGEMENT_CONFIG}." + bashio::log.info "Using existing management config at ${MANAGEMENT_CONFIG}." fi # Generate relay env file if missing RELAY_ENV_FILE="$DATA_DIR/relay/relay.env" if [[ ! -f "$RELAY_ENV_FILE" ]]; then - bashio::log.info "Generating relay env file at ${RELAY_ENV_FILE}." - cat < "$RELAY_ENV_FILE" + bashio::log.info "Generating relay env file at ${RELAY_ENV_FILE}." + cat << CONFIG > "$RELAY_ENV_FILE" NB_LOG_LEVEL=info NB_LISTEN_ADDRESS=:${RELAY_PORT} NB_EXPOSED_ADDRESS=${NETBIRD_RELAY_PROTO}://${DOMAIN}:${NETBIRD_PORT} @@ -118,14 +118,14 @@ NB_STUN_PORTS=${NETBIRD_STUN_PORT} CONFIG fi if ! grep -q '^NB_METRICS_PORT=' "$RELAY_ENV_FILE"; then - echo "NB_METRICS_PORT=${RELAY_METRICS_PORT}" >> "$RELAY_ENV_FILE" + echo "NB_METRICS_PORT=${RELAY_METRICS_PORT}" >> "$RELAY_ENV_FILE" fi # Generate dashboard env file if missing DASHBOARD_ENV_FILE="$DATA_DIR/dashboard/env" if [[ ! -f "$DASHBOARD_ENV_FILE" ]]; then - bashio::log.info "Generating dashboard env file at ${DASHBOARD_ENV_FILE}." - cat < "$DASHBOARD_ENV_FILE" + bashio::log.info "Generating dashboard env file at ${DASHBOARD_ENV_FILE}." + cat << CONFIG > "$DASHBOARD_ENV_FILE" # Endpoints NETBIRD_MGMT_API_ENDPOINT=${NETBIRD_HTTP_PROTOCOL}://${DOMAIN} NETBIRD_MGMT_GRPC_API_ENDPOINT=${NETBIRD_HTTP_PROTOCOL}://${DOMAIN} @@ -143,15 +143,15 @@ NGINX_SSL_PORT=443 # Letsencrypt LETSENCRYPT_DOMAIN=none CONFIG - chmod 600 "$DASHBOARD_ENV_FILE" + chmod 600 "$DASHBOARD_ENV_FILE" fi sed -i 's/^AUTH_SUPPORTED_SCOPES=openid profile email groups$/AUTH_SUPPORTED_SCOPES="openid profile email groups"/' "$DASHBOARD_ENV_FILE" # Generate Caddyfile if missing CADDYFILE="$DATA_DIR/Caddyfile" if [[ ! -f "$CADDYFILE" ]]; then - bashio::log.info "Generating Caddyfile at ${CADDYFILE}." - cat < "$CADDYFILE" + bashio::log.info "Generating Caddyfile at ${CADDYFILE}." + cat << CONFIG > "$CADDYFILE" { servers :80,:443 { protocols h1 h2c h2 h3 @@ -186,7 +186,7 @@ if [[ ! -f "$CADDYFILE" ]]; then } CONFIG else - bashio::log.info "Using existing Caddyfile at ${CADDYFILE}." + bashio::log.info "Using existing Caddyfile at ${CADDYFILE}." fi mkdir -p /run/nginx diff --git a/netbird-server/rootfs/etc/services.d/caddy/run b/netbird-server/rootfs/etc/services.d/caddy/run old mode 100644 new mode 100755 index 6e37ad1778..921885733d --- a/netbird-server/rootfs/etc/services.d/caddy/run +++ b/netbird-server/rootfs/etc/services.d/caddy/run @@ -11,8 +11,8 @@ DATA_DIR="/config/netbird" CADDYFILE="${DATA_DIR}/Caddyfile" if [[ ! -f "$CADDYFILE" ]]; then - bashio::log.error "Missing Caddyfile at ${CADDYFILE}." - bashio::exit.nok + bashio::log.error "Missing Caddyfile at ${CADDYFILE}." + bashio::exit.nok fi export XDG_DATA_HOME="${DATA_DIR}/caddy/data" diff --git a/netbird-server/rootfs/etc/services.d/dashboard/run b/netbird-server/rootfs/etc/services.d/dashboard/run old mode 100644 new mode 100755 index e67341fc93..47b4a56602 --- a/netbird-server/rootfs/etc/services.d/dashboard/run +++ b/netbird-server/rootfs/etc/services.d/dashboard/run @@ -9,13 +9,13 @@ set -euo pipefail ENV_FILE="/config/netbird/dashboard/env" if [[ -f "$ENV_FILE" ]]; then - set -a - # shellcheck disable=SC1090 - . "$ENV_FILE" - set +a + set -a + # shellcheck disable=SC1090 + . "$ENV_FILE" + set +a else - bashio::log.error "Missing dashboard env file at ${ENV_FILE}." - bashio::exit.nok + bashio::log.error "Missing dashboard env file at ${ENV_FILE}." + bashio::exit.nok fi bashio::log.info "Preparing NetBird Dashboard assets..." diff --git a/netbird-server/rootfs/etc/services.d/management/run b/netbird-server/rootfs/etc/services.d/management/run old mode 100644 new mode 100755 index d09c88fef3..677f60e92a --- a/netbird-server/rootfs/etc/services.d/management/run +++ b/netbird-server/rootfs/etc/services.d/management/run @@ -14,18 +14,18 @@ MANAGEMENT_PORT=8081 MANAGEMENT_METRICS_PORT=9091 if [[ ! -f "$MANAGEMENT_CONFIG" ]]; then - bashio::log.error "Missing management configuration at ${MANAGEMENT_CONFIG}." - bashio::exit.nok + bashio::log.error "Missing management configuration at ${MANAGEMENT_CONFIG}." + bashio::exit.nok fi bashio::log.info "Starting NetBird Management..." exec /usr/local/bin/netbird-mgmt management \ - --config "$MANAGEMENT_CONFIG" \ - --port "$MANAGEMENT_PORT" \ - --metrics-port "$MANAGEMENT_METRICS_PORT" \ - --log-level "$LOG_LEVEL" \ - --log-file console \ - --disable-anonymous-metrics=false \ - --single-account-mode-domain=netbird.selfhosted \ - --dns-domain=netbird.selfhosted \ - --idp-sign-key-refresh-enabled + --config "$MANAGEMENT_CONFIG" \ + --port "$MANAGEMENT_PORT" \ + --metrics-port "$MANAGEMENT_METRICS_PORT" \ + --log-level "$LOG_LEVEL" \ + --log-file console \ + --disable-anonymous-metrics=false \ + --single-account-mode-domain=netbird.selfhosted \ + --dns-domain=netbird.selfhosted \ + --idp-sign-key-refresh-enabled diff --git a/netbird-server/rootfs/etc/services.d/relay/run b/netbird-server/rootfs/etc/services.d/relay/run old mode 100644 new mode 100755 index 8685fcd1b0..bf67e04093 --- a/netbird-server/rootfs/etc/services.d/relay/run +++ b/netbird-server/rootfs/etc/services.d/relay/run @@ -10,13 +10,13 @@ set -euo pipefail RELAY_ENV_FILE="/config/netbird/relay/relay.env" if [[ -f "$RELAY_ENV_FILE" ]]; then - set -a - # shellcheck disable=SC1090 - . "$RELAY_ENV_FILE" - set +a + set -a + # shellcheck disable=SC1090 + . "$RELAY_ENV_FILE" + set +a else - bashio::log.error "Missing relay env file at ${RELAY_ENV_FILE}." - bashio::exit.nok + bashio::log.error "Missing relay env file at ${RELAY_ENV_FILE}." + bashio::exit.nok fi bashio::log.info "Starting NetBird Relay..." diff --git a/netbird-server/rootfs/etc/services.d/signal/run b/netbird-server/rootfs/etc/services.d/signal/run old mode 100644 new mode 100755 index 725b733bc1..ddbb022a4a --- a/netbird-server/rootfs/etc/services.d/signal/run +++ b/netbird-server/rootfs/etc/services.d/signal/run @@ -13,7 +13,7 @@ LOG_LEVEL="info" bashio::log.info "Starting NetBird Signal on port ${SIGNAL_PORT} (legacy gRPC on 10000)..." exec /usr/local/bin/netbird-signal run \ - --port "$SIGNAL_PORT" \ - --metrics-port "$SIGNAL_METRICS_PORT" \ - --log-level "$LOG_LEVEL" \ - --log-file console + --port "$SIGNAL_PORT" \ + --metrics-port "$SIGNAL_METRICS_PORT" \ + --log-level "$LOG_LEVEL" \ + --log-file console diff --git a/nextcloud/CHANGELOG.md b/nextcloud/CHANGELOG.md index 085d9d4447..54243136de 100644 --- a/nextcloud/CHANGELOG.md +++ b/nextcloud/CHANGELOG.md @@ -1,17 +1,17 @@ - + ## 34.0.4 (2026-09-12) - Update to latest version from linuxserver/docker-nextcloud (changelog : https://github.com/linuxserver/docker-nextcloud/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 34.0.3 (2026-08-13) - Update to latest version from linuxserver/docker-nextcloud (changelog : https://github.com/linuxserver/docker-nextcloud/releases) - + ## 34.0.2 (2026-07-25) - Update to latest version from linuxserver/docker-nextcloud (changelog : https://github.com/linuxserver/docker-nextcloud/releases) - + ## 34.0.1 (2026-06-27) - Update to latest version from linuxserver/docker-nextcloud (changelog : https://github.com/linuxserver/docker-nextcloud/releases) - + ## 34.0.0 (2026-06-11) - Update to latest version from linuxserver/docker-nextcloud (changelog : https://github.com/linuxserver/docker-nextcloud/releases) ## 33.0.5 (2026-06-05) diff --git a/nextcloud/README.md b/nextcloud/README.md index cf1f5a958e..5332183986 100644 --- a/nextcloud/README.md +++ b/nextcloud/README.md @@ -43,7 +43,7 @@ Webui can be found at `:port`. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `1000` | Group ID for file permissions | | `PUID` | int | `1000` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | @@ -143,7 +143,7 @@ If you use clients for file syncing, the use of SQLite is highly discouraged. and you want to overcome this, follow the below steps: - 1. Install `mariadb` add-on, configure it with some random infos and start it. It is important to start it successfully in order to be seen by `nextcloud` in the network. -- 2. Install `nextcloud` add-on (or restart it if you have already installed), watch the logs until you will notice the following `warning`: +- 1. Install `nextcloud` add-on (or restart it if you have already installed), watch the logs until you will notice the following `warning`: ```bash WARNING: MariaDB addon was found! It can't be configured automatically due to the way Nextcloud works, but you can configure it manually when running the web UI for the first time using those values : @@ -153,8 +153,8 @@ and you want to overcome this, follow the below steps: Host-name : core-mariadb:3306 ``` -- 3. Go back at `mariadb` add-on, configure it with above credentials and restart it. Make sure the add-on is creating the `nextcloud` database. -- 4. Go in the webui and fill all required info. Here you can view an example: +- 1. Go back at `mariadb` add-on, configure it with above credentials and restart it. Make sure the add-on is creating the `nextcloud` database. +- 1. Go in the webui and fill all required info. Here you can view an example: ![image](https://user-images.githubusercontent.com/19391765/207888717-50b43002-a5e2-4782-b5c9-1f582309df2b.png) diff --git a/nginx_webserver_proxy/README.md b/nginx_webserver_proxy/README.md index 803637603c..d5aaf63a66 100644 --- a/nginx_webserver_proxy/README.md +++ b/nginx_webserver_proxy/README.md @@ -35,7 +35,7 @@ This add-on combines a full reverse proxy with a proper static file server, allo ## Configuration | Option | Default | Description | -|--------|---------|-------------| +| -------- | --------- | ------------- | | `static_site_enabled` | `true` | Enable or disable the static file server on port 80 | | `static_site_root` | `/share/www` | Path to serve static files from | | `static_site_prefix` | `/` | URL prefix for the static site (e.g., `/www` for `http://host/www`) | diff --git a/nzbget/CHANGELOG.md b/nzbget/CHANGELOG.md index 65085e96aa..2049532405 100644 --- a/nzbget/CHANGELOG.md +++ b/nzbget/CHANGELOG.md @@ -1,25 +1,25 @@ - + ## v26.3.263 (2026-09-12) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.3-ls263 - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v26.3.262 (2026-09-05) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.3-ls262 - + ## v26.3.261 (2026-08-29) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.3-ls261 - + ## v26.2.260 (2026-08-22) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.2-ls260 - + ## v26.2.258 (2026-08-15) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.2-ls258 - + ## v26.2.257 (2026-08-08) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - Upstream tag : v26.2-ls257 @@ -27,31 +27,31 @@ - Version renamed from `v26.2-ls256`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## v26.2-ls256 (2026-08-01) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.2-ls255 (2026-07-25) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.2-ls254 (2026-07-18) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.2-ls251 (2026-07-04) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.2-ls250 (2026-06-27) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.2-ls249 (2026-06-20) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.1-ls248 (2026-06-13) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.1-ls247 (2026-06-11) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) - + ## v26.1-ls246 (2026-06-06) - Update to latest version from linuxserver/docker-nzbget (changelog : https://github.com/linuxserver/docker-nzbget/releases) diff --git a/nzbget/README.md b/nzbget/README.md index c03ac763c8..e25071618c 100644 --- a/nzbget/README.md +++ b/nzbget/README.md @@ -41,7 +41,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/nzbget/rootfs/etc/services.d/nginx/run b/nzbget/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/script b/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/script old mode 100755 new mode 100644 index 963956c739..e1783817f5 --- a/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/script +++ b/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/script @@ -8,12 +8,12 @@ # run first. NPM's image uses s6-overlay v3 and has no /etc/cont-init.d. if [ ! -L /etc/letsencrypt ]; then - mkdir -p /data/letsencrypt - if [ -d /etc/letsencrypt ] && [ -n "$(ls -A /etc/letsencrypt 2> /dev/null)" ]; then - cp -a /etc/letsencrypt/. /data/letsencrypt/ 2> /dev/null || true - fi - rm -rf /etc/letsencrypt - ln -sf /data/letsencrypt /etc/letsencrypt + mkdir -p /data/letsencrypt + if [ -d /etc/letsencrypt ] && [ -n "$(ls -A /etc/letsencrypt 2>/dev/null)" ]; then + cp -a /etc/letsencrypt/. /data/letsencrypt/ 2>/dev/null || true + fi + rm -rf /etc/letsencrypt + ln -sf /data/letsencrypt /etc/letsencrypt fi mkdir -p /data/letsencrypt diff --git a/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/up b/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/addon-init/up old mode 100755 new mode 100644 diff --git a/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/couchdb/finish b/obsidian_syncserver_npm/rootfs/etc/s6-overlay/s6-rc.d/couchdb/finish old mode 100755 new mode 100644 diff --git a/obsidian_syncserver_solo/CHANGELOG.md b/obsidian_syncserver_solo/CHANGELOG.md index e6f29bbcf4..9964fa3332 100644 --- a/obsidian_syncserver_solo/CHANGELOG.md +++ b/obsidian_syncserver_solo/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.5.2.1 (2026-08-13) - Update to latest version from library/couchdb - Upstream tag : 3.5.2.1-nouveau diff --git a/obsidian_syncserver_ssl/CHANGELOG.md b/obsidian_syncserver_ssl/CHANGELOG.md index abf1f29921..89aaf6fe36 100644 --- a/obsidian_syncserver_ssl/CHANGELOG.md +++ b/obsidian_syncserver_ssl/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 3.5.2.1 (2026-08-13) - Update to latest version from library/couchdb - Upstream tag : 3.5.2.1-nouveau diff --git a/openproject/CHANGELOG.md b/openproject/CHANGELOG.md index 25814d4558..f8f02ea852 100644 --- a/openproject/CHANGELOG.md +++ b/openproject/CHANGELOG.md @@ -1,20 +1,20 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 17.8.0 (2026-09-05) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) - + ## 17.7.2 (2026-08-13) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) - + ## 17.7.1 (2026-08-08) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) - + ## 17.6.0 (2026-07-18) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) - + ## 17.5.1 (2026-06-17) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) - + ## 17.5.0 (2026-06-11) - Update to latest version from opf/openproject (changelog : https://github.com/opf/openproject/releases) diff --git a/photoprism/CHANGELOG.md b/photoprism/CHANGELOG.md index c45b6a9486..70b78f3f95 100644 --- a/photoprism/CHANGELOG.md +++ b/photoprism/CHANGELOG.md @@ -3,7 +3,7 @@ - Version renamed from `ubuntu-2026-07-28`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## ubuntu-2026-07-28 (2026-07-28) - Update to latest version from photoprism/photoprism ## ubuntu-2026-06-01 (2026-06-01) @@ -25,7 +25,7 @@ ## ubuntu-2025-11-30 (2025-11-30) - Update to latest version from photoprism/photoprism -## (2025-12-23) +## (2025-12-23) - Update to latest version from photoprism/photoprism ## ubuntu-2025-11-30-2 (2025-12-06) - Increase healthcheck interval to reduce background CPU usage diff --git a/photoprism/README.md b/photoprism/README.md index 6eb7c2b0a7..c2d8faa18c 100644 --- a/photoprism/README.md +++ b/photoprism/README.md @@ -67,7 +67,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ssl` | bool | `false` | Enable HTTPS for the web interface | | `certfile` | str | `fullchain.pem` | SSL certificate file (must be in /ssl) | | `keyfile` | str | `privkey.pem` | SSL key file (must be in /ssl) | diff --git a/piwigo/README.md b/piwigo/README.md index 356c45301c..8d2d008f59 100644 --- a/piwigo/README.md +++ b/piwigo/README.md @@ -63,7 +63,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/plex/CHANGELOG.md b/plex/CHANGELOG.md index dae7f12d04..8552aa1119 100644 --- a/plex/CHANGELOG.md +++ b/plex/CHANGELOG.md @@ -1,20 +1,20 @@ - + ## 1.43.4.10903.324 (2026-09-12) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - Upstream tag : 1.43.4.10903-e5521bd8c-ls324 - + ## 1.43.3.10896.322 (2026-09-05) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - Upstream tag : 1.43.3.10896-cb3ebc72d-ls322 - + ## 1.43.3.10896.321 (2026-08-22) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - Upstream tag : 1.43.3.10896-cb3ebc72d-ls321 - + ## 1.43.3.10861.319 (2026-08-13) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - Upstream tag : 1.43.3.10861-07dfddaeb-ls319 - + ## 1.43.3.10861.318 (2026-08-08) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - Upstream tag : 1.43.3.10861-07dfddaeb-ls318 @@ -22,19 +22,19 @@ - Version renamed from `1.43.3.10828-00f62d37d-ls316`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## 1.43.3.10828-00f62d37d-ls316 (2026-08-01) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - + ## 1.43.3.10828-00f62d37d-ls315 (2026-07-18) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - + ## 1.43.2.10687-563d026ea-ls311 (2026-07-04) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - + ## 1.43.2.10687-563d026ea-ls310 (2026-06-23) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) - + ## 1.43.2.10687-563d026ea-ls308 (2026-06-11) - Update to latest version from linuxserver/docker-plex (changelog : https://github.com/linuxserver/docker-plex/releases) diff --git a/plex/README.md b/plex/README.md index a5ace650eb..71294d7e3e 100644 --- a/plex/README.md +++ b/plex/README.md @@ -49,7 +49,7 @@ Webui can be found at `:32400`. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/plex/rootfs/etc/cont-init.d/21-folders.sh b/plex/rootfs/etc/cont-init.d/21-folders.sh index 4e0d4e50a1..6ff9091d6b 100755 --- a/plex/rootfs/etc/cont-init.d/21-folders.sh +++ b/plex/rootfs/etc/cont-init.d/21-folders.sh @@ -34,7 +34,7 @@ copy_tree() { local src="$1" local dst="$2" - if command -v rsync >/dev/null 2>&1; then + if command -v rsync > /dev/null 2>&1; then # Trailing slashes are important: copy contents into dst rsync -aH --numeric-ids --inplace --info=progress2 "${src%/}/" "${dst%/}/" else @@ -50,7 +50,7 @@ copy_tree() { LAST_LOCATION="" if [[ -f "$LAST_FILE" ]]; then - LAST_LOCATION="$(cat "$LAST_FILE" 2>/dev/null || true)" + LAST_LOCATION="$(cat "$LAST_FILE" 2> /dev/null || true)" fi OLD_DIR="" diff --git a/portainer/CHANGELOG.md b/portainer/CHANGELOG.md index 6aec367e46..79126550ab 100644 --- a/portainer/CHANGELOG.md +++ b/portainer/CHANGELOG.md @@ -1,14 +1,14 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.45.0 (2026-08-29) - Update to latest version from portainer/portainer (changelog : https://github.com/portainer/portainer/releases) - + ## 2.44.0.1 (2026-08-05) - Ingress: request uncompressed responses from Portainer so they carry a Content-Length and stay on the Home Assistant relay's buffered path. Portainer compresses its own responses, and the Accept-Encoding strip in proxy_params.conf never applied because the location block declares its own proxy_set_header directives, which makes nginx discard the server-level ones. Partial mitigation for #2766: the page itself and all assets under 4 MB are now buffered, but the two large JavaScript bundles still exceed the relay's 4 MB buffering threshold. Direct access on the web UI port keeps compression - + ## 2.44.0 (2026-08-01) - Update to latest version from portainer/portainer (changelog : https://github.com/portainer/portainer/releases) - + ## 2.43.0.1 (2026-07-24) - Ingress: disable nginx gzip re-compression of proxied responses so Home Assistant relays them via the buffered path (addresses 502 Bad Gateway via ingress, #2766) diff --git a/portainer/README.md b/portainer/README.md index 864a08ceba..d2711ba336 100644 --- a/portainer/README.md +++ b/portainer/README.md @@ -76,7 +76,7 @@ The default username is "admin" and the password is described in the startup log ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ssl` | bool | `false` | Enable HTTPS for web interface | | `certfile` | str | `fullchain.pem` | SSL certificate file (in `/ssl/`) | | `keyfile` | str | `privkey.pem` | SSL private key file (in `/ssl/`) | diff --git a/portainer_agent/CHANGELOG.md b/portainer_agent/CHANGELOG.md index 256b82f833..7155224635 100644 --- a/portainer_agent/CHANGELOG.md +++ b/portainer_agent/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 2.44.0.2 (2026-08-29) - Update to latest version from portainer/agent - Upstream tag : sts @@ -59,7 +59,7 @@ ## alpine-sts (2025-12-24) - Update to latest version from portainer/agent -## (2025-12-23) +## (2025-12-23) - Update to latest version from portainer/agent - The Home Assistant project has deprecated support for the armv7, armhf and i386 architectures. Support wil be fully dropped in the upcoming Home Assistant 2025.12 release diff --git a/portainer_agent/rootfs/etc/services.d/portainer_agent/run b/portainer_agent/rootfs/etc/services.d/portainer_agent/run index fd47fc233f..0a679c1f79 100755 --- a/portainer_agent/rootfs/etc/services.d/portainer_agent/run +++ b/portainer_agent/rootfs/etc/services.d/portainer_agent/run @@ -12,7 +12,7 @@ export HOSTNAME="${container_name}" args=() if bashio::config.has_value 'PORTAINER_AGENT_ARGS'; then - read -r -a args <<<"$(bashio::config 'PORTAINER_AGENT_ARGS')" + read -r -a args <<< "$(bashio::config 'PORTAINER_AGENT_ARGS')" fi ./agent "${args[@]}" diff --git a/portainer_agent/rootfs/usr/sbin/healthcheck b/portainer_agent/rootfs/usr/sbin/healthcheck index 0fbd41134e..2fd71dc565 100644 --- a/portainer_agent/rootfs/usr/sbin/healthcheck +++ b/portainer_agent/rootfs/usr/sbin/healthcheck @@ -5,7 +5,7 @@ # would fail before this script runs. Nothing here needs the s6 environment. if [ -f "/tmp/healthcheck-signal" ]; then - curl -k --fail https://127.0.0.1:9001/ping || exit 1 + curl -k --fail https://127.0.0.1:9001/ping || exit 1 else - touch /tmp/healthcheck-signal -fi \ No newline at end of file + touch /tmp/healthcheck-signal +fi diff --git a/portainer_be/CHANGELOG.md b/portainer_be/CHANGELOG.md index b415e3a557..6b8b11f689 100644 --- a/portainer_be/CHANGELOG.md +++ b/portainer_be/CHANGELOG.md @@ -3,7 +3,7 @@ - Version renamed from `sts`, which Home Assistant could not order and therefore could not reliably offer as an update: the previous version held no number that could order it, so the date of this change is used. The addon itself and the upstream version it tracks are unchanged - + ## sts (2026-08-01) - Update to latest version from portainer/portainer-ee ## 2.43.0 (2026-07-27) diff --git a/portainer_be/README.md b/portainer_be/README.md index eaf76195ae..479b04dd8b 100644 --- a/portainer_be/README.md +++ b/portainer_be/README.md @@ -77,7 +77,7 @@ The default username is "admin" and the password is the value you set in the add ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `ssl` | bool | `false` | Enable HTTPS for web interface | | `certfile` | str | `fullchain.pem` | SSL certificate file (in `/ssl/`) | | `keyfile` | str | `privkey.pem` | SSL private key file (in `/ssl/`) | diff --git a/postgres_15/README.md b/postgres_15/README.md index 7fc6295ff0..b8fc830c7d 100644 --- a/postgres_15/README.md +++ b/postgres_15/README.md @@ -41,7 +41,7 @@ Default user: `postgres`, password: set by `POSTGRES_PASSWORD` ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `POSTGRES_PASSWORD` | password | `homeassistant` | Password for the postgres user | | `POSTGRES_USER` | str | | Optional custom username | | `POSTGRES_DB` | str | | Optional default database name | diff --git a/postgres_15/rootfs/etc/cont-init.d/99-run.sh b/postgres_15/rootfs/etc/cont-init.d/99-run.sh index d3acf9b187..c14d44903b 100755 --- a/postgres_15/rootfs/etc/cont-init.d/99-run.sh +++ b/postgres_15/rootfs/etc/cont-init.d/99-run.sh @@ -108,7 +108,7 @@ wait_for_postgres() { while ! pg_isready -h "$DB_HOSTNAME" -p "$DB_PORT" -U "$DB_USERNAME" > /dev/null 2>&1; do tries=$((tries + 1)) [ "$tries" -ge 60 ] && { - bashio::log.error "Postgres did not start after 2 minutes, aborting." + bashio::log.error "Postgres did not start after 2 minutes, aborting." exit 1 } echo "PostgreSQL is starting up... ($tries/60)" diff --git a/prowlarr/CHANGELOG.md b/prowlarr/CHANGELOG.md index 025dc98fb0..9688c41aa3 100644 --- a/prowlarr/CHANGELOG.md +++ b/prowlarr/CHANGELOG.md @@ -1,33 +1,33 @@ - + ## 2.6.4.5611.15 (2026-09-12) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.4.5611-ls15 - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.6.3.5608.14 (2026-09-05) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.3.5608-ls14 - + ## 2.6.2.5583.12 (2026-08-29) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5583-ls12 - + ## 2.6.2.5581.11 (2026-08-29) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5581-ls11 - + ## 2.6.2.5558.10 (2026-08-22) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5558-ls10 - + ## 2.6.2.5548.10 (2026-08-13) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5548-ls10 - + ## 2.6.2.5534.9 (2026-08-11) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5534-ls9 - + ## 2.6.2.5517.9 (2026-08-02) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - Upstream tag : nightly-2.6.2.5517-ls9 @@ -35,19 +35,19 @@ - Version renamed from `nightly-2.6.1.5509-ls8`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## nightly-2.6.1.5509-ls8 (2026-08-01) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## nightly-2.6.0.5494-ls8 (2026-07-25) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## nightly-2.5.2.5487-ls8 (2026-07-22) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## nightly-2.5.1.5478-ls7 (2026-07-18) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## develop-2.5.1.5464-ls268 (2026-07-13) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) @@ -57,16 +57,16 @@ ## nightly-2.5.0.5453-ls4 (2026-07-04) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## nightly-2.5.0.5443-ls4 (2026-06-29) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) ## nightly-2.5.0.5422-ls3 (2026-06-27) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## nightly-2.5.0.5422-ls2 (2026-06-20) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) - + ## 2.4.0.5397-ls149 (2026-06-11) - Update to latest version from linuxserver/docker-prowlarr (changelog : https://github.com/linuxserver/docker-prowlarr/releases) ## nightly-2.4.0.5391-ls238 (2026-06-05) diff --git a/prowlarr/README.md b/prowlarr/README.md index 1f026d30e5..206010561f 100644 --- a/prowlarr/README.md +++ b/prowlarr/README.md @@ -59,7 +59,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/qbittorrent/README.md b/qbittorrent/README.md index 2ea9c72a7d..09df0312d5 100644 --- a/qbittorrent/README.md +++ b/qbittorrent/README.md @@ -55,7 +55,7 @@ Network disk is mounted to `/mnt/`. You need to map the exposed port ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/qbittorrent/rootfs/etc/cont-init.d/93-openvpn.sh b/qbittorrent/rootfs/etc/cont-init.d/93-openvpn.sh index 8633755cf5..c1fb45286d 100755 --- a/qbittorrent/rootfs/etc/cont-init.d/93-openvpn.sh +++ b/qbittorrent/rootfs/etc/cont-init.d/93-openvpn.sh @@ -55,7 +55,7 @@ if bashio::config.has_value "openvpn_config"; then fi if [[ -z "${openvpn_config}" ]]; then bashio::log.info 'openvpn_config option left empty. Attempting automatic selection.' - mapfile -t configs < <(find /config/openvpn -maxdepth 1 \( -type f -name '*.conf' -o -name '*.ovpn' \) -print) + mapfile -t configs < <(find /config/openvpn -maxdepth 1 \( -type f -name '*.conf' -o -name '*.ovpn' \) -print) if [ "${#configs[@]}" -eq 0 ]; then bashio::log.fatal 'OpenVPN is enabled but no .conf or .ovpn file was found in /config/openvpn.' bashio::addon.stop @@ -91,7 +91,7 @@ openvpn_runtime_config="${OPENVPN_STATE_DIR}/${interface_name}.conf" cp "${openvpn_config}" "${openvpn_runtime_config}" chmod 600 "${openvpn_runtime_config}" -dos2unix "${openvpn_runtime_config}" >/dev/null 2>&1 || true +dos2unix "${openvpn_runtime_config}" > /dev/null 2>&1 || true sed -i '/^[[:space:]]*[;#]/d' "${openvpn_runtime_config}" sed -i 's/#.*//' "${openvpn_runtime_config}" sed -i '/^[[:space:]]*$/d' "${openvpn_runtime_config}" diff --git a/qbittorrent/rootfs/etc/cont-init.d/94-wireguard.sh b/qbittorrent/rootfs/etc/cont-init.d/94-wireguard.sh index 833084bd48..ec9a1702bb 100755 --- a/qbittorrent/rootfs/etc/cont-init.d/94-wireguard.sh +++ b/qbittorrent/rootfs/etc/cont-init.d/94-wireguard.sh @@ -36,7 +36,7 @@ if bashio::config.has_value "wireguard_config"; then fi if [[ -z "${wireguard_config}" ]]; then bashio::log.info 'wireguard_config option left empty. Attempting automatic selection.' - mapfile -t configs < <(find /config/wireguard -maxdepth 1 -type f -name '*.conf' -print) + mapfile -t configs < <(find /config/wireguard -maxdepth 1 -type f -name '*.conf' -print) if [ "${#configs[@]}" -eq 0 ]; then bashio::log.fatal 'WireGuard is enabled but no .conf file was found in /config/wireguard.' bashio::addon.stop @@ -71,7 +71,7 @@ wireguard_runtime_config="${WIREGUARD_STATE_DIR}/${interface_name}.conf" cp "${wireguard_config}" "${wireguard_runtime_config}" chmod 600 "${wireguard_runtime_config}" -dos2unix "${wireguard_runtime_config}" >/dev/null 2>&1 || true +dos2unix "${wireguard_runtime_config}" > /dev/null 2>&1 || true sed -i '/^[[:space:]]*[;#]/d' "${wireguard_runtime_config}" sed -i 's/#.*//' "${wireguard_runtime_config}" sed -i '/^[[:space:]]*$/d' "${wireguard_runtime_config}" diff --git a/qbittorrent/rootfs/etc/cont-init.d/95-vpn-monitor.sh b/qbittorrent/rootfs/etc/cont-init.d/95-vpn-monitor.sh index 1e9f430032..8a62c1b9a4 100755 --- a/qbittorrent/rootfs/etc/cont-init.d/95-vpn-monitor.sh +++ b/qbittorrent/rootfs/etc/cont-init.d/95-vpn-monitor.sh @@ -23,7 +23,7 @@ _fetch_public_ip() { mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf) # Loop through the now-randomized list for url in "${shuffled_urls[@]}"; do - resp=$(curl -fsS --max-time 5 "${url}" 2>/dev/null || true) + resp=$(curl -fsS --max-time 5 "${url}" 2> /dev/null || true) resp="${resp//[[:space:]]/}" # Validate IPv4 or IPv6 diff --git a/qbittorrent/rootfs/etc/s6-overlay/s6-rc.d/svc-qbittorrent/run b/qbittorrent/rootfs/etc/s6-overlay/s6-rc.d/svc-qbittorrent/run old mode 100644 new mode 100755 diff --git a/qbittorrent/rootfs/etc/services.d/timer/run b/qbittorrent/rootfs/etc/services.d/timer/run old mode 100644 new mode 100755 diff --git a/qbittorrent/rootfs/etc/services.d/vpn-monitor/run b/qbittorrent/rootfs/etc/services.d/vpn-monitor/run index 707b20f628..d454796aa2 100755 --- a/qbittorrent/rootfs/etc/services.d/vpn-monitor/run +++ b/qbittorrent/rootfs/etc/services.d/vpn-monitor/run @@ -33,7 +33,7 @@ _fetch_public_ip() { mapfile -t shuffled_urls < <(printf "%s\n" "${urls[@]}" | shuf) for url in "${shuffled_urls[@]}"; do - resp=$(curl -fsS --max-time 5 "${curl_iface_opts[@]}" "${url}" 2>/dev/null || true) + resp=$(curl -fsS --max-time 5 "${curl_iface_opts[@]}" "${url}" 2> /dev/null || true) resp="${resp//[[:space:]]/}" if [[ "${resp}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ "${resp}" =~ ^[0-9a-fA-F:]+$ ]]; then @@ -81,7 +81,7 @@ get_ip_info() { url="${info_url%/}/${ip}/json" fi - if json="$(curl -fsS --max-time 10 "${url}" 2>/dev/null || true)" && [[ -n "${json}" ]]; then + if json="$(curl -fsS --max-time 10 "${url}" 2> /dev/null || true)" && [[ -n "${json}" ]]; then country="$( printf '%s\n' "${json}" \ | sed -n 's/.*"country"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \ @@ -151,7 +151,7 @@ elif [[ "${vpn_wireguard}" == true ]]; then VPN_INTERFACE=$(cat "/var/run/wireguard/interface") bashio::log.info "VPN monitor set to query external IP through interface ${VPN_INTERFACE} (interface binding)." fi -if [[ -z "${VPN_INTERFACE}" ]] || ! ip link show "${VPN_INTERFACE}" > /dev/null 2>&1 ; then +if [[ -z "${VPN_INTERFACE}" ]] || ! ip link show "${VPN_INTERFACE}" > /dev/null 2>&1; then bashio::log.error "VPN interface not found." bashio::addon.stop exit 1 @@ -178,7 +178,8 @@ read -r VPN_IP VPN_COUNTRY <<< "${VPN_INFO_OUT}" bashio::log.info "VPN external IP: ${VPN_IP} (${VPN_COUNTRY})" while true; do - sleep "${VPN_CHECK_INTERVAL}" & wait $! + sleep "${VPN_CHECK_INTERVAL}" & + wait $! if ! current_out="$(get_ip_info)"; then bashio::log.warning "Failed to refresh external IP; keeping previous assumptions." diff --git a/qbittorrent/rootfs/etc/services.d/vpn-upnp/run b/qbittorrent/rootfs/etc/services.d/vpn-upnp/run old mode 100644 new mode 100755 index 9aba7d6705..dd3c6cd87b --- a/qbittorrent/rootfs/etc/services.d/vpn-upnp/run +++ b/qbittorrent/rootfs/etc/services.d/vpn-upnp/run @@ -100,7 +100,7 @@ _pnat_get_gw() { for n in {1..254}; do local try_ip="$(echo "${ipv4}" | cut -d'.' -f1-3).${n}" if [ "${try_ip}" != "${ipv4}" ]; then - if nc -4 -vw1 "${try_ip}" 1 &>/dev/null 2>&1; then + if nc -4 -vw1 "${try_ip}" 1 &> /dev/null 2>&1; then config["IPv4Gateway"]=${try_ip} return 0 fi @@ -174,7 +174,7 @@ _qbt_check_sid() { fi } -_qbt_isreachable(){ +_qbt_isreachable() { _cmd "nc -4 -vw 5 ${config["Server"]} ${config["Port"]} &>/dev/null 2>&1" return $? } @@ -249,13 +249,13 @@ pnat_set() { _parse_config "/config/qBittorrent/qBittorrent.conf" -config["Interval"]=$(( ${VPN_UPNP_INTERVAL} + (${VPN_UPNP_INTERVAL} / 2) )) +config["Interval"]=$((${VPN_UPNP_INTERVAL} + (${VPN_UPNP_INTERVAL} / 2))) bashio::log.info "VPN UPnP enabled, starting NAT-PMP/UPnP monitor with an interval of ${VPN_UPNP_INTERVAL} seconds (UPnP lease interval: ${config["Interval"]} seconds)." -while true; -do - sleep ${VPN_UPNP_INTERVAL} & wait $! +while true; do + sleep ${VPN_UPNP_INTERVAL} & + wait $! if pnat_set; then bashio::log.info "NAT-PMP/UPnP Ok!" diff --git a/qbittorrent/rootfs/usr/local/sbin/vpn b/qbittorrent/rootfs/usr/local/sbin/vpn old mode 100755 new mode 100644 diff --git a/radarr/CHANGELOG.md b/radarr/CHANGELOG.md index 4574a72f4d..dd871aa146 100644 --- a/radarr/CHANGELOG.md +++ b/radarr/CHANGELOG.md @@ -1,12 +1,12 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 6.3.0.10514.1 (2026-08-11) - Bump the addon version so Home Assistant offers the rebuilt image. The `6.3.0.10514` tag was rebuilt on 2026-08-03 and now ships LinuxServer.io `ls313` instead of the `ls311` build that existing installations pulled, but because the addon version string was unchanged the Supervisor saw nothing new and never offered the update. Radarr itself is still 6.3.0.10514 — only the LinuxServer.io base image moved ## 6.3.0.10514 (2026-07-13) - Update to latest version from linuxserver/docker-radarr (changelog : https://github.com/linuxserver/docker-radarr/releases) - + ## 6.2.1.10461 (2026-06-11) - Update to latest version from linuxserver/docker-radarr (changelog : https://github.com/linuxserver/docker-radarr/releases) diff --git a/radarr/README.md b/radarr/README.md index c08f0e2b28..7430bc9081 100644 --- a/radarr/README.md +++ b/radarr/README.md @@ -60,7 +60,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/readarr/README.md b/readarr/README.md index 3d11a0cc20..dedde6f304 100644 --- a/readarr/README.md +++ b/readarr/README.md @@ -57,7 +57,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/readarr/rootfs/etc/services.d/nginx/run b/readarr/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/requestrr/README.md b/requestrr/README.md index 57d9dbf137..d565bdc886 100644 --- a/requestrr/README.md +++ b/requestrr/README.md @@ -60,7 +60,7 @@ Run the addon first, then customize the config file created in `/app_configs/db2 ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/sabnzbd/CHANGELOG.md b/sabnzbd/CHANGELOG.md index 6d6b533dd9..b06f155aeb 100644 --- a/sabnzbd/CHANGELOG.md +++ b/sabnzbd/CHANGELOG.md @@ -1,11 +1,11 @@ - + ## 5.1.3 (2026-09-12) - Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 5.1.2 (2026-08-29) - Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases) - + ## 5.1.1.3 (2026-08-25) - Fixed ingress returning `403 External internet access denied` when Home Assistant is reached from outside the local network. SABnzbd's `verify_xff_header` option, which is on by default, refuses any request whose `X-Forwarded-For` chain contains a non-local address, so the proxy no longer forwards that header. @@ -15,10 +15,10 @@ ## 5.1.1 (2026-08-22) - Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases) - + ## 5.1.0 (2026-08-13) - Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases) - + ## 5.0.4 (2026-06-11) - Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases) diff --git a/sabnzbd/README.md b/sabnzbd/README.md index b1da4f58c6..ecab2b822b 100644 --- a/sabnzbd/README.md +++ b/sabnzbd/README.md @@ -41,7 +41,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/sabnzbd/rootfs/etc/services.d/nginx/finish b/sabnzbd/rootfs/etc/services.d/nginx/finish old mode 100755 new mode 100644 diff --git a/scrutiny/CHANGELOG.md b/scrutiny/CHANGELOG.md index 08ad197c65..c1062f3e56 100644 --- a/scrutiny/CHANGELOG.md +++ b/scrutiny/CHANGELOG.md @@ -1,20 +1,20 @@ - + ## v1.74.1 (2026-09-12) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v1.70.0 (2026-08-29) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.69.1 (2026-08-22) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.69.0 (2026-08-15) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.68.0 (2026-08-01) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.67.0-8 (2026-07-18) - Keep content-level backup validation and atomic replacement tests while removing a blocking synthetic FIFO test fixture from cross-architecture builds. @@ -32,19 +32,19 @@ ## v1.67.0 (2026-07-18) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.66.0 (2026-07-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.65.0 (2026-07-04) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.64.0 (2026-06-20) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.63.0 (2026-06-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.62.4 (2026-06-11) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) ## v1.62.3 (2026-06-05) diff --git a/scrutiny/README.md b/scrutiny/README.md index b42cb0af37..de81a11025 100644 --- a/scrutiny/README.md +++ b/scrutiny/README.md @@ -52,7 +52,7 @@ It automatically mounts all local drives. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `Updates` | list | `Hourly` | Update schedule (Quarterly/Hourly/Daily/Weekly/Custom) | | `Updates_custom_time` | str | | Custom update interval (e.g., "5m", "2h", "1w", "2mo") | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/scrutiny/rootfs/usr/local/bin/scrutiny-ha-influxdb-preflight b/scrutiny/rootfs/usr/local/bin/scrutiny-ha-influxdb-preflight index 7d99aac7d8..1630139b71 100644 --- a/scrutiny/rootfs/usr/local/bin/scrutiny-ha-influxdb-preflight +++ b/scrutiny/rootfs/usr/local/bin/scrutiny-ha-influxdb-preflight @@ -15,212 +15,212 @@ source_manifest="" archive_manifest="" log() { - local level="$1" - local message="$2" - printf 'time="%s" level=%s msg="%s" type=ha-influxdb-upgrade-preflight\n' \ - "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$message" + local level="$1" + local message="$2" + printf 'time="%s" level=%s msg="%s" type=ha-influxdb-upgrade-preflight\n' \ + "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$message" } cleanup() { - rm -f "$temporary_backup" "$marker_tmp" - if [[ -n "$source_manifest" ]]; then rm -f "$source_manifest"; fi - if [[ -n "$archive_manifest" ]]; then rm -f "$archive_manifest"; fi + rm -f "$temporary_backup" "$marker_tmp" + if [[ -n "$source_manifest" ]]; then rm -f "$source_manifest"; fi + if [[ -n "$archive_manifest" ]]; then rm -f "$archive_manifest"; fi } trap cleanup EXIT has_existing_data() { - if [[ -e "${data_dir}/influxd.bolt" || -e "${data_dir}/influxd.sqlite" ]]; then - return 0 - fi + if [[ -e "${data_dir}/influxd.bolt" || -e "${data_dir}/influxd.sqlite" ]]; then + return 0 + fi - if [[ -d "${data_dir}/engine" ]] && find "${data_dir}/engine" -mindepth 1 -print -quit | grep -q .; then - return 0 - fi + if [[ -d "${data_dir}/engine" ]] && find "${data_dir}/engine" -mindepth 1 -print -quit | grep -q .; then + return 0 + fi - return 1 + return 1 } validate_archive() { - local archive="$1" - [[ -s "$archive" ]] || return 1 - gzip -t "$archive" >/dev/null 2>&1 || return 1 - tar -tzf "$archive" >/dev/null 2>&1 + local archive="$1" + [[ -s "$archive" ]] || return 1 + gzip -t "$archive" >/dev/null 2>&1 || return 1 + tar -tzf "$archive" >/dev/null 2>&1 } reset_manifests() { - rm -f "$source_manifest" "$archive_manifest" - source_manifest="" - archive_manifest="" + rm -f "$source_manifest" "$archive_manifest" + source_manifest="" + archive_manifest="" } validate_backup_against_source() { - local archive="$1" - local relative_path - local source_path - local archive_member - local listing - local source_checksum - local archive_checksum - local source_target - local archive_target + local archive="$1" + local relative_path + local source_path + local archive_member + local listing + local source_checksum + local archive_checksum + local source_target + local archive_target - validate_archive "$archive" || return 1 - source_manifest="$(mktemp)" - archive_manifest="$(mktemp)" + validate_archive "$archive" || return 1 + source_manifest="$(mktemp)" + archive_manifest="$(mktemp)" - ( - cd "$data_dir" - find . -mindepth 1 -print | sed -e 's#^\./##' -e 's#/$##' | LC_ALL=C sort - ) > "$source_manifest" + ( + cd "$data_dir" + find . -mindepth 1 -print | sed -e 's#^\./##' -e 's#/$##' | LC_ALL=C sort + ) >"$source_manifest" - tar -tzf "$archive" | \ - sed -e "s#^${archive_root}/##" -e '/^$/d' -e 's#/$##' | \ - LC_ALL=C sort > "$archive_manifest" + tar -tzf "$archive" | + sed -e "s#^${archive_root}/##" -e '/^$/d' -e 's#/$##' | + LC_ALL=C sort >"$archive_manifest" - if ! cmp -s "$source_manifest" "$archive_manifest"; then - reset_manifests - return 1 - fi + if ! cmp -s "$source_manifest" "$archive_manifest"; then + reset_manifests + return 1 + fi - while IFS= read -r relative_path; do - source_path="${data_dir}/${relative_path}" - archive_member="${archive_root}/${relative_path}" - listing="$(tar -tvzf "$archive" -- "$archive_member" 2>/dev/null)" || { - reset_manifests - return 1 - } + while IFS= read -r relative_path; do + source_path="${data_dir}/${relative_path}" + archive_member="${archive_root}/${relative_path}" + listing="$(tar -tvzf "$archive" -- "$archive_member" 2>/dev/null)" || { + reset_manifests + return 1 + } - if [[ -L "$source_path" ]]; then - [[ "${listing:0:1}" == "l" ]] || { - reset_manifests - return 1 - } - source_target="$(readlink "$source_path")" - archive_target="${listing##* -> }" - [[ "$source_target" == "$archive_target" ]] || { - reset_manifests - return 1 - } - elif [[ -f "$source_path" ]]; then - [[ "${listing:0:1}" == "-" || "${listing:0:1}" == "h" ]] || { - reset_manifests - return 1 - } - source_checksum="$(sha256sum "$source_path" | awk '{print $1}')" - archive_checksum="$(tar -xOzf "$archive" -- "$archive_member" | sha256sum | awk '{print $1}')" - [[ "$source_checksum" == "$archive_checksum" ]] || { - reset_manifests - return 1 - } - elif [[ -d "$source_path" ]]; then - [[ "${listing:0:1}" == "d" ]] || { - reset_manifests - return 1 - } - else - reset_manifests - return 1 - fi - done < "$source_manifest" + if [[ -L "$source_path" ]]; then + [[ "${listing:0:1}" == "l" ]] || { + reset_manifests + return 1 + } + source_target="$(readlink "$source_path")" + archive_target="${listing##* -> }" + [[ "$source_target" == "$archive_target" ]] || { + reset_manifests + return 1 + } + elif [[ -f "$source_path" ]]; then + [[ "${listing:0:1}" == "-" || "${listing:0:1}" == "h" ]] || { + reset_manifests + return 1 + } + source_checksum="$(sha256sum "$source_path" | awk '{print $1}')" + archive_checksum="$(tar -xOzf "$archive" -- "$archive_member" | sha256sum | awk '{print $1}')" + [[ "$source_checksum" == "$archive_checksum" ]] || { + reset_manifests + return 1 + } + elif [[ -d "$source_path" ]]; then + [[ "${listing:0:1}" == "d" ]] || { + reset_manifests + return 1 + } + else + reset_manifests + return 1 + fi + done <"$source_manifest" - reset_manifests + reset_manifests } marker_value() { - local key="$1" - awk -F= -v key="$key" '$1 == key {sub(/^[^=]*=/, ""); print; exit}' "$marker_file" + local key="$1" + awk -F= -v key="$key" '$1 == key {sub(/^[^=]*=/, ""); print; exit}' "$marker_file" } write_marker() { - local state="$1" - local checksum="${2:-}" - local validation="${3:-}" + local state="$1" + local checksum="${2:-}" + local validation="${3:-}" - { - printf 'state=%s\n' "$state" - if [[ -n "$validation" ]]; then printf 'validation=%s\n' "$validation"; fi - if [[ -n "$checksum" ]]; then printf 'backup_sha256=%s\n' "$checksum"; fi - } > "$marker_tmp" - mv "$marker_tmp" "$marker_file" + { + printf 'state=%s\n' "$state" + if [[ -n "$validation" ]]; then printf 'validation=%s\n' "$validation"; fi + if [[ -n "$checksum" ]]; then printf 'backup_sha256=%s\n' "$checksum"; fi + } >"$marker_tmp" + mv "$marker_tmp" "$marker_file" } validate_existing_marker() { - local state - local validation - local expected_checksum - local actual_checksum + local state + local validation + local expected_checksum + local actual_checksum - state="$(marker_value state)" - validation="$(marker_value validation)" + state="$(marker_value state)" + validation="$(marker_value validation)" - case "$state" in - no-legacy-data) - if [[ -n "$validation" && "$validation" != "no-data-v1" ]]; then - log error "no-data migration marker uses an unknown validation method" - return 1 - fi - if [[ -z "$validation" ]]; then - write_marker no-legacy-data "" no-data-v1 - log warning "upgraded no-data migration marker with explicit validation state" - fi - return 0 - ;; - legacy-backup) - if [[ "$validation" != "$validation_method" ]]; then - log error "legacy backup marker lacks content-level validation proof" - return 1 - fi - expected_checksum="$(marker_value backup_sha256)" - if [[ -z "$expected_checksum" ]] || ! validate_archive "$backup_file"; then - log error "legacy backup marker exists but its archive cannot be verified" - return 1 - fi - actual_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" - if [[ "$actual_checksum" != "$expected_checksum" ]]; then - log error "legacy backup checksum does not match the migration marker" - return 1 - fi - return 0 - ;; - *) - log error "legacy migration marker lacks content-level validation proof" - return 1 - ;; - esac + case "$state" in + no-legacy-data) + if [[ -n "$validation" && "$validation" != "no-data-v1" ]]; then + log error "no-data migration marker uses an unknown validation method" + return 1 + fi + if [[ -z "$validation" ]]; then + write_marker no-legacy-data "" no-data-v1 + log warning "upgraded no-data migration marker with explicit validation state" + fi + return 0 + ;; + legacy-backup) + if [[ "$validation" != "$validation_method" ]]; then + log error "legacy backup marker lacks content-level validation proof" + return 1 + fi + expected_checksum="$(marker_value backup_sha256)" + if [[ -z "$expected_checksum" ]] || ! validate_archive "$backup_file"; then + log error "legacy backup marker exists but its archive cannot be verified" + return 1 + fi + actual_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" + if [[ "$actual_checksum" != "$expected_checksum" ]]; then + log error "legacy backup checksum does not match the migration marker" + return 1 + fi + return 0 + ;; + *) + log error "legacy migration marker lacks content-level validation proof" + return 1 + ;; + esac } mkdir -p "$data_dir" if [[ -f "$marker_file" ]]; then - if validate_existing_marker; then exit 0; fi - exit 1 + if validate_existing_marker; then exit 0; fi + exit 1 fi if ! has_existing_data; then - write_marker no-legacy-data "" no-data-v1 - log info "no existing InfluxDB data detected; recorded no-data migration state" - exit 0 + write_marker no-legacy-data "" no-data-v1 + log info "no existing InfluxDB data detected; recorded no-data migration state" + exit 0 fi mkdir -p "$backup_dir" if [[ -e "$backup_file" ]] && validate_backup_against_source "$backup_file"; then - log info "using existing content-verified InfluxDB backup at ${backup_file}" + log info "using existing content-verified InfluxDB backup at ${backup_file}" else - if [[ -e "$backup_file" ]]; then - log warning "existing InfluxDB backup is stale, incomplete, or invalid and will be atomically replaced" - fi + if [[ -e "$backup_file" ]]; then + log warning "existing InfluxDB backup is stale, incomplete, or invalid and will be atomically replaced" + fi - log warning "creating offline InfluxDB backup at ${backup_file}" - tar -C "$(dirname "$data_dir")" -czf "$temporary_backup" "$archive_root" + log warning "creating offline InfluxDB backup at ${backup_file}" + tar -C "$(dirname "$data_dir")" -czf "$temporary_backup" "$archive_root" - if ! validate_backup_against_source "$temporary_backup"; then - log error "created InfluxDB backup failed content-level validation" - exit 1 - fi + if ! validate_backup_against_source "$temporary_backup"; then + log error "created InfluxDB backup failed content-level validation" + exit 1 + fi - mv -f "$temporary_backup" "$backup_file" - log info "InfluxDB backup created and content-verified at ${backup_file}" + mv -f "$temporary_backup" "$backup_file" + log info "InfluxDB backup created and content-verified at ${backup_file}" fi backup_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" diff --git a/scrutiny_fa/CHANGELOG.md b/scrutiny_fa/CHANGELOG.md index 7dbde5d0e4..febec978b0 100644 --- a/scrutiny_fa/CHANGELOG.md +++ b/scrutiny_fa/CHANGELOG.md @@ -1,20 +1,20 @@ - + ## v1.74.1 (2026-09-12) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v1.70.0 (2026-08-29) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.69.1 (2026-08-22) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.69.0 (2026-08-15) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.68.0 (2026-08-01) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.67.0-8 (2026-07-18) - Verify every backup file checksum and symbolic-link target, record the content-validation method in the migration marker, atomically preserve the previous archive until replacement validation succeeds, and remove the blocking synthetic FIFO test fixture. @@ -35,19 +35,19 @@ ## v1.67.0 (2026-07-18) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.66.0 (2026-07-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.65.0 (2026-07-04) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.64.0 (2026-06-20) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.63.0 (2026-06-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.62.4 (2026-06-11) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) ## v1.62.3 (2026-06-05) diff --git a/scrutiny_fa/README.md b/scrutiny_fa/README.md index 1caea65aed..2e30f51a1d 100644 --- a/scrutiny_fa/README.md +++ b/scrutiny_fa/README.md @@ -52,7 +52,7 @@ It automatically mounts all local drives. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `Updates` | list | `Hourly` | Update schedule (Quarterly/Hourly/Daily/Weekly/Custom) | | `Updates_custom_time` | str | | Custom update interval (e.g., "5m", "2h", "1w", "2mo") | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/scrutiny_fa/rootfs_overlay/etc/services.d/nginx/finish b/scrutiny_fa/rootfs_overlay/etc/services.d/nginx/finish old mode 100755 new mode 100644 diff --git a/scrutiny_fa/scrutiny-ha-influxdb-preflight b/scrutiny_fa/scrutiny-ha-influxdb-preflight index 5f7cfcb6ab..370f397e5c 100644 --- a/scrutiny_fa/scrutiny-ha-influxdb-preflight +++ b/scrutiny_fa/scrutiny-ha-influxdb-preflight @@ -15,212 +15,212 @@ source_manifest="" archive_manifest="" log() { - local level="$1" - local message="$2" - printf 'time="%s" level=%s msg="%s" type=ha-influxdb-upgrade-preflight\n' \ - "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$message" + local level="$1" + local message="$2" + printf 'time="%s" level=%s msg="%s" type=ha-influxdb-upgrade-preflight\n' \ + "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$message" } cleanup() { - rm -f "$temporary_backup" "$marker_tmp" - if [[ -n "$source_manifest" ]]; then rm -f "$source_manifest"; fi - if [[ -n "$archive_manifest" ]]; then rm -f "$archive_manifest"; fi + rm -f "$temporary_backup" "$marker_tmp" + if [[ -n "$source_manifest" ]]; then rm -f "$source_manifest"; fi + if [[ -n "$archive_manifest" ]]; then rm -f "$archive_manifest"; fi } trap cleanup EXIT has_existing_data() { - if [[ -e "${data_dir}/influxd.bolt" || -e "${data_dir}/influxd.sqlite" ]]; then - return 0 - fi + if [[ -e "${data_dir}/influxd.bolt" || -e "${data_dir}/influxd.sqlite" ]]; then + return 0 + fi - if [[ -d "${data_dir}/engine" ]] && find "${data_dir}/engine" -mindepth 1 -print -quit | grep -q .; then - return 0 - fi + if [[ -d "${data_dir}/engine" ]] && find "${data_dir}/engine" -mindepth 1 -print -quit | grep -q .; then + return 0 + fi - return 1 + return 1 } validate_archive() { - local archive="$1" - [[ -s "$archive" ]] || return 1 - gzip -t "$archive" >/dev/null 2>&1 || return 1 - tar -tzf "$archive" >/dev/null 2>&1 + local archive="$1" + [[ -s "$archive" ]] || return 1 + gzip -t "$archive" >/dev/null 2>&1 || return 1 + tar -tzf "$archive" >/dev/null 2>&1 } reset_manifests() { - rm -f "$source_manifest" "$archive_manifest" - source_manifest="" - archive_manifest="" + rm -f "$source_manifest" "$archive_manifest" + source_manifest="" + archive_manifest="" } validate_backup_against_source() { - local archive="$1" - local relative_path - local source_path - local archive_member - local listing - local source_checksum - local archive_checksum - local source_target - local archive_target + local archive="$1" + local relative_path + local source_path + local archive_member + local listing + local source_checksum + local archive_checksum + local source_target + local archive_target - validate_archive "$archive" || return 1 - source_manifest="$(mktemp)" - archive_manifest="$(mktemp)" + validate_archive "$archive" || return 1 + source_manifest="$(mktemp)" + archive_manifest="$(mktemp)" - ( - cd "$data_dir" - find . -mindepth 1 -print | sed -e 's#^\./##' -e 's#/$##' | LC_ALL=C sort - ) > "$source_manifest" + ( + cd "$data_dir" + find . -mindepth 1 -print | sed -e 's#^\./##' -e 's#/$##' | LC_ALL=C sort + ) >"$source_manifest" - tar -tzf "$archive" | \ - sed -e "s#^${archive_root}/##" -e '/^$/d' -e 's#/$##' | \ - LC_ALL=C sort > "$archive_manifest" + tar -tzf "$archive" | + sed -e "s#^${archive_root}/##" -e '/^$/d' -e 's#/$##' | + LC_ALL=C sort >"$archive_manifest" - if ! cmp -s "$source_manifest" "$archive_manifest"; then - reset_manifests - return 1 - fi + if ! cmp -s "$source_manifest" "$archive_manifest"; then + reset_manifests + return 1 + fi - while IFS= read -r relative_path; do - source_path="${data_dir}/${relative_path}" - archive_member="${archive_root}/${relative_path}" - listing="$(tar -tvzf "$archive" -- "$archive_member" 2>/dev/null)" || { - reset_manifests - return 1 - } + while IFS= read -r relative_path; do + source_path="${data_dir}/${relative_path}" + archive_member="${archive_root}/${relative_path}" + listing="$(tar -tvzf "$archive" -- "$archive_member" 2>/dev/null)" || { + reset_manifests + return 1 + } - if [[ -L "$source_path" ]]; then - [[ "${listing:0:1}" == "l" ]] || { - reset_manifests - return 1 - } - source_target="$(readlink "$source_path")" - archive_target="${listing##* -> }" - [[ "$source_target" == "$archive_target" ]] || { - reset_manifests - return 1 - } - elif [[ -f "$source_path" ]]; then - [[ "${listing:0:1}" == "-" || "${listing:0:1}" == "h" ]] || { - reset_manifests - return 1 - } - source_checksum="$(sha256sum "$source_path" | awk '{print $1}')" - archive_checksum="$(tar -xOzf "$archive" -- "$archive_member" | sha256sum | awk '{print $1}')" - [[ "$source_checksum" == "$archive_checksum" ]] || { - reset_manifests - return 1 - } - elif [[ -d "$source_path" ]]; then - [[ "${listing:0:1}" == "d" ]] || { - reset_manifests - return 1 - } - else - reset_manifests - return 1 - fi - done < "$source_manifest" + if [[ -L "$source_path" ]]; then + [[ "${listing:0:1}" == "l" ]] || { + reset_manifests + return 1 + } + source_target="$(readlink "$source_path")" + archive_target="${listing##* -> }" + [[ "$source_target" == "$archive_target" ]] || { + reset_manifests + return 1 + } + elif [[ -f "$source_path" ]]; then + [[ "${listing:0:1}" == "-" || "${listing:0:1}" == "h" ]] || { + reset_manifests + return 1 + } + source_checksum="$(sha256sum "$source_path" | awk '{print $1}')" + archive_checksum="$(tar -xOzf "$archive" -- "$archive_member" | sha256sum | awk '{print $1}')" + [[ "$source_checksum" == "$archive_checksum" ]] || { + reset_manifests + return 1 + } + elif [[ -d "$source_path" ]]; then + [[ "${listing:0:1}" == "d" ]] || { + reset_manifests + return 1 + } + else + reset_manifests + return 1 + fi + done <"$source_manifest" - reset_manifests + reset_manifests } marker_value() { - local key="$1" - awk -F= -v key="$key" '$1 == key {sub(/^[^=]*=/, ""); print; exit}' "$marker_file" + local key="$1" + awk -F= -v key="$key" '$1 == key {sub(/^[^=]*=/, ""); print; exit}' "$marker_file" } write_marker() { - local state="$1" - local checksum="${2:-}" - local validation="${3:-}" + local state="$1" + local checksum="${2:-}" + local validation="${3:-}" - { - printf 'state=%s\n' "$state" - if [[ -n "$validation" ]]; then printf 'validation=%s\n' "$validation"; fi - if [[ -n "$checksum" ]]; then printf 'backup_sha256=%s\n' "$checksum"; fi - } > "$marker_tmp" - mv "$marker_tmp" "$marker_file" + { + printf 'state=%s\n' "$state" + if [[ -n "$validation" ]]; then printf 'validation=%s\n' "$validation"; fi + if [[ -n "$checksum" ]]; then printf 'backup_sha256=%s\n' "$checksum"; fi + } >"$marker_tmp" + mv "$marker_tmp" "$marker_file" } validate_existing_marker() { - local state - local validation - local expected_checksum - local actual_checksum + local state + local validation + local expected_checksum + local actual_checksum - state="$(marker_value state)" - validation="$(marker_value validation)" + state="$(marker_value state)" + validation="$(marker_value validation)" - case "$state" in - no-legacy-data) - if [[ -n "$validation" && "$validation" != "no-data-v1" ]]; then - log error "no-data migration marker uses an unknown validation method" - return 1 - fi - if [[ -z "$validation" ]]; then - write_marker no-legacy-data "" no-data-v1 - log warning "upgraded no-data migration marker with explicit validation state" - fi - return 0 - ;; - legacy-backup) - if [[ "$validation" != "$validation_method" ]]; then - log error "legacy backup marker lacks content-level validation proof" - return 1 - fi - expected_checksum="$(marker_value backup_sha256)" - if [[ -z "$expected_checksum" ]] || ! validate_archive "$backup_file"; then - log error "legacy backup marker exists but its archive cannot be verified" - return 1 - fi - actual_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" - if [[ "$actual_checksum" != "$expected_checksum" ]]; then - log error "legacy backup checksum does not match the migration marker" - return 1 - fi - return 0 - ;; - *) - log error "legacy migration marker lacks content-level validation proof" - return 1 - ;; - esac + case "$state" in + no-legacy-data) + if [[ -n "$validation" && "$validation" != "no-data-v1" ]]; then + log error "no-data migration marker uses an unknown validation method" + return 1 + fi + if [[ -z "$validation" ]]; then + write_marker no-legacy-data "" no-data-v1 + log warning "upgraded no-data migration marker with explicit validation state" + fi + return 0 + ;; + legacy-backup) + if [[ "$validation" != "$validation_method" ]]; then + log error "legacy backup marker lacks content-level validation proof" + return 1 + fi + expected_checksum="$(marker_value backup_sha256)" + if [[ -z "$expected_checksum" ]] || ! validate_archive "$backup_file"; then + log error "legacy backup marker exists but its archive cannot be verified" + return 1 + fi + actual_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" + if [[ "$actual_checksum" != "$expected_checksum" ]]; then + log error "legacy backup checksum does not match the migration marker" + return 1 + fi + return 0 + ;; + *) + log error "legacy migration marker lacks content-level validation proof" + return 1 + ;; + esac } mkdir -p "$data_dir" if [[ -f "$marker_file" ]]; then - if validate_existing_marker; then exit 0; fi - exit 1 + if validate_existing_marker; then exit 0; fi + exit 1 fi if ! has_existing_data; then - write_marker no-legacy-data "" no-data-v1 - log info "no existing InfluxDB data detected; recorded no-data migration state" - exit 0 + write_marker no-legacy-data "" no-data-v1 + log info "no existing InfluxDB data detected; recorded no-data migration state" + exit 0 fi mkdir -p "$backup_dir" if [[ -e "$backup_file" ]] && validate_backup_against_source "$backup_file"; then - log info "using existing content-verified InfluxDB backup at ${backup_file}" + log info "using existing content-verified InfluxDB backup at ${backup_file}" else - if [[ -e "$backup_file" ]]; then - log warning "existing InfluxDB backup is stale, incomplete, or invalid and will be atomically replaced" - fi + if [[ -e "$backup_file" ]]; then + log warning "existing InfluxDB backup is stale, incomplete, or invalid and will be atomically replaced" + fi - log warning "creating offline InfluxDB backup at ${backup_file}" - tar -C "$(dirname "$data_dir")" -czf "$temporary_backup" "$archive_root" + log warning "creating offline InfluxDB backup at ${backup_file}" + tar -C "$(dirname "$data_dir")" -czf "$temporary_backup" "$archive_root" - if ! validate_backup_against_source "$temporary_backup"; then - log error "created InfluxDB backup failed content-level validation" - exit 1 - fi + if ! validate_backup_against_source "$temporary_backup"; then + log error "created InfluxDB backup failed content-level validation" + exit 1 + fi - mv -f "$temporary_backup" "$backup_file" - log info "InfluxDB backup created and content-verified at ${backup_file}" + mv -f "$temporary_backup" "$backup_file" + log info "InfluxDB backup created and content-verified at ${backup_file}" fi backup_checksum="$(sha256sum "$backup_file" | awk '{print $1}')" diff --git a/scrutiny_fa_original/CHANGELOG.md b/scrutiny_fa_original/CHANGELOG.md index 06a711070b..8aadfe91e8 100644 --- a/scrutiny_fa_original/CHANGELOG.md +++ b/scrutiny_fa_original/CHANGELOG.md @@ -1,17 +1,17 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v0.9.3.1 (2026-08-18) - Fix startup crash: `collector-once` failed with `s6-svwait: fatal: unable to subscribe to events for /run/service/scrutiny` because the add-on entrypoint never started real s6 supervision. Keep the upstream image's own `/init` as PID 1, same as `scrutiny_original` (#2991) and `scrutiny`/`scrutiny_fa` (#2878). - + ## v0.9.3 (2026-08-13) - Update to latest version from analogj/scrutiny (changelog : https://github.com/analogj/scrutiny/releases) - + ## v0.9.2 (2026-06-20) - Update to latest version from analogj/scrutiny (changelog : https://github.com/analogj/scrutiny/releases) - + ## v1.63.0 (2026-06-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.62.4 (2026-06-11) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) ## v1.62.3 (2026-06-05) diff --git a/scrutiny_fa_original/README.md b/scrutiny_fa_original/README.md index d304fd7341..539bea6913 100644 --- a/scrutiny_fa_original/README.md +++ b/scrutiny_fa_original/README.md @@ -52,7 +52,7 @@ It automatically mounts all local drives. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `Updates` | list | `Hourly` | Update schedule (Quarterly/Hourly/Daily/Weekly/Custom) | | `Updates_custom_time` | str | | Custom update interval (e.g., "5m", "2h", "1w", "2mo") | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/scrutiny_original/CHANGELOG.md b/scrutiny_original/CHANGELOG.md index 1fa8e8d7ea..05cb99774c 100644 --- a/scrutiny_original/CHANGELOG.md +++ b/scrutiny_original/CHANGELOG.md @@ -1,17 +1,17 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v0.9.3.1 (2026-08-18) - Fix startup crash: `collector-once` failed with `s6-svwait: fatal: unable to subscribe to events for /run/service/scrutiny` because the add-on entrypoint never started real s6 supervision. Keep the upstream image's own `/init` as PID 1 (same fix already shipped for `scrutiny`/`scrutiny_fa` in #2878). Closes #2989. - + ## v0.9.3 (2026-08-13) - Update to latest version from analogj/scrutiny (changelog : https://github.com/analogj/scrutiny/releases) - + ## v0.9.2 (2026-06-20) - Update to latest version from analogj/scrutiny (changelog : https://github.com/analogj/scrutiny/releases) - + ## v1.63.0 (2026-06-13) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) - + ## v1.62.4 (2026-06-11) - Update to latest version from Starosdev/scrutiny (changelog : https://github.com/Starosdev/scrutiny/releases) ## v1.62.3 (2026-06-05) diff --git a/scrutiny_original/README.md b/scrutiny_original/README.md index 73ccb0ab02..d98cae6df0 100644 --- a/scrutiny_original/README.md +++ b/scrutiny_original/README.md @@ -52,7 +52,7 @@ It automatically mounts all local drives. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `Updates` | list | `Hourly` | Update schedule (Quarterly/Hourly/Daily/Weekly/Custom) | | `Updates_custom_time` | str | | Custom update interval (e.g., "5m", "2h", "1w", "2mo") | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/seafile/CHANGELOG.md b/seafile/CHANGELOG.md index a0a709eaca..5c81162334 100644 --- a/seafile/CHANGELOG.md +++ b/seafile/CHANGELOG.md @@ -17,7 +17,7 @@ ## 12.0.14 (2025-12-28) - Update to latest version from franchetti/seafile-arm -## (2025-12-23) +## (2025-12-23) - Update to latest version from franchetti/seafile-arm - The Home Assistant project has deprecated support for the armv7, armhf and i386 architectures. Support wil be fully dropped in the upcoming Home Assistant 2025.12 release diff --git a/seafile/README.md b/seafile/README.md index 1e89c325d0..dd30f3bb59 100644 --- a/seafile/README.md +++ b/seafile/README.md @@ -68,7 +68,7 @@ Webui can be found at (Seahub) and 0' "${JSONSOURCE}" >/dev/null; then +if jq -e '.env_vars? | length > 0' "${JSONSOURCE}" > /dev/null; then bashio::log.info "Applying env_vars" while IFS=$'\t' read -r ENV_NAME ENV_VALUE; do if [[ -z "${ENV_NAME}" || "${ENV_NAME}" == "null" ]]; then @@ -57,7 +57,7 @@ if jq -e '.env_vars? | length > 0' "${JSONSOURCE}" >/dev/null; then ENV_VALUE_ESCAPED=$(printf "%q" "${ENV_VALUE}") ENV_LINE="export ${ENV_NAME}=${ENV_VALUE_ESCAPED}" - sed -i "1a ${ENV_LINE}" /home/seafile/*.sh 2>/dev/null + sed -i "1a ${ENV_LINE}" /home/seafile/*.sh 2> /dev/null find /opt/seafile -name '*.sh' -print0 | xargs -0 sed -i "1a ${ENV_LINE}" done < <(jq -r '.env_vars[] | [.name, .value] | @tsv' "${JSONSOURCE}") fi @@ -108,8 +108,8 @@ sed -i "s|/shared|$DATA_LOCATION|g" /home/seafile/*.sh ADMIN_EMAIL_VAL="$(bashio::config 'SEAFILE_ADMIN_EMAIL')" ADMIN_PASSWORD_VAL="$(bashio::config 'SEAFILE_ADMIN_PASSWORD')" -if [[ -n "${ADMIN_EMAIL_VAL}" && "${ADMIN_EMAIL_VAL}" != "null" \ - && -n "${ADMIN_PASSWORD_VAL}" && "${ADMIN_PASSWORD_VAL}" != "null" ]]; then +if [[ -n "${ADMIN_EMAIL_VAL}" && "${ADMIN_EMAIL_VAL}" != "null" && + -n "${ADMIN_PASSWORD_VAL}" && "${ADMIN_PASSWORD_VAL}" != "null" ]]; then bashio::log.info "Seeding admin credentials" mkdir -p "${DATA_LOCATION}/conf" @@ -126,13 +126,13 @@ if [[ -n "${ADMIN_EMAIL_VAL}" && "${ADMIN_EMAIL_VAL}" != "null" \ sed -i '/^SEAFILE_ADMIN_PASSWORD=/d' "${SEAFILE_ENV_FILE}" case "${ADMIN_EMAIL_VAL}" in - *$'\n'*|*$'\r'*) + *$'\n'* | *$'\r'*) bashio::exit.nok "SEAFILE_ADMIN_EMAIL must not contain newlines" ;; esac case "${ADMIN_PASSWORD_VAL}" in - *$'\n'*|*$'\r'*) + *$'\n'* | *$'\r'*) bashio::exit.nok "SEAFILE_ADMIN_PASSWORD must not contain newlines" ;; esac @@ -223,7 +223,7 @@ done URLEOF chmod +x /home/seafile/apply_addon_urls.sh sed -i '/print "Launching seafile"/i /home/seafile/apply_addon_urls.sh' /home/seafile/launch.sh -if ! grep -q 'apply_addon_urls.sh' /home/seafile/launch.sh 2>/dev/null; then +if ! grep -q 'apply_addon_urls.sh' /home/seafile/launch.sh 2> /dev/null; then bashio::log.warning "Could not inject URL configuration into launch.sh; URLs may use upstream defaults" fi @@ -237,7 +237,7 @@ bashio::log.info "Defining database" # provided (Home Assistant stores multi-select options this way). Fallback to # the raw value to stay compatible with older configurations that used a # string. -DATABASE_SELECTION=$(bashio::config 'database[0]' 2>/dev/null || true) +DATABASE_SELECTION=$(bashio::config 'database[0]' 2> /dev/null || true) DATABASE_SELECTION=${DATABASE_SELECTION:-$(bashio::config 'database')} case "${DATABASE_SELECTION}" in diff --git a/seerr/CHANGELOG.md b/seerr/CHANGELOG.md index 82ee1954c0..92efdd8fe6 100644 --- a/seerr/CHANGELOG.md +++ b/seerr/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.4.1.3 (2026-08-18) - Fixed the `404: Not Found` on **Discover** persisting for browsers that had already opened Seerr through ingress, even after 3.4.1.1 and 3.4.1.2 were installed (#2975). Seerr serves its JavaScript bundle with `Cache-Control: public, max-age=31536000, immutable`, and the add-on's nginx rewrites that bundle to carry the ingress prefix - which strips the `ETag` and `Last-Modified` a browser would revalidate with. Since every add-on version served the same upstream build, the chunk URLs never changed either, so a browser kept replaying the broken 3.4.1/3.4.1.1 JavaScript from its own cache for up to a year and no fix could reach it. That is why the report persisted on the origin the reporter uses daily (`https:///`) while a browser that had never cached it (`http://:8123/`) already showed the fixed behaviour. The asset paths now carry the add-on version, so each release has its own URLs and the first page load after an update fetches the current bundle. Only ingress was affected; the directly published port 5055 always worked. diff --git a/seerr/README.md b/seerr/README.md index 36c5e3cf11..6910ebf7d1 100644 --- a/seerr/README.md +++ b/seerr/README.md @@ -25,7 +25,7 @@ Use `env_vars` to pass extra environment variables when needed. Seerr configurat ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `NODE_MEMORY_LIMIT` | int | `512` | Maximum Node.js heap memory in MB. Increase if Seerr crashes with large libraries; decrease on memory-constrained systems. | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | diff --git a/seerr/config.yaml b/seerr/config.yaml index 8c340a0cac..bbe59afbf8 100644 --- a/seerr/config.yaml +++ b/seerr/config.yaml @@ -1,8 +1,7 @@ arch: - aarch64 - amd64 -description: - Open-source media request and discovery manager for Jellyfin, Plex, and Emby +description: Open-source media request and discovery manager for Jellyfin, Plex, and Emby devices: - /dev/dri - /dev/dri/card0 diff --git a/seerr/rootfs/etc/services.d/nginx/run b/seerr/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/seerr/rootfs/etc/services.d/seerr/finish b/seerr/rootfs/etc/services.d/seerr/finish old mode 100755 new mode 100644 diff --git a/signalk/CHANGELOG.md b/signalk/CHANGELOG.md index 6690936e4a..05ab4be0df 100644 --- a/signalk/CHANGELOG.md +++ b/signalk/CHANGELOG.md @@ -1,20 +1,20 @@ - + ## 2.32.0 (2026-09-12) - Update to latest version from SignalK/signalk-server (changelog : https://github.com/SignalK/signalk-server/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 2.31.1 (2026-08-22) - Update to latest version from SignalK/signalk-server (changelog : https://github.com/SignalK/signalk-server/releases) - + ## 2.31.0 (2026-08-15) - Update to latest version from SignalK/signalk-server (changelog : https://github.com/SignalK/signalk-server/releases) - + ## 2.30.0 (2026-07-04) - Update to latest version from SignalK/signalk-server (changelog : https://github.com/SignalK/signalk-server/releases) ## 2.28.0-2 (2026-06-28) - Add Bonjour/mDNS development headers so native npm modules such as mdns can rebuild after add-on updates. - + ## 2.28.0 (2026-06-20) - Update to latest version from SignalK/signalk-server (changelog : https://github.com/SignalK/signalk-server/releases) diff --git a/signalk/rootfs/etc/cont-init.d/99-run.sh b/signalk/rootfs/etc/cont-init.d/99-run.sh index 69ca67c93f..85cbada4a3 100755 --- a/signalk/rootfs/etc/cont-init.d/99-run.sh +++ b/signalk/rootfs/etc/cont-init.d/99-run.sh @@ -42,6 +42,5 @@ if [[ ! -f /data/version || "$current_version" != "$(cat /data/version)" ]]; the fi fi - bashio::log.info "Starting application" sudo -u "$USER" -s /bin/sh -c "/home/node/signalk/startup.sh" diff --git a/social_to_mealie/CHANGELOG.md b/social_to_mealie/CHANGELOG.md index 99f55dcf0a..2f799dda02 100644 --- a/social_to_mealie/CHANGELOG.md +++ b/social_to_mealie/CHANGELOG.md @@ -1,11 +1,11 @@ - + ## 1.7.3 (2026-09-12) - Update to latest version from GerardPolloRebozado/social-to-mealie (changelog : https://github.com/GerardPolloRebozado/social-to-mealie/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.7.2 (2026-07-18) - Update to latest version from GerardPolloRebozado/social-to-mealie (changelog : https://github.com/GerardPolloRebozado/social-to-mealie/releases) - + ## 1.7.1 (2026-07-13) - Update to latest version from GerardPolloRebozado/social-to-mealie (changelog : https://github.com/GerardPolloRebozado/social-to-mealie/releases) diff --git a/social_to_mealie/README.md b/social_to_mealie/README.md index 31ad7e5e3d..c5aa0b0334 100644 --- a/social_to_mealie/README.md +++ b/social_to_mealie/README.md @@ -47,7 +47,7 @@ Webui can be found at . ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `OPENAI_URL` | str | `https://api.openai.com/v1` | URL for the OpenAI-compatible endpoint | | `OPENAI_API_KEY` | str | `` | API key for the OpenAI-compatible provider | | `TRANSCRIPTION_MODEL` | str | `whisper-1` | Whisper model to use for transcription | diff --git a/sonarr/CHANGELOG.md b/sonarr/CHANGELOG.md index e6799faf4b..fd2e43e0f3 100644 --- a/sonarr/CHANGELOG.md +++ b/sonarr/CHANGELOG.md @@ -1,27 +1,27 @@ - + ## 4.0.20.3012 (2026-09-12) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 4.0.19.3009 (2026-09-05) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - + ## 4.0.19.3007 (2026-08-29) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - + ## 4.0.19.3001 (2026-08-11) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - Upstream tag : develop-4.0.19.3001-ls184 - + ## 4.0.19.2997 (2026-08-08) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - + ## 4.0.19.2995 (2026-08-01) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - + ## 4.0.19.2979 (2026-06-27) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) - + ## 4.0.17.2969 (2026-06-06) - Update to latest version from linuxserver/docker-sonarr (changelog : https://github.com/linuxserver/docker-sonarr/releases) diff --git a/sonarr/README.md b/sonarr/README.md index 2175c43b18..ee31e18e30 100644 --- a/sonarr/README.md +++ b/sonarr/README.md @@ -60,7 +60,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/sponsorblockcast/README.md b/sponsorblockcast/README.md index 905c20c2aa..e7dbfa7207 100644 --- a/sponsorblockcast/README.md +++ b/sponsorblockcast/README.md @@ -46,7 +46,7 @@ The addon automatically discovers local Chromecast devices and monitors YouTube ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CSS_CATEGORIES` | str | `sponsor, intro, outro, selfpromo` | SponsorBlock categories to skip (comma-separated) | | `CSS_DISCOVER_INTERVAL` | str | `5m` | Interval to restart the DNS discovery client | | `CSS_DEVICES` | str | `[]` | Comma-separated list of device addresses; disables discovery | diff --git a/spotify_to_plex/CHANGELOG.md b/spotify_to_plex/CHANGELOG.md index ec88c8e727..b90b49a24d 100644 --- a/spotify_to_plex/CHANGELOG.md +++ b/spotify_to_plex/CHANGELOG.md @@ -1,8 +1,8 @@ - + ## 1.0.109 (2026-09-12) - Update to latest version from jjdenhertog/spotify-to-plex - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 1.0.107 (2026-08-15) - Update to latest version from jjdenhertog/spotify-to-plex ## 1.0.104 (2026-07-06) diff --git a/spotify_to_plex/README.md b/spotify_to_plex/README.md index ee8c42a81c..c2c2919c47 100644 --- a/spotify_to_plex/README.md +++ b/spotify_to_plex/README.md @@ -41,7 +41,7 @@ Before starting the add-on you need a Spotify developer application (https://dev Fill the add-on options: | Option | Description | -|--------|-------------| +| -------- | ------------- | | `SPOTIFY_API_CLIENT_ID` | Client ID of your Spotify developer application | | `SPOTIFY_API_CLIENT_SECRET` | Client secret of your Spotify developer application | | `SPOTIFY_API_REDIRECT_URI` | OAuth redirect URI (must match the one configured in your Spotify app) | diff --git a/tandoor_recipes/CHANGELOG.md b/tandoor_recipes/CHANGELOG.md index 24d085db16..ba84626a8c 100644 --- a/tandoor_recipes/CHANGELOG.md +++ b/tandoor_recipes/CHANGELOG.md @@ -1,13 +1,13 @@ - + ## 2.6.15 (2026-09-12) - Update to latest version from TandoorRecipes/recipes (changelog : https://github.com/TandoorRecipes/recipes/releases) - + ## 2.6.13 (2026-07-13) - Update to latest version from TandoorRecipes/recipes (changelog : https://github.com/TandoorRecipes/recipes/releases) - + ## 2.6.12 (2026-07-04) - Update to latest version from TandoorRecipes/recipes (changelog : https://github.com/TandoorRecipes/recipes/releases) - + ## 2.6.11 (2026-06-23) - Update to latest version from TandoorRecipes/recipes (changelog : https://github.com/TandoorRecipes/recipes/releases) diff --git a/tandoor_recipes/README.md b/tandoor_recipes/README.md index 6b3ed4669a..e1acdf8b1c 100644 --- a/tandoor_recipes/README.md +++ b/tandoor_recipes/README.md @@ -44,7 +44,7 @@ Complete documentation: https://docs.tandoor.dev/install/docker/ ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `SECRET_KEY` | str | `YOUR_SECRET_KEY` | **REQUIRED**: Django secret key for security | | `ALLOWED_HOSTS` | str | | **REQUIRED**: Comma-separated Home Assistant URLs for ingress | | `DB_TYPE` | list | `sqlite` | Database type (sqlite or postgresql_external) | diff --git a/tdarr/CHANGELOG.md b/tdarr/CHANGELOG.md index 0f4b70bc07..0414e2a0e2 100644 --- a/tdarr/CHANGELOG.md +++ b/tdarr/CHANGELOG.md @@ -1,25 +1,25 @@ - + ## 2.87.01 (2026-09-12) - Update to latest version from haveagitgat/tdarr - + ## 2.86.01 (2026-08-08) - Update to latest version from haveagitgat/tdarr - + ## 2.85.01 (2026-07-25) - Update to latest version from haveagitgat/tdarr - + ## 2.84.01 (2026-07-21) - Update to latest version from haveagitgat/tdarr - + ## 2.83.01 (2026-07-13) - Update to latest version from haveagitgat/tdarr - + ## 2.81.01 (2026-06-27) - Update to latest version from haveagitgat/tdarr - + ## 2.80.01 (2026-06-23) - Update to latest version from haveagitgat/tdarr - + ## 2.78.01 (2026-06-13) - Update to latest version from haveagitgat/tdarr @@ -64,7 +64,7 @@ ## 2.58.02 (2025-12-24) - Update to latest version from haveagitgat/tdarr -## (2025-12-23) +## (2025-12-23) - Update to latest version from haveagitgat/tdarr ## 2.58.02 (2025-11-22) diff --git a/tdarr/README.md b/tdarr/README.md index 232e1a4a1b..ce26027c5e 100644 --- a/tdarr/README.md +++ b/tdarr/README.md @@ -62,7 +62,7 @@ The server port is `8266` for connecting external Tdarr nodes. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `CONFIG_LOCATION` | str | `/config/addons_config/tdarr` | Path where Tdarr config is stored | | `TZ` | str | | Timezone (e.g., `Europe/London`) | | `localdisks` | str | | Local drives to mount (e.g., `sda1,sdb1,MYNAS`) | diff --git a/teamspeak/README.md b/teamspeak/README.md index 0a7cdbb8ec..805cd49eba 100644 --- a/teamspeak/README.md +++ b/teamspeak/README.md @@ -61,7 +61,7 @@ Connect using the TeamSpeak client at `homeassistant.local:9987`. ### Ports | Port | Protocol | Description | -|------|----------|-------------| +| ------ | ---------- | ------------- | | `9987` | UDP | Voice communication | | `10011` | TCP | ServerQuery (raw) | | `30033` | TCP | File transfer | diff --git a/transmission/CHANGELOG.md b/transmission/CHANGELOG.md index 918fed3b92..eba70f714e 100644 --- a/transmission/CHANGELOG.md +++ b/transmission/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 4.1.3 (2026-07-04) - Update to latest version from linuxserver/docker-transmission (changelog : https://github.com/linuxserver/docker-transmission/releases) ## 4.1.2 (2026-06-05) diff --git a/transmission/README.md b/transmission/README.md index 3f0f2fe702..5711a052d0 100644 --- a/transmission/README.md +++ b/transmission/README.md @@ -56,7 +56,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/transmission_openvpn/CHANGELOG.md b/transmission_openvpn/CHANGELOG.md index 7c0a719898..1a67c2d785 100644 --- a/transmission_openvpn/CHANGELOG.md +++ b/transmission_openvpn/CHANGELOG.md @@ -1,10 +1,10 @@ - + ## v5.5.2 (2026-08-22) - Update to latest version from haugene/docker-transmission-openvpn (changelog : https://github.com/haugene/docker-transmission-openvpn/releases) - + ## v5.5.1 (2026-08-08) - Update to latest version from haugene/docker-transmission-openvpn (changelog : https://github.com/haugene/docker-transmission-openvpn/releases) - + ## v5.4.2 (2026-08-01) - Update to latest version from haugene/docker-transmission-openvpn (changelog : https://github.com/haugene/docker-transmission-openvpn/releases) diff --git a/transmission_openvpn/config.yaml b/transmission_openvpn/config.yaml index cdba03ce04..6a69f602cc 100644 --- a/transmission_openvpn/config.yaml +++ b/transmission_openvpn/config.yaml @@ -123,7 +123,7 @@ schema: OPENVPN_PASSWORD: str PGID: int PUID: int - TRANSMISSION_HOME: str + TRANSMISSION_HOME: str TRANSMISSION_DOWNLOAD_DIR: str TRANSMISSION_INCOMPLETE_DIR: str TRANSMISSION_WATCH_DIR: str diff --git a/transmission_openvpn/rootfs/etc/openvpn/update-resolv-conf b/transmission_openvpn/rootfs/etc/openvpn/update-resolv-conf old mode 100755 new mode 100644 index 56288cd998..5d7c94b4c3 --- a/transmission_openvpn/rootfs/etc/openvpn/update-resolv-conf +++ b/transmission_openvpn/rootfs/etc/openvpn/update-resolv-conf @@ -7,51 +7,51 @@ [ "$script_type" ] || exit 0 case "$script_type" in - up) - # Backup original resolv.conf - if [ ! -f /etc/resolv.conf.ovpn-bak ]; then - cp /etc/resolv.conf /etc/resolv.conf.ovpn-bak 2>/dev/null || true - fi +up) + # Backup original resolv.conf + if [ ! -f /etc/resolv.conf.ovpn-bak ]; then + cp /etc/resolv.conf /etc/resolv.conf.ovpn-bak 2>/dev/null || true + fi - dns_servers="" - dns_domain="" - dns_search="" + dns_servers="" + dns_domain="" + dns_search="" - # Parse foreign_option_* environment variables set by OpenVPN - for optionname in ${!foreign_option_*}; do - option="${!optionname}" - case "$option" in - dhcp-option\ DNS\ *) - dns_servers="${dns_servers} ${option#dhcp-option DNS }" - ;; - dhcp-option\ DOMAIN\ *) - dns_domain="${option#dhcp-option DOMAIN }" - ;; - dhcp-option\ DOMAIN-SEARCH\ *) - dns_search="${dns_search} ${option#dhcp-option DOMAIN-SEARCH }" - ;; - esac - done + # Parse foreign_option_* environment variables set by OpenVPN + for optionname in ${!foreign_option_*}; do + option="${!optionname}" + case "$option" in + dhcp-option\ DNS\ *) + dns_servers="${dns_servers} ${option#dhcp-option DNS }" + ;; + dhcp-option\ DOMAIN\ *) + dns_domain="${option#dhcp-option DOMAIN }" + ;; + dhcp-option\ DOMAIN-SEARCH\ *) + dns_search="${dns_search} ${option#dhcp-option DOMAIN-SEARCH }" + ;; + esac + done - # Write new resolv.conf with VPN DNS servers - if [ -n "$dns_servers" ]; then - { - for server in $dns_servers; do - echo "nameserver $server" - done - [ -n "$dns_domain" ] && echo "domain $dns_domain" - [ -n "$dns_search" ] && echo "search${dns_search}" - } > /etc/resolv.conf - fi - ;; + # Write new resolv.conf with VPN DNS servers + if [ -n "$dns_servers" ]; then + { + for server in $dns_servers; do + echo "nameserver $server" + done + [ -n "$dns_domain" ] && echo "domain $dns_domain" + [ -n "$dns_search" ] && echo "search${dns_search}" + } >/etc/resolv.conf + fi + ;; - down) - # Restore original resolv.conf - if [ -f /etc/resolv.conf.ovpn-bak ]; then - cp /etc/resolv.conf.ovpn-bak /etc/resolv.conf - rm -f /etc/resolv.conf.ovpn-bak - fi - ;; +down) + # Restore original resolv.conf + if [ -f /etc/resolv.conf.ovpn-bak ]; then + cp /etc/resolv.conf.ovpn-bak /etc/resolv.conf + rm -f /etc/resolv.conf.ovpn-bak + fi + ;; esac exit 0 diff --git a/ubooquity/README.md b/ubooquity/README.md index 53a8672d94..6fb4d072e4 100644 --- a/ubooquity/README.md +++ b/ubooquity/README.md @@ -66,7 +66,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/unpackerr/CHANGELOG.md b/unpackerr/CHANGELOG.md index 56bcbec1be..2d7a1d2ac4 100644 --- a/unpackerr/CHANGELOG.md +++ b/unpackerr/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## v0.16.1 (2026-09-05) - Update to latest version from Unpackerr/unpackerr (changelog : https://github.com/Unpackerr/unpackerr/releases) diff --git a/unpackerr/README.md b/unpackerr/README.md index 31eb72f62a..db53f3a06d 100644 --- a/unpackerr/README.md +++ b/unpackerr/README.md @@ -65,7 +65,7 @@ Unpackerr monitors completed downloads and extracts archives automatically. ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `1000` | Group ID for file permissions | | `PUID` | int | `1000` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/unpackerr/rootfs/etc/cont-init.d/90-vpn.sh b/unpackerr/rootfs/etc/cont-init.d/90-vpn.sh index 2b4a522772..7755f95a2b 100755 --- a/unpackerr/rootfs/etc/cont-init.d/90-vpn.sh +++ b/unpackerr/rootfs/etc/cont-init.d/90-vpn.sh @@ -30,5 +30,5 @@ esac # Disable port forwarder when VPN_AUTO_PORT_FORWARD is explicitly false VPN_AUTO_PORT_FORWARD="${VPN_AUTO_PORT_FORWARD:-}" if [ "${VPN_AUTO_PORT_FORWARD,,}" = "false" ]; then - sed -i "1a sleep infinity" /etc/s6*/s6*/service-forwarder/run 2>/dev/null || true + sed -i "1a sleep infinity" /etc/s6*/s6*/service-forwarder/run 2> /dev/null || true fi diff --git a/webtop/README.md b/webtop/README.md index 65f0cfa2bf..e81fbd4ba4 100644 --- a/webtop/README.md +++ b/webtop/README.md @@ -49,7 +49,7 @@ See all potential ENV variables here : https://docs.linuxserver.io/images/docker ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/webtop_kde/CHANGELOG.md b/webtop_kde/CHANGELOG.md index cbb83f946c..a18972ffd2 100644 --- a/webtop_kde/CHANGELOG.md +++ b/webtop_kde/CHANGELOG.md @@ -1,13 +1,13 @@ - + ## 4.16.0.97 (2026-09-12) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - Upstream tag : 4.16-r0-ls93 - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 4.16.0.96 (2026-09-05) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - Upstream tag : 4.16-r0-ls92 - + ## 4.16.0.95 (2026-08-29) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - Upstream tag : 4.16-r0-ls95 @@ -15,7 +15,7 @@ - Version renamed from `4.16-r0-ls94`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged - + ## 4.16-r0-ls94 (2026-08-01) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) ## 4.16-r0-ls93.2 (28-07-2026) @@ -28,19 +28,19 @@ - Share the Selkies startup scripts with the `claude_desktop` add-on by symlink (`20-folders.sh`, `21-gpu_permissions.sh`, `80-configuration.sh`, `90-ingress.sh` and the nginx includes), so the fixes made there now apply here too. This brings in: GPU render-node permissions granted before the graphical services start (fixes `libEGL warning: failed to open /dev/dri/card0: Permission denied` and the resulting "waiting for stream" hang); the s6 envdir and `XDG_RUNTIME_DIR` created up front; the cache redirected to tmpfs; `/tmp/.X11-unix` pre-created so Xorg can bind its socket as a non-root user; the `init-video` and `init-selkies-config` oneshots made non-fatal so a partially permitted device setup no longer crash-loops the add-on; and an ingress config that keeps the correct (non-SSL) nginx server block. The Microsoft Edge install moves to its own webtop-only `81-microsoft_edge.sh`, which also picks up the ownership fixup that previously ran in `20-folders.sh` before Edge was installed and so never matched anything. - + ## 4.16-r0-ls93 (2026-07-21) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - + ## 4.16-r0-ls95 (2026-07-18) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - + ## 4.16-r0-ls94 (2026-06-23) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - + ## 4.16-r0-ls93 (2026-06-20) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) - + ## 4.16-r0-ls92 (2026-06-11) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) ## 4.16-r0-ls95 (2026-06-05) @@ -49,7 +49,7 @@ ## 4.16-r0-ls94 (2026-05-23) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) -## (2026-05-22) +## (2026-05-22) - Update to latest version from linuxserver/docker-webtop (changelog : https://github.com/linuxserver/docker-webtop/releases) ## 4.16-r0-ls94 (2026-05-22) diff --git a/wger/CHANGELOG.md b/wger/CHANGELOG.md index ac7d2656c5..7cab2c06e8 100644 --- a/wger/CHANGELOG.md +++ b/wger/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 2.8.0-dev.0 (2026-09-12) - Update to latest version from wger/server ## 2.6.4 (2026-09-04) diff --git a/whoogle/README.md b/whoogle/README.md index dc05fa23ee..03a064926b 100644 --- a/whoogle/README.md +++ b/whoogle/README.md @@ -42,7 +42,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `TZ` | str | `Europe/Amsterdam` | Timezone | | `WHOOGLE_CONFIG_LANGUAGE` | str | `lang_en` | Interface language | | `WHOOGLE_CONFIG_URL` | str | | Base URL for the service | diff --git a/zoneminder/CHANGELOG.md b/zoneminder/CHANGELOG.md index ebdca14610..367157b8af 100644 --- a/zoneminder/CHANGELOG.md +++ b/zoneminder/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## 1.38.4 (2026-08-13) - Update to latest version from zoneminder-containers/zoneminder-base (changelog : https://github.com/zoneminder-containers/zoneminder-base/releases) diff --git a/zoneminder/rootfs/etc/cont-init.d/99-run.sh b/zoneminder/rootfs/etc/cont-init.d/99-run.sh index 00b1ef5414..0430f05cf5 100755 --- a/zoneminder/rootfs/etc/cont-init.d/99-run.sh +++ b/zoneminder/rootfs/etc/cont-init.d/99-run.sh @@ -73,7 +73,7 @@ case "$(bashio::config "DB_CONNECTION")" in local out out="$("${mysql_base[@]}" -e \ "SELECT schema_name FROM information_schema.schemata WHERE schema_name='${db}';" \ - 2>/dev/null || true)" + 2> /dev/null || true)" [ -n "$out" ] } @@ -82,7 +82,7 @@ case "$(bashio::config "DB_CONNECTION")" in # If schema doesn't exist, count should be 0 "${mysql_base[@]}" -e \ "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${db}';" \ - 2>/dev/null || echo 0 + 2> /dev/null || echo 0 } is_likely_zoneminder_db() { @@ -99,7 +99,7 @@ case "$(bashio::config "DB_CONNECTION")" in "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${db}' AND LOWER(table_name) IN ('config','monitors');" \ - 2>/dev/null || echo 0)" + 2> /dev/null || echo 0)" # Firefly-ish signature tables (heuristic blacklist) local ff_sig @@ -110,14 +110,14 @@ case "$(bashio::config "DB_CONNECTION")" in 'accounts','transactions','transaction_journals','categories', 'budgets','bills','tags','piggy_banks','rules','rule_groups' );" \ - 2>/dev/null || echo 0)" + 2> /dev/null || echo 0)" [ "${zm_required:-0}" -ge 2 ] && [ "${ff_sig:-0}" -eq 0 ] } create_db_if_missing() { local db="$1" - "${mysql_base[@]}" -e "CREATE DATABASE IF NOT EXISTS \`${db}\`;" >/dev/null + "${mysql_base[@]}" -e "CREATE DATABASE IF NOT EXISTS \`${db}\`;" > /dev/null } # --- Legacy fix: previous buggy addon used DB name 'firefly' --- @@ -154,9 +154,9 @@ case "$(bashio::config "DB_CONNECTION")" in create_db_if_missing "$ZM_DB_NAME" dump_bin="" - if command -v mysqldump >/dev/null 2>&1; then + if command -v mysqldump > /dev/null 2>&1; then dump_bin="mysqldump" - elif command -v mariadb-dump >/dev/null 2>&1; then + elif command -v mariadb-dump > /dev/null 2>&1; then dump_bin="mariadb-dump" fi @@ -167,8 +167,8 @@ case "$(bashio::config "DB_CONNECTION")" in -u "${ZM_DB_USER}" -p"${ZM_DB_PASS}" \ -h "${ZM_DB_HOST}" -P "${ZM_DB_PORT}" \ --routines --events --triggers \ - "${LEGACY_DB_NAME}" | \ - mysql \ + "${LEGACY_DB_NAME}" \ + | mysql \ -u "${ZM_DB_USER}" -p"${ZM_DB_PASS}" \ -h "${ZM_DB_HOST}" -P "${ZM_DB_PORT}" \ "${ZM_DB_NAME}"; then diff --git a/zoraxy/CHANGELOG.md b/zoraxy/CHANGELOG.md index 9cfbabde52..2c6f7dc9bb 100644 --- a/zoraxy/CHANGELOG.md +++ b/zoraxy/CHANGELOG.md @@ -1,5 +1,5 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.3.4 (2026-08-29) - Update to latest version from tobychui/zoraxy (changelog : https://github.com/tobychui/zoraxy/releases) ## 3.3.3.1 (29-08-2026) diff --git a/zoraxy/rootfs/etc/services.d/zoraxy/run b/zoraxy/rootfs/etc/services.d/zoraxy/run old mode 100644 new mode 100755 diff --git a/zzz_archived_code-server/CHANGELOG.md b/zzz_archived_code-server/CHANGELOG.md index c320f94f37..dd4a86994e 100644 --- a/zzz_archived_code-server/CHANGELOG.md +++ b/zzz_archived_code-server/CHANGELOG.md @@ -1,34 +1,34 @@ - + ## 4.137.0 (2026-09-12) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.135.0 (2026-08-29) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.133.0 (2026-08-22) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.132.0 (2026-08-13) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.131.0 (2026-08-01) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.130.0 (2026-07-25) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.129.0 (2026-07-18) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.128.0 (2026-07-13) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.127.0 (2026-07-04) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.126.0 (2026-06-27) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) - + ## 4.125.0 (2026-06-20) - Update to latest version from linuxserver/docker-code-server (changelog : https://github.com/linuxserver/docker-code-server/releases) ## 4.123.0 (2026-06-05) diff --git a/zzz_archived_jellyseerr/CHANGELOG.md b/zzz_archived_jellyseerr/CHANGELOG.md index d0f041fb29..b1b603396d 100644 --- a/zzz_archived_jellyseerr/CHANGELOG.md +++ b/zzz_archived_jellyseerr/CHANGELOG.md @@ -1,4 +1,4 @@ - + ## v3.4.1 (2026-08-01) - Update to latest version from Fallenbagel/jellyseerr (changelog : https://github.com/Fallenbagel/jellyseerr/releases) ## v3.3.0 (2026-06-05) diff --git a/zzz_archived_jellyseerr/config.yaml b/zzz_archived_jellyseerr/config.yaml index 23e0ff2c92..414ce15f6a 100644 --- a/zzz_archived_jellyseerr/config.yaml +++ b/zzz_archived_jellyseerr/config.yaml @@ -1,8 +1,7 @@ arch: - aarch64 - amd64 -description: - Fork of overseerr for jellyfin support (deprecated, migrate to Seerr add-on) +description: Fork of overseerr for jellyfin support (deprecated, migrate to Seerr add-on) devices: - /dev/dri - /dev/dri/card0 diff --git a/zzz_archived_kapowarr/README.md b/zzz_archived_kapowarr/README.md index 7819490f88..d075a2c5e3 100644 --- a/zzz_archived_kapowarr/README.md +++ b/zzz_archived_kapowarr/README.md @@ -26,7 +26,7 @@ and converted the way you want them. ## Configuration | Option | Description | -|--------|-------------| +| -------- | ------------- | | `PUID` / `PGID` | Ownership applied to the add-on configuration directory, and the user Kapowarr runs as. Defaults to `0` (root). | | `TZ` | Timezone, e.g. `Europe/Paris`. | | `localdisks` | Local disks to mount, e.g. `sda1` or a disk label. | diff --git a/zzz_archived_kapowarr/rootfs/etc/services.d/kapowarr/run b/zzz_archived_kapowarr/rootfs/etc/services.d/kapowarr/run old mode 100644 new mode 100755 diff --git a/zzz_archived_kapowarr/rootfs/etc/services.d/nginx/run b/zzz_archived_kapowarr/rootfs/etc/services.d/nginx/run old mode 100644 new mode 100755 diff --git a/zzz_archived_omada/rootfs/etc/cont-init.d/99-run.sh b/zzz_archived_omada/rootfs/etc/cont-init.d/99-run.sh old mode 100644 new mode 100755 diff --git a/zzz_archived_omada_v3/CHANGELOG.md b/zzz_archived_omada_v3/CHANGELOG.md index 71e29b4449..092fc8ca76 100644 --- a/zzz_archived_omada_v3/CHANGELOG.md +++ b/zzz_archived_omada_v3/CHANGELOG.md @@ -1,5 +1,5 @@ -## (2025-12-23) +## (2025-12-23) - Update to latest version from mbentley/omada-controller ## 5.3-amd64-2025-11-09-2 (2025-11-18) - Minor bugs fixed diff --git a/zzz_archived_overseerr/README.md b/zzz_archived_overseerr/README.md index b29cf846f1..20b6d8d0dc 100644 --- a/zzz_archived_overseerr/README.md +++ b/zzz_archived_overseerr/README.md @@ -61,7 +61,7 @@ Configurations can be done through the app webUI, except for the following optio ### Options | Option | Type | Default | Description | -|--------|------|---------|-------------| +| -------- | ------ | --------- | ------------- | | `PGID` | int | `0` | Group ID for file permissions | | `PUID` | int | `0` | User ID for file permissions | | `TZ` | str | | Timezone (e.g., `Europe/London`) | diff --git a/zzz_archived_overseerr/rootfs/etc/cont-init.d/00-config_location.sh b/zzz_archived_overseerr/rootfs/etc/cont-init.d/00-config_location.sh old mode 100644 new mode 100755 diff --git a/zzz_archived_overseerr/rootfs/etc/cont-init.d/20-folders.sh b/zzz_archived_overseerr/rootfs/etc/cont-init.d/20-folders.sh old mode 100644 new mode 100755 diff --git a/zzz_archived_paperless_ngx/CHANGELOG.md b/zzz_archived_paperless_ngx/CHANGELOG.md index e47c7f8b59..eb43c85725 100644 --- a/zzz_archived_paperless_ngx/CHANGELOG.md +++ b/zzz_archived_paperless_ngx/CHANGELOG.md @@ -1,17 +1,17 @@ - Migrate legacy add-on configuration map names to current app configuration terminology. - + ## 3.1.3 (2026-09-05) - Update to latest version from paperless-ngx/paperless-ngx (changelog : https://github.com/paperless-ngx/paperless-ngx/releases) - + ## 3.1.0 (2026-08-29) - Update to latest version from paperless-ngx/paperless-ngx (changelog : https://github.com/paperless-ngx/paperless-ngx/releases) - + ## 3.0.5 (2026-08-02) - Update to latest version from paperless-ngx/paperless-ngx (changelog : https://github.com/paperless-ngx/paperless-ngx/releases) - + ## 3.0.4 (2026-08-01) - Update to latest version from paperless-ngx/paperless-ngx (changelog : https://github.com/paperless-ngx/paperless-ngx/releases) - + ## 3.0.2 (2026-07-25) - Update to latest version from paperless-ngx/paperless-ngx (changelog : https://github.com/paperless-ngx/paperless-ngx/releases) ## 2.20.15 (2026-06-05) diff --git a/zzz_archived_paperless_ngx/rootfs/etc/cont-init.d/99-run.sh b/zzz_archived_paperless_ngx/rootfs/etc/cont-init.d/99-run.sh index f7d3cfe2d5..f55750eb16 100755 --- a/zzz_archived_paperless_ngx/rootfs/etc/cont-init.d/99-run.sh +++ b/zzz_archived_paperless_ngx/rootfs/etc/cont-init.d/99-run.sh @@ -90,7 +90,10 @@ set +u # container_environment, which is read by `with-contenv` when each service starts. S6_ENV_DIR="" for candidate in /var/run/s6/container_environment /run/s6/container_environment; do - if [ -d "$candidate" ]; then S6_ENV_DIR="$candidate"; break; fi + if [ -d "$candidate" ]; then + S6_ENV_DIR="$candidate" + break + fi done # For all relevant variables diff --git a/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/init-tor/run b/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/init-tor/run index 4afd5a026b..0dba87d38e 100755 --- a/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/init-tor/run +++ b/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/init-tor/run @@ -38,8 +38,7 @@ fi # Checks if client names where configured when using stealth mode if bashio::config.true 'hidden_services' \ && bashio::config.true 'stealth' \ - && ! bashio::config.has_value 'client_names'; -then + && ! bashio::config.has_value 'client_names'; then bashio::log.fatal bashio::log.fatal 'Add-on configuration is incomplete.' bashio::log.fatal @@ -62,19 +61,19 @@ chmod -R 0700 /ssl/tor # Find the matching Tor log level if bashio::config.has_value 'log_level'; then case "$(bashio::string.lower "$(bashio::config 'log_level')")" in - all|trace) + all | trace) log_level="debug" ;; debug) log_level="info" ;; - info|notice) + info | notice) log_level="notice" ;; warning) log_level="warn" ;; - error|fatal|off) + error | fatal | off) log_level="err" ;; esac @@ -99,7 +98,7 @@ if bashio::config.true 'hidden_services'; then echo "HiddenServiceDir ${hidden_service_dir}" >> "${torrc}" for port in $(bashio::config 'ports'); do - count=$(echo "${port}" | sed 's/[^:]//g'| awk '{ print length }') + count=$(echo "${port}" | sed 's/[^:]//g' | awk '{ print length }') if [[ "${count}" == 0 ]]; then host='homeassistant' virtual_port="${port}" @@ -132,8 +131,7 @@ fi # Configure bridges if bashio::config.exists 'bridges' \ - && ! bashio::config.is_empty 'bridges'; -then + && ! bashio::config.is_empty 'bridges'; then bashio::log.info 'Use bridges:' echo "UseBridges 1" >> "${torrc}" @@ -164,10 +162,10 @@ if bashio::config.true 'hidden_services'; then sleep 1 done - address=$(<"${hostname_file}") - grep -m 1 "Bootstrapped 100% (done): Done" <&3 >/dev/null 2>&1 + address=$(< "${hostname_file}") + grep -m 1 "Bootstrapped 100% (done): Done" <&3 > /dev/null 2>&1 - kill "$(pgrep tor)" >/dev/null 2>&1 + kill "$(pgrep tor)" > /dev/null 2>&1 bashio::log.info '---------------------------------------------------------' bashio::log.info 'Your Home Assistant instance is available on Tor!' @@ -176,14 +174,12 @@ if bashio::config.true 'hidden_services'; then fi # Configure stealth mode -if bashio::config.true 'hidden_services' && bashio::config.true 'stealth'; -then +if bashio::config.true 'hidden_services' && bashio::config.true 'stealth'; then # Following the documentation at: # https://community.torproject.org/onion-services/advanced/client-auth/ while read -r clientname; do # Generate key is they do not exist yet - if ! bashio::fs.file_exists "${authorized_clients_dir}/${clientname}.auth" - then + if ! bashio::fs.file_exists "${authorized_clients_dir}/${clientname}.auth"; then key=$(openssl genpkey -algorithm x25519) private_key=$( @@ -198,12 +194,12 @@ then public_key=$( openssl pkey -pubout \ - <<< "${key}" \ - | sed -e '/----.*PUBLIC KEY----\|^[[:space:]]*$/d' \ - | base64 -d \ - | tail -c 32 \ - | base32 \ - | sed 's/=//g' + <<< "${key}" \ + | sed -e '/----.*PUBLIC KEY----\|^[[:space:]]*$/d' \ + | base64 -d \ + | tail -c 32 \ + | base32 \ + | sed 's/=//g' ) # Create authorized client file diff --git a/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/tor/run b/zzz_archived_tor/rootfs/etc/s6-overlay/s6-rc.d/tor/run old mode 100644 new mode 100755