Compare commits

...

7 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
fc33543d4b Merge remote-tracking branch 'origin/master' into create-pull-request/patch-1788655917
# Conflicts:
#	.claude/skills/hassio-addon-workflow/scripts/pr_review.sh

Co-authored-by: alexbelgium <44178713+alexbelgium@users.noreply.github.com>
2026-09-07 11:26:55 +00:00
Alexandre
bcde474c15 skill(hassio-addon-workflow): stop pr_review.sh watch reporting a false pass (#3052)
* skill(hassio-addon-workflow): stop pr_review.sh watch reporting a false pass

`gh pr checks` emits TAB-separated columns, but `watch` parsed it with awk's
default field splitting. Every check name containing a space was truncated to
its first word and the state column was never read:

    Codacy Static Code Analysis<TAB>fail   ->  Codacy=Static
    Addon linting (wger)<TAB>pass          ->  Addon=linting
    Test addon build (wger)<TAB>pending    ->  Test=addon

All three blocking gates have multi-word names, so the `case` matched neither
*fail* nor *pending* and fell through to "settled - all passing". That is a
false pass from the one command whose job is to report CI truthfully: #3044
was called green with Codacy red, and #3042 was called green while the HA
add-on linter was failing. A build that had not started would also have read
as a pass.

- parse with `awk -F'\t'`
- judge the state column alone, never the joined name=state text, so a check
  named e.g. `flaky-fail-detector` cannot read as a failure
- allowlist the good states (pass/skipping/pending) and treat anything
  unrecognised as a failure, so a new state cannot reach the passing branch
- name the checks that failed instead of only saying FAILURES
- keep waiting when only advisory checks have reported

Codacy is red on essentially every add-on PR here (#3019, #3044 and #3050 all
merged with it failing; master has no branch protection), so it is excluded
from the verdict but printed every poll and called out explicitly on settle.
Agreed with the maintainer. It is a denylist of known noise rather than an
allowlist of gates, so a job added to CI later counts as blocking by default.

Verified against real PRs: #3042 (blocking linter failure) now exits 1 and
names the check where it previously exited 0; #3018/#3019/#3044/#3050 report
correctly; pending, advisory-only, unknown-state and empty-output cases
checked against a stubbed gh. shellcheck clean.

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

* skill: record PR number in the traps entry

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

* skill: address review — skipped-checks wording, document TSV contract

CodeRabbit (correct): the header claimed exit 0 means "every blocking check
passed", but a skipped gate also yields 0. A PR touching no add-on skips all
three gates, so that wording overstated what a 0 means. Reworded; the runtime
warning about skipped jobs was already there.

Copilot recommended switching to `gh pr checks --json`. Not applied: that flag
does not exist before gh 2.36 and 2.23 ships in this add-on, where it fails
with `unknown flag: --json`. Its premises are also wrong for the path the
script takes — piped output carries no header and uses real tabs; the aligned
ANSI table is the TTY renderer, which $(... | awk) never gets. Documented the
non-TTY contract and the gh-version constraint in the comment and traps.md so
this is not "corrected" back into a break later.

The underlying worry — a format change reintroducing a false pass — is already
answered by the allowlist design, now verified explicitly: a header row lands
in the failure branch (exit 1) and a space-aligned table parses to zero rows,
so watch keeps waiting. Neither can return 0.

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

---------

Co-authored-by: BirdNET-Go Addon Builder <addon-builder@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 13:23:28 +02:00
GitHub Actions
15b6736818 Revert "birdnet-go-dev: rebuild after upstream sync and conflict fixes in fork PRs #6 and #36"
This reverts commit 96526dc31f.
2026-09-07 08:42:55 +00:00
alexbelgium
96526dc31f birdnet-go-dev: rebuild after upstream sync and conflict fixes in fork PRs #6 and #36 2026-09-07 10:38:44 +02:00
Alexandre
89654e4f13 fix(omni-tools): let the add-on stop by backgrounding nginx and running the entrypoint as PID 1 (#3050)
The add-on could not be stopped: Home Assistant showed an Error status
after a few seconds and the container kept running and still served the
web UI (#3049).

cont-init.d/99-run.sh started nginx in the foreground -- '&>' is a
redirect, not a background operator -- and ha_entrypoint.sh runs every
cont-init.d script sequentially in the foreground. That script therefore
never returned, so the entrypoint never reached the code that installs
the terminate() handler forwarding SIGTERM to the application. The
reporter's log shows both halves of this: it prints 'Starting custom
scripts' and never reaches 'Everything started!'.

The add-on also shipped no 'init:' key, so Supervisor's default of true
made Docker inject its own init as PID 1 and left ha_entrypoint.sh as
PID 2, where the 'if $PID1' block holding the trap is skipped outright.

Background the launch and set init: false. The script then returns, the
entrypoint installs its trap, and nginx -- orphaned by the exiting
script -- is reparented to the entrypoint as PID 1, where terminate()'s
'pgrep -P $$' finds it and signals it directly.

Backgrounding from cont-init.d is what 24 other add-ons here already do
(autobrr runs a bare 'nginx &'). Moving the launch to services.d was
considered and rejected: ha_entrypoint.sh runs each services.d/*/run
inside a restart subshell, so the application ends up a grandchild of
PID 1 while terminate() enumerates direct children only. Reproduced --
the app survives that path unsignalled -- and it is the larger change.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 10:25:33 +02:00
github-actions
607487cfb8 Github bot : image compressed 2026-09-06 23:12:31 +00:00
github-actions[bot]
6c5e2c171a Update stargazer map & cache 2026-09-06 00:55:35 +00:00
139 changed files with 185 additions and 120 deletions

View File

@@ -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 Analysis<TAB>fail` becomes `Codacy=Static`, and
`Test addon build (wger)<TAB>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 `## <version> (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

View File

@@ -8,6 +8,10 @@
# pr_review.sh resolve <PR> <THREAD_ID...|--all> --all = every unresolved, asks first
# pr_review.sh watch <PR> [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 <PR> <THREAD_ID...> (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 Analysis<TAB>fail" 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 <PR> <THREAD_ID...> (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

View File

@@ -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
1 username country last_checked
418 KuerbisK 2026-08-10
419 Kvasenok 2026-08-10
420 KyleGolfer 2026-08-10
421 L00PERY 2026-09-06
422 L0rdShrek Germany
423 L1nKinc Germany
424 LaLaBer 2026-08-10
445 Lolekpolek 2026-08-10
446 LonelySoul7X 2026-08-10
447 Loong-He China 2026-08-30
448 Loong-OvO China 2026-09-06
449 Lorsel Italy
450 Luca2165801154 2026-08-10
451 Lucius-Waverly 2026-08-16
1392 flozi00 Germany
1393 flue17 2026-08-10
1394 fmcglinn Australia
1395 foodstampou812 United States 2026-09-06
1396 forming 2026-08-10
1397 forreggbor Hungary
1398 foundbobby United States
1837 m23l 2026-08-10
1838 m2sh Iran, Islamic Republic of
1839 m4rcSA 2026-08-10
1840 m4rkolson United States 2026-09-06
1841 mProwler United States
1842 mStrangers 2026-08-10
1843 mabt 2026-08-10
2089 paalwilliams Germany
2090 pace6666 2026-08-10
2091 pafnow France
2092 pahanitsch Germany 2026-09-06
2093 pandabreads 2026-08-10
2094 pankaj151 India
2095 papafrank66 2026-08-10
2669 yyll2233 2026-08-10
2670 z2833 2026-08-10
2671 z307917424 2026-08-10
2672 zacharee Panama 2026-09-06
2673 zachgilliam United States
2674 zanyraspi 2026-08-10
2675 zdaar 2026-08-10

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
.github/stats.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -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.

View File

@@ -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]"

View File

@@ -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 &

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More