diff --git a/.claude/skills/hassio-addon-workflow/references/traps.md b/.claude/skills/hassio-addon-workflow/references/traps.md index d7063b4481..a6fefa7d7d 100644 --- a/.claude/skills/hassio-addon-workflow/references/traps.md +++ b/.claude/skills/hassio-addon-workflow/references/traps.md @@ -254,13 +254,38 @@ without your involvement — another reason a shared checkout goes stale mid-tas **Reviewers**: CodeRabbit (deepest — often runs scripts to prove a claim; reviews ~9 minutes after the PR opens, or on `@coderabbitai review`), chatgpt-codex-connector, Copilot, Codacy. -**Codacy `action_required` is this repo's normal state.** Other open PRs show the same. It -exposes no annotations via the API, so its findings are only visible in the maintainer's Codacy -account. Note it and move on rather than guessing. +**Codacy is red on essentially every add-on PR and gates nothing.** `gh pr checks` reports it as +`fail` (older runs showed `action_required`); #3019, #3044 and #3050 all merged with it failing, +and `master` carries no branch protection, so no check is required in the GitHub sense. It exposes +no annotations via the API, so its findings are only visible in the maintainer's Codacy account. +Note it and move on rather than guessing. `pr_review.sh watch` therefore prints it every poll but +keeps it out of the verdict — the one check on that list, which is a denylist of known noise, not +an allowlist of gates, so a job added to CI later counts as blocking until someone exempts it. **Resolving a review thread requires GraphQL** (`resolveReviewThread`); the REST API cannot do it. `scripts/pr_review.sh` wraps fetch / reply / resolve. +**`gh pr checks` output is TAB-separated, and every blocking gate here has spaces in its name.** +Parsing it with awk's default field splitting truncates each check to its first word and reads the +wrong column as the state: `Codacy Static Code Analysisfail` becomes `Codacy=Static`, and +`Test addon build (wger)pending` becomes `Test=addon`. A `case` over that string then matches +neither `*fail*` nor `*pending*` and falls through to the "all passing" branch — the failure mode +that makes a CI-reporting command lie. `pr_review.sh watch` called #3044 green while Codacy was +red, and on #3042 printed "settled — all passing" while the HA add-on linter was failing; it would +also have called a build that had not started a pass. Use `awk -F'\t'`, judge the state column +alone (never the joined `name=state` text, or a check named `flaky-fail-detector` reads as a +failure), and treat an unrecognised state as a failure instead of letting it reach the passing +branch. Fixed in #3052. + +The TSV is gh's *non-TTY* renderer, which is what `$(gh pr checks ... | awk)` always gets; attached +to a terminal the same command prints a coloured, aligned table with a summary line, so never +sanity-check the format by eye in a shell and assume the script sees that. `gh pr checks --json` +would be sturdier, and Copilot recommends it (#3052), but it does not exist before gh 2.36 and the +add-on ships 2.23 — it fails with `unknown flag: --json`. The parse is therefore built to fail +safe instead: states are allowlisted, so a header row would land in the failure branch and a +space-aligned table would parse to zero rows and keep `watch` waiting. Either way it cannot +return a false pass. + **CHANGELOG heading dates are ISO, whatever the bots' defaults say.** Match the format already in the add-on's file. Repo-wide that is `## (YYYY-MM-DD)`: 7705 dated headings against 363 in `DD-MM-YYYY`, and the newest entry is ISO in 125 of 135 add-ons. Copilot flags an ISO file that diff --git a/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh b/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh index dcc0be02bc..6f4570822c 100755 --- a/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh +++ b/.claude/skills/hassio-addon-workflow/scripts/pr_review.sh @@ -8,6 +8,10 @@ # pr_review.sh resolve --all = every unresolved, asks first # pr_review.sh watch [minutes] poll checks (run this backgrounded) # +# watch exits 0 when every blocking check passed *or was skipped* — a PR touching no add-on +# skips all three gates, and it says so — 1 on failure, 2 if it ran out of minutes. Codacy is +# advisory here: printed every poll, excluded from the verdict. +# # Reviewers seen here: coderabbitai (deepest; reviews ~9 min after open, or on # "@coderabbitai review"), chatgpt-codex-connector, Copilot, Codacy. # @@ -18,126 +22,146 @@ 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,16p' "$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=$? +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 - # 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 + 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 - ;; - 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 - wfail=2 # not 0: running out of minutes with checks still pending is not a pass - for i in $(seq 1 "$MINS"); do - c=$(gh pr checks "$PR" 2> /dev/null | awk '{print $1"="$2}' | tr '\n' ' ') - if [ -z "$c" ]; 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 - echo "[$i] $c" - case "$c" in - *pending*) sleep 60 ;; - *fail* | *error* | *cancel*) - echo "settled — with FAILURES (see above)" - wfail=1 - break - ;; - *) - echo "settled — all passing" - wfail=0 - break - ;; + # 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 - [ "$wfail" -eq 2 ] && echo "gave up after ${MINS}m, checks still unsettled — NOT a pass" - # A PR touching no */config.* skips the CHANGELOG, linter and build jobs outright (#3018). - case "${c:-}" 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 - ;; + 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 ;; esac diff --git a/.github/stargazer_countries.csv b/.github/stargazer_countries.csv index 4cb8fb1cbf..5d0e6c59a8 100644 --- a/.github/stargazer_countries.csv +++ b/.github/stargazer_countries.csv @@ -418,6 +418,7 @@ KuchenKavalier,,2026-08-10 KuerbisK,,2026-08-10 Kvasenok,,2026-08-10 KyleGolfer,,2026-08-10 +L00PERY,,2026-09-06 L0rdShrek,Germany, L1nKinc,Germany, LaLaBer,,2026-08-10 @@ -444,6 +445,7 @@ LoginByCall,,2026-08-10 Lolekpolek,,2026-08-10 LonelySoul7X,,2026-08-10 Loong-He,China,2026-08-30 +Loong-OvO,China,2026-09-06 Lorsel,Italy, Luca2165801154,,2026-08-10 Lucius-Waverly,,2026-08-16 @@ -1390,6 +1392,7 @@ flostingapplesauce,,2026-08-10 flozi00,Germany, flue17,,2026-08-10 fmcglinn,Australia, +foodstampou812,United States,2026-09-06 forming,,2026-08-10 forreggbor,Hungary, foundbobby,United States, @@ -1834,6 +1837,7 @@ m1kethai,Canada, m23l,,2026-08-10 m2sh,"Iran, Islamic Republic of", m4rcSA,,2026-08-10 +m4rkolson,United States,2026-09-06 mProwler,United States, mStrangers,,2026-08-10 mabt,,2026-08-10 @@ -2085,6 +2089,7 @@ p0wertiger,Poland, paalwilliams,Germany, pace6666,,2026-08-10 pafnow,France, +pahanitsch,Germany,2026-09-06 pandabreads,,2026-08-10 pankaj151,India, papafrank66,,2026-08-10 @@ -2664,6 +2669,7 @@ yycsdm,,2026-08-10 yyll2233,,2026-08-10 z2833,,2026-08-10 z307917424,,2026-08-10 +zacharee,Panama,2026-09-06 zachgilliam,United States, zanyraspi,,2026-08-10 zdaar,,2026-08-10 diff --git a/.github/stargazer_map.png b/.github/stargazer_map.png index e11321f25a..f822c56eaa 100644 Binary files a/.github/stargazer_map.png and b/.github/stargazer_map.png differ diff --git a/.github/stats.png b/.github/stats.png index 869f8ae5ac..fc3982ea8b 100644 Binary files a/.github/stats.png and b/.github/stats.png differ diff --git a/.github/stats_addons.png b/.github/stats_addons.png index b567eebc4c..0d5ad72a3f 100644 Binary files a/.github/stats_addons.png and b/.github/stats_addons.png differ diff --git a/addons_updater/stats.png b/addons_updater/stats.png index ee5ec87c1c..c57530930d 100644 Binary files a/addons_updater/stats.png and b/addons_updater/stats.png differ diff --git a/arpspoof/stats.png b/arpspoof/stats.png index 6143b099f8..fc6cc26c64 100644 Binary files a/arpspoof/stats.png and b/arpspoof/stats.png differ diff --git a/aurral/stats.png b/aurral/stats.png index be4d0a0b77..5d68b8239e 100644 Binary files a/aurral/stats.png and b/aurral/stats.png differ diff --git a/autobrr/stats.png b/autobrr/stats.png index ddab876144..c7c9829c51 100644 Binary files a/autobrr/stats.png and b/autobrr/stats.png differ diff --git a/baikal/stats.png b/baikal/stats.png index bc83db114a..2e4c0ba794 100644 Binary files a/baikal/stats.png and b/baikal/stats.png differ diff --git a/battybirdnet-pi/stats.png b/battybirdnet-pi/stats.png index 2f4acf630d..c79430fabf 100644 Binary files a/battybirdnet-pi/stats.png and b/battybirdnet-pi/stats.png differ diff --git a/bazarr/stats.png b/bazarr/stats.png index 65ab94a922..80af078e26 100644 Binary files a/bazarr/stats.png and b/bazarr/stats.png differ diff --git a/bentopdf/stats.png b/bentopdf/stats.png index ca5e7e8846..bd3f4891c1 100644 Binary files a/bentopdf/stats.png and b/bentopdf/stats.png differ diff --git a/binance-trading-bot/stats.png b/binance-trading-bot/stats.png index c205fbc5a9..967832f12b 100644 Binary files a/binance-trading-bot/stats.png and b/binance-trading-bot/stats.png differ diff --git a/birdnet-go-dev/stats.png b/birdnet-go-dev/stats.png index 1a08b55ebf..448a84f26d 100644 Binary files a/birdnet-go-dev/stats.png and b/birdnet-go-dev/stats.png differ diff --git a/birdnet-go/stats.png b/birdnet-go/stats.png index 07cc832dd1..c23ef70c2c 100644 Binary files a/birdnet-go/stats.png and b/birdnet-go/stats.png differ diff --git a/birdnet-pi-zach/stats.png b/birdnet-pi-zach/stats.png index 01c9ec8860..a15017a190 100644 Binary files a/birdnet-pi-zach/stats.png and b/birdnet-pi-zach/stats.png differ diff --git a/birdnet-pi/stats.png b/birdnet-pi/stats.png index 246ce3ba7c..273b498b6c 100644 Binary files a/birdnet-pi/stats.png and b/birdnet-pi/stats.png differ diff --git a/birdnet-pipy/stats.png b/birdnet-pipy/stats.png index a49679d18d..655c305e6c 100644 Binary files a/birdnet-pipy/stats.png and b/birdnet-pipy/stats.png differ diff --git a/bitwarden/stats.png b/bitwarden/stats.png index f7708433be..ecef9f906f 100644 Binary files a/bitwarden/stats.png and b/bitwarden/stats.png differ diff --git a/browser_brave/stats.png b/browser_brave/stats.png index 4129c109cb..34ad3f67fc 100644 Binary files a/browser_brave/stats.png and b/browser_brave/stats.png differ diff --git a/browser_chromium/stats.png b/browser_chromium/stats.png index 83596734c4..ea9c78e542 100644 Binary files a/browser_chromium/stats.png and b/browser_chromium/stats.png differ diff --git a/browserless_chrome/stats.png b/browserless_chrome/stats.png index cccb094096..966c136968 100644 Binary files a/browserless_chrome/stats.png and b/browserless_chrome/stats.png differ diff --git a/calibre/stats.png b/calibre/stats.png index 710364aeeb..c957f795b5 100644 Binary files a/calibre/stats.png and b/calibre/stats.png differ diff --git a/changedetection.io/stats.png b/changedetection.io/stats.png index 1ae49923de..0204583b69 100644 Binary files a/changedetection.io/stats.png and b/changedetection.io/stats.png differ diff --git a/claude_desktop/stats.png b/claude_desktop/stats.png index 3d682c3aa4..ad321e32ac 100644 Binary files a/claude_desktop/stats.png and b/claude_desktop/stats.png differ diff --git a/cleanuparr/stats.png b/cleanuparr/stats.png index e6bd286db4..7414902e13 100644 Binary files a/cleanuparr/stats.png and b/cleanuparr/stats.png differ diff --git a/cloudcommander/stats.png b/cloudcommander/stats.png index df2f37c07b..ef4f5c39b0 100644 Binary files a/cloudcommander/stats.png and b/cloudcommander/stats.png differ diff --git a/codex/stats.png b/codex/stats.png index abae7af279..240ca4e613 100644 Binary files a/codex/stats.png and b/codex/stats.png differ diff --git a/collabora/stats.png b/collabora/stats.png index 311ed155f8..0f2cec4e91 100644 Binary files a/collabora/stats.png and b/collabora/stats.png differ diff --git a/comicarr/stats.png b/comicarr/stats.png index ef785a093d..671715ba5a 100644 Binary files a/comicarr/stats.png and b/comicarr/stats.png differ diff --git a/comixed/stats.png b/comixed/stats.png index 74f06ec350..0e1a71208d 100644 Binary files a/comixed/stats.png and b/comixed/stats.png differ diff --git a/elasticsearch/stats.png b/elasticsearch/stats.png index e03dc3f8db..2ada49c19f 100644 Binary files a/elasticsearch/stats.png and b/elasticsearch/stats.png differ diff --git a/emby/stats.png b/emby/stats.png index 37afda87ee..c1a36e0b6b 100644 Binary files a/emby/stats.png and b/emby/stats.png differ diff --git a/emby_beta/stats.png b/emby_beta/stats.png index 67d67ae349..c0e2e63e25 100644 Binary files a/emby_beta/stats.png and b/emby_beta/stats.png differ diff --git a/enedisgateway2mqtt/stats.png b/enedisgateway2mqtt/stats.png index ca08895f95..444c6596b7 100644 Binary files a/enedisgateway2mqtt/stats.png and b/enedisgateway2mqtt/stats.png differ diff --git a/enedisgateway2mqtt_dev/stats.png b/enedisgateway2mqtt_dev/stats.png index 5750845cf7..abd7659ea0 100644 Binary files a/enedisgateway2mqtt_dev/stats.png and b/enedisgateway2mqtt_dev/stats.png differ diff --git a/ente/stats.png b/ente/stats.png index f8346e6d79..ee086b7658 100644 Binary files a/ente/stats.png and b/ente/stats.png differ diff --git a/epicgamesfree/stats.png b/epicgamesfree/stats.png index a44cedde9b..c6d4fa721c 100644 Binary files a/epicgamesfree/stats.png and b/epicgamesfree/stats.png differ diff --git a/filebrowser_quantum/stats.png b/filebrowser_quantum/stats.png index f3230b2ede..2e7c871c56 100644 Binary files a/filebrowser_quantum/stats.png and b/filebrowser_quantum/stats.png differ diff --git a/fireflyiii/stats.png b/fireflyiii/stats.png index 636fe9c07e..492db04575 100644 Binary files a/fireflyiii/stats.png and b/fireflyiii/stats.png differ diff --git a/fireflyiii_data_importer/stats.png b/fireflyiii_data_importer/stats.png index 6a6581aa6d..76b426ffe5 100644 Binary files a/fireflyiii_data_importer/stats.png and b/fireflyiii_data_importer/stats.png differ diff --git a/fireflyiii_fints_importer/stats.png b/fireflyiii_fints_importer/stats.png index 6987866553..d766ea9595 100644 Binary files a/fireflyiii_fints_importer/stats.png and b/fireflyiii_fints_importer/stats.png differ diff --git a/flaresolverr/stats.png b/flaresolverr/stats.png index 743d18d70c..7e8000a67d 100644 Binary files a/flaresolverr/stats.png and b/flaresolverr/stats.png differ diff --git a/flexget/stats.png b/flexget/stats.png index cf5611d282..16fbd0608b 100644 Binary files a/flexget/stats.png and b/flexget/stats.png differ diff --git a/free_games_claimer/stats.png b/free_games_claimer/stats.png index 114fd781fb..a912e87da1 100644 Binary files a/free_games_claimer/stats.png and b/free_games_claimer/stats.png differ diff --git a/gazpar2mqtt/stats.png b/gazpar2mqtt/stats.png index 872adae875..873fcfe37b 100644 Binary files a/gazpar2mqtt/stats.png and b/gazpar2mqtt/stats.png differ diff --git a/gitea/stats.png b/gitea/stats.png index 0dca303318..034ca043ce 100644 Binary files a/gitea/stats.png and b/gitea/stats.png differ diff --git a/grampsweb/stats.png b/grampsweb/stats.png index f04155d75b..3cdeb467dc 100644 Binary files a/grampsweb/stats.png and b/grampsweb/stats.png differ diff --git a/grav/stats.png b/grav/stats.png index c1b6598a17..a8ed49f939 100644 Binary files a/grav/stats.png and b/grav/stats.png differ diff --git a/guacamole/stats.png b/guacamole/stats.png index b21c632a11..5551f79e07 100644 Binary files a/guacamole/stats.png and b/guacamole/stats.png differ diff --git a/immich/stats.png b/immich/stats.png index 1ba6cdcd11..420e0f0964 100644 Binary files a/immich/stats.png and b/immich/stats.png differ diff --git a/immich_cuda/stats.png b/immich_cuda/stats.png index 50063e21b2..b9b738a4fa 100644 Binary files a/immich_cuda/stats.png and b/immich_cuda/stats.png differ diff --git a/immich_frame/stats.png b/immich_frame/stats.png index 328403b5f1..2d22bfb321 100644 Binary files a/immich_frame/stats.png and b/immich_frame/stats.png differ diff --git a/immich_noml/stats.png b/immich_noml/stats.png index ade2aa70d7..5ce541ee6a 100644 Binary files a/immich_noml/stats.png and b/immich_noml/stats.png differ diff --git a/immich_openvino/stats.png b/immich_openvino/stats.png index 1771ad0562..8649c8f320 100644 Binary files a/immich_openvino/stats.png and b/immich_openvino/stats.png differ diff --git a/immich_power_tools/stats.png b/immich_power_tools/stats.png index ae9a6d7a49..23f19d001f 100644 Binary files a/immich_power_tools/stats.png and b/immich_power_tools/stats.png differ diff --git a/inadyn/stats.png b/inadyn/stats.png index 1a6f429306..3d9d373f76 100644 Binary files a/inadyn/stats.png and b/inadyn/stats.png differ diff --git a/jackett/stats.png b/jackett/stats.png index 9b6e350654..06cb51295b 100644 Binary files a/jackett/stats.png and b/jackett/stats.png differ diff --git a/jellyfin/stats.png b/jellyfin/stats.png index 2701aeaac7..5d22a2e52e 100644 Binary files a/jellyfin/stats.png and b/jellyfin/stats.png differ diff --git a/joal/stats.png b/joal/stats.png index fc1d37d3ed..aa65f63495 100644 Binary files a/joal/stats.png and b/joal/stats.png differ diff --git a/joplin/stats.png b/joplin/stats.png index aba9da5d29..dd8402b17e 100644 Binary files a/joplin/stats.png and b/joplin/stats.png differ diff --git a/kometa/stats.png b/kometa/stats.png index 2226370930..b35a58666f 100644 Binary files a/kometa/stats.png and b/kometa/stats.png differ diff --git a/komga/stats.png b/komga/stats.png index 4d49d414f6..86e7e915ec 100644 Binary files a/komga/stats.png and b/komga/stats.png differ diff --git a/librespeed/stats.png b/librespeed/stats.png index 58b8e47ab4..296c4848d3 100644 Binary files a/librespeed/stats.png and b/librespeed/stats.png differ diff --git a/lidarr/stats.png b/lidarr/stats.png index f8e99181c9..30487cd002 100644 Binary files a/lidarr/stats.png and b/lidarr/stats.png differ diff --git a/linkwarden/stats.png b/linkwarden/stats.png index 8c3ab8fc51..e691d2c61a 100644 Binary files a/linkwarden/stats.png and b/linkwarden/stats.png differ diff --git a/maintainerr/stats.png b/maintainerr/stats.png index a35ea0e643..1e2d678b04 100644 Binary files a/maintainerr/stats.png and b/maintainerr/stats.png differ diff --git a/manyfold/stats.png b/manyfold/stats.png index 8043f9be61..12664b8e71 100644 Binary files a/manyfold/stats.png and b/manyfold/stats.png differ diff --git a/mealie/stats.png b/mealie/stats.png index 3829ffd40b..1484f9d729 100644 Binary files a/mealie/stats.png and b/mealie/stats.png differ diff --git a/monica/stats.png b/monica/stats.png index 419376f4b7..fcca2c51ab 100644 Binary files a/monica/stats.png and b/monica/stats.png differ diff --git a/mylar3/stats.png b/mylar3/stats.png index a762584a60..aa3e26cd17 100644 Binary files a/mylar3/stats.png and b/mylar3/stats.png differ diff --git a/navidrome/stats.png b/navidrome/stats.png index 34ad1273e4..127f8ccdc9 100644 Binary files a/navidrome/stats.png and b/navidrome/stats.png differ diff --git a/netbird-server/stats.png b/netbird-server/stats.png index 5feb142e06..7b328a3bfe 100644 Binary files a/netbird-server/stats.png and b/netbird-server/stats.png differ diff --git a/nextcloud/stats.png b/nextcloud/stats.png index b0f25b840d..a78983fafc 100644 Binary files a/nextcloud/stats.png and b/nextcloud/stats.png differ diff --git a/nginx_webserver_proxy/stats.png b/nginx_webserver_proxy/stats.png index 753e5fd6ce..4023ec851f 100644 Binary files a/nginx_webserver_proxy/stats.png and b/nginx_webserver_proxy/stats.png differ diff --git a/nzbget/stats.png b/nzbget/stats.png index 5c7f050f46..30c95c1299 100644 Binary files a/nzbget/stats.png and b/nzbget/stats.png differ diff --git a/obsidian_syncserver_npm/stats.png b/obsidian_syncserver_npm/stats.png index 92c9afef61..8eee01d24e 100644 Binary files a/obsidian_syncserver_npm/stats.png and b/obsidian_syncserver_npm/stats.png differ diff --git a/obsidian_syncserver_solo/stats.png b/obsidian_syncserver_solo/stats.png index 6dc001ffc4..82764cec9d 100644 Binary files a/obsidian_syncserver_solo/stats.png and b/obsidian_syncserver_solo/stats.png differ diff --git a/obsidian_syncserver_ssl/stats.png b/obsidian_syncserver_ssl/stats.png index 345aa108b2..7859b3ada0 100644 Binary files a/obsidian_syncserver_ssl/stats.png and b/obsidian_syncserver_ssl/stats.png differ diff --git a/omni-tools/CHANGELOG.md b/omni-tools/CHANGELOG.md index 9843129816..84876f01dc 100644 --- a/omni-tools/CHANGELOG.md +++ b/omni-tools/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.6.1.1 (2026-09-07) +- Fix the add-on refusing to stop: Home Assistant reported an Error status after a few seconds and the container kept running and serving the web UI. `cont-init.d/99-run.sh` started nginx in the foreground, and `ha_entrypoint.sh` runs every cont-init script in the foreground, so the entrypoint never reached the point where it installs the `terminate()` handler that forwards SIGTERM to the application on shutdown. The application is now started in the background, and `init: false` makes the entrypoint run as PID 1 so the orphaned process is reparented to it and receives that signal. Closes #3049. + ## 0.6.1 (2025-11-18) - Added `env_vars` option to allow passing custom environment variables from the add-on configuration. diff --git a/omni-tools/config.yaml b/omni-tools/config.yaml index 5bd05156b2..c837256c08 100644 --- a/omni-tools/config.yaml +++ b/omni-tools/config.yaml @@ -5,6 +5,7 @@ description: Self-hosted collection of powerful web-based tools for everyday tasks. No ads, no tracking, just fast, accessible utilities right from your browser image: ghcr.io/alexbelgium/omni-tools-{arch} +init: false map: - addon_config:rw name: Omni Tools @@ -20,5 +21,5 @@ schema: value: str? slug: omni-tools url: https://github.com/alexbelgium/hassio-addons -version: 0.6.1 +version: 0.6.1.1 webui: "[PROTO:ssl]://[HOST]:[PORT:80]" diff --git a/omni-tools/rootfs/etc/cont-init.d/99-run.sh b/omni-tools/rootfs/etc/cont-init.d/99-run.sh index 27b2477084..ea4c591250 100755 --- a/omni-tools/rootfs/etc/cont-init.d/99-run.sh +++ b/omni-tools/rootfs/etc/cont-init.d/99-run.sh @@ -7,4 +7,10 @@ # Start omni-tools container content bashio::log.info "Starting application" -/./docker-entrypoint.sh nginx -g "daemon off;" &> /proc/1/fd/1 +# Backgrounded on purpose. ha_entrypoint.sh runs every cont-init.d script in the +# foreground, so launching nginx here in the foreground never lets it reach the +# terminate() handler that forwards SIGTERM on shutdown -- the add-on then could +# not be stopped at all (#3049). Backgrounded, this script returns, nginx is +# reparented to the entrypoint as PID 1, and terminate() signals it directly. +# Requires init: false in config.yaml, which is what makes the entrypoint PID 1. +/./docker-entrypoint.sh nginx -g "daemon off;" &> /proc/1/fd/1 & diff --git a/omni-tools/stats.png b/omni-tools/stats.png index f76fcf3c10..27bcb7d64b 100644 Binary files a/omni-tools/stats.png and b/omni-tools/stats.png differ diff --git a/openproject/stats.png b/openproject/stats.png index fb09a8d96d..e6b4c835f5 100644 Binary files a/openproject/stats.png and b/openproject/stats.png differ diff --git a/organizr/stats.png b/organizr/stats.png index ef7cde0f36..9d70dfa8b0 100644 Binary files a/organizr/stats.png and b/organizr/stats.png differ diff --git a/photoprism/stats.png b/photoprism/stats.png index fd95fdec4f..3e86ec05ac 100644 Binary files a/photoprism/stats.png and b/photoprism/stats.png differ diff --git a/piwigo/stats.png b/piwigo/stats.png index 2478a42f7a..9e0d786c5b 100644 Binary files a/piwigo/stats.png and b/piwigo/stats.png differ diff --git a/plex/stats.png b/plex/stats.png index 96a12b6458..011e550a7f 100644 Binary files a/plex/stats.png and b/plex/stats.png differ diff --git a/portainer/stats.png b/portainer/stats.png index 737298841f..715139bf94 100644 Binary files a/portainer/stats.png and b/portainer/stats.png differ diff --git a/portainer_agent/stats.png b/portainer_agent/stats.png index b1f4cb360c..87a301efda 100644 Binary files a/portainer_agent/stats.png and b/portainer_agent/stats.png differ diff --git a/portainer_be/stats.png b/portainer_be/stats.png index 7ca4086590..6864252410 100644 Binary files a/portainer_be/stats.png and b/portainer_be/stats.png differ diff --git a/postgres_15/stats.png b/postgres_15/stats.png index 9382239b7e..ef1644db54 100644 Binary files a/postgres_15/stats.png and b/postgres_15/stats.png differ diff --git a/postgres_17/stats.png b/postgres_17/stats.png index 1a9326702f..bef6ee87d1 100644 Binary files a/postgres_17/stats.png and b/postgres_17/stats.png differ diff --git a/prowlarr/stats.png b/prowlarr/stats.png index 12db1019d9..7ac0e91a12 100644 Binary files a/prowlarr/stats.png and b/prowlarr/stats.png differ diff --git a/qbittorrent/stats.png b/qbittorrent/stats.png index 61761f745c..86d2621ffb 100644 Binary files a/qbittorrent/stats.png and b/qbittorrent/stats.png differ diff --git a/radarr/stats.png b/radarr/stats.png index 8a5d9fab93..6681e7d5a5 100644 Binary files a/radarr/stats.png and b/radarr/stats.png differ diff --git a/readarr/stats.png b/readarr/stats.png index 14baa95efa..0539ff9216 100644 Binary files a/readarr/stats.png and b/readarr/stats.png differ diff --git a/requestrr/stats.png b/requestrr/stats.png index d473a9b260..d0c6aba566 100644 Binary files a/requestrr/stats.png and b/requestrr/stats.png differ diff --git a/resiliosync/stats.png b/resiliosync/stats.png index 5deff107ad..a4ff71a7f1 100644 Binary files a/resiliosync/stats.png and b/resiliosync/stats.png differ diff --git a/sabnzbd/stats.png b/sabnzbd/stats.png index 02c72884ce..8d99302a75 100644 Binary files a/sabnzbd/stats.png and b/sabnzbd/stats.png differ diff --git a/scrutiny/stats.png b/scrutiny/stats.png index c567613082..82d8cfe155 100644 Binary files a/scrutiny/stats.png and b/scrutiny/stats.png differ diff --git a/scrutiny_fa/stats.png b/scrutiny_fa/stats.png index 45634ad4e7..93a2f6b358 100644 Binary files a/scrutiny_fa/stats.png and b/scrutiny_fa/stats.png differ diff --git a/scrutiny_fa_original/stats.png b/scrutiny_fa_original/stats.png index ba7f2e2afe..8f32abfb93 100644 Binary files a/scrutiny_fa_original/stats.png and b/scrutiny_fa_original/stats.png differ diff --git a/scrutiny_original/stats.png b/scrutiny_original/stats.png index 33fea4f12f..28cc0536de 100644 Binary files a/scrutiny_original/stats.png and b/scrutiny_original/stats.png differ diff --git a/seafile/stats.png b/seafile/stats.png index 04bc9406a2..e247b75af5 100644 Binary files a/seafile/stats.png and b/seafile/stats.png differ diff --git a/signalk/stats.png b/signalk/stats.png index 8f2b2a3217..a2b48b832a 100644 Binary files a/signalk/stats.png and b/signalk/stats.png differ diff --git a/social_to_mealie/stats.png b/social_to_mealie/stats.png index e63f0d52b9..9f99e43988 100644 Binary files a/social_to_mealie/stats.png and b/social_to_mealie/stats.png differ diff --git a/sonarr/stats.png b/sonarr/stats.png index b5c4ee57e8..bb78f06c08 100644 Binary files a/sonarr/stats.png and b/sonarr/stats.png differ diff --git a/sponsorblockcast/stats.png b/sponsorblockcast/stats.png index b7a1c4606b..5319786001 100644 Binary files a/sponsorblockcast/stats.png and b/sponsorblockcast/stats.png differ diff --git a/spotweb/stats.png b/spotweb/stats.png index d1efc5f497..98170e19da 100644 Binary files a/spotweb/stats.png and b/spotweb/stats.png differ diff --git a/tandoor_recipes/stats.png b/tandoor_recipes/stats.png index 38a71accdc..49893d6913 100644 Binary files a/tandoor_recipes/stats.png and b/tandoor_recipes/stats.png differ diff --git a/tdarr/stats.png b/tdarr/stats.png index 3a0a9f8a35..e49e81dc6a 100644 Binary files a/tdarr/stats.png and b/tdarr/stats.png differ diff --git a/teamspeak/stats.png b/teamspeak/stats.png index d7086a0a25..86df97c239 100644 Binary files a/teamspeak/stats.png and b/teamspeak/stats.png differ diff --git a/transmission/stats.png b/transmission/stats.png index a9d0206695..8cc9bb13b1 100644 Binary files a/transmission/stats.png and b/transmission/stats.png differ diff --git a/transmission_openvpn/stats.png b/transmission_openvpn/stats.png index 1970763c62..8e8509b5cc 100644 Binary files a/transmission_openvpn/stats.png and b/transmission_openvpn/stats.png differ diff --git a/ubooquity/stats.png b/ubooquity/stats.png index 87438913e9..13a001c0b7 100644 Binary files a/ubooquity/stats.png and b/ubooquity/stats.png differ diff --git a/unpackerr/stats.png b/unpackerr/stats.png index d3d3aae31e..c378c2b275 100644 Binary files a/unpackerr/stats.png and b/unpackerr/stats.png differ diff --git a/webtop/stats.png b/webtop/stats.png index 1369684a4f..61cf0825e8 100644 Binary files a/webtop/stats.png and b/webtop/stats.png differ diff --git a/webtop_kde/stats.png b/webtop_kde/stats.png index d1282933ef..ad96742f8b 100644 Binary files a/webtop_kde/stats.png and b/webtop_kde/stats.png differ diff --git a/webtrees/stats.png b/webtrees/stats.png index 6e78fabfcc..e749bac9e5 100644 Binary files a/webtrees/stats.png and b/webtrees/stats.png differ diff --git a/wger/stats.png b/wger/stats.png index 230ea4178a..c36f4721f8 100644 Binary files a/wger/stats.png and b/wger/stats.png differ diff --git a/whatsapper/stats.png b/whatsapper/stats.png index 21e4679062..1954f423f1 100644 Binary files a/whatsapper/stats.png and b/whatsapper/stats.png differ diff --git a/whoogle/stats.png b/whoogle/stats.png index a9ccadda44..08d8a4048e 100644 Binary files a/whoogle/stats.png and b/whoogle/stats.png differ diff --git a/xteve/stats.png b/xteve/stats.png index 0ecd5b75a6..100c355391 100644 Binary files a/xteve/stats.png and b/xteve/stats.png differ diff --git a/zoneminder/stats.png b/zoneminder/stats.png index d2a8f1a040..c49de3c208 100644 Binary files a/zoneminder/stats.png and b/zoneminder/stats.png differ diff --git a/zoraxy/stats.png b/zoraxy/stats.png index fc4718c438..533adceff9 100644 Binary files a/zoraxy/stats.png and b/zoraxy/stats.png differ diff --git a/zzz_archived_code-server/stats.png b/zzz_archived_code-server/stats.png index 75c3896529..bc237cfdbb 100644 Binary files a/zzz_archived_code-server/stats.png and b/zzz_archived_code-server/stats.png differ diff --git a/zzz_archived_jellyseerr/stats.png b/zzz_archived_jellyseerr/stats.png index 31e8b8ad65..8b7a4041ce 100644 Binary files a/zzz_archived_jellyseerr/stats.png and b/zzz_archived_jellyseerr/stats.png differ diff --git a/zzz_archived_kapowarr/stats.png b/zzz_archived_kapowarr/stats.png index a607c13a70..eb84651e18 100644 Binary files a/zzz_archived_kapowarr/stats.png and b/zzz_archived_kapowarr/stats.png differ diff --git a/zzz_archived_omada/stats.png b/zzz_archived_omada/stats.png index f742d037f4..6bfd1bb3a3 100644 Binary files a/zzz_archived_omada/stats.png and b/zzz_archived_omada/stats.png differ diff --git a/zzz_archived_omada_v3/stats.png b/zzz_archived_omada_v3/stats.png index abdcba6b2f..a15d382a16 100644 Binary files a/zzz_archived_omada_v3/stats.png and b/zzz_archived_omada_v3/stats.png differ diff --git a/zzz_archived_ombi/stats.png b/zzz_archived_ombi/stats.png index bfd8379e35..8fa40cb77c 100644 Binary files a/zzz_archived_ombi/stats.png and b/zzz_archived_ombi/stats.png differ diff --git a/zzz_archived_overseerr/stats.png b/zzz_archived_overseerr/stats.png index 0f2d757be2..a7d980e7cb 100644 Binary files a/zzz_archived_overseerr/stats.png and b/zzz_archived_overseerr/stats.png differ diff --git a/zzz_archived_paperless_ngx/stats.png b/zzz_archived_paperless_ngx/stats.png index 0f4be94e0a..6c09a29225 100644 Binary files a/zzz_archived_paperless_ngx/stats.png and b/zzz_archived_paperless_ngx/stats.png differ diff --git a/zzz_archived_papermerge/stats.png b/zzz_archived_papermerge/stats.png index 755919cc08..3f8a5894b5 100644 Binary files a/zzz_archived_papermerge/stats.png and b/zzz_archived_papermerge/stats.png differ diff --git a/zzz_archived_plex_meta_manager/stats.png b/zzz_archived_plex_meta_manager/stats.png index ac5bdb3e50..121d6fef15 100644 Binary files a/zzz_archived_plex_meta_manager/stats.png and b/zzz_archived_plex_meta_manager/stats.png differ diff --git a/zzz_archived_tor/stats.png b/zzz_archived_tor/stats.png index 59c57d06b3..9d4b534b1f 100644 Binary files a/zzz_archived_tor/stats.png and b/zzz_archived_tor/stats.png differ