Compare commits

..

1 Commits

Author SHA1 Message Date
claude-ai-fix[bot]
c2dad6c88e ci: fail a PR that changes an add-on without bumping its version
Supervisor only offers a rebuild when `version` changes, so an add-on fix
merged without one reaches nobody: the image stays put, the issue looks closed.
Nothing in CI checked this. Not hypothetical — PR #2972 changed birdnet-pi's
99-run.sh with no bump and sat green.

Worse, it also hid the rest of CI. check-addon-changes derives `changedAddons`
from `^<addon>/config\.(json|ya?ml)$` alone, and the CHANGELOG gate, add-on
linter and Docker build are all matrixed over it — so a PR that edits only an
add-on's scripts produces an empty set and every one of them *skips*. #2972 was
never linted or built, and birdnet-pi-zach turned out to have been failing to
build for five weeks behind that same gap.

This check therefore does NOT reuse `changedAddons`: gating on it would make the
check a no-op in exactly the case it exists for. It runs its own scan and
changes nothing about which add-ons get linted or built — widening that
detection is a separate change with real build-time cost.

Significance is an allowlist of ignorable paths anchored at the add-on root
(CHANGELOG, top-level *.md, icon/logo/stats png, images/, updater.json) with
everything else counting, so a new kind of file defaults to needing a bump.
Note addon/images/ is artwork but addon/rootfs/**/images/ ships, hence the
anchoring.

Reads the manifest at HEAD by its own resolved filename, so renaming
config.yaml -> config.yml mid-PR cannot slip through; parses config.json with
jq so a minified file is read correctly; anchors the YAML `version:` match at
column 0 so an indented key in a nested mapping is not mistaken for the
manifest's; strips a trailing YAML comment so `version: "1.2.3" # note` is not
read as a change. An unreadable version is an error, not a warning — the check
refuses to pass where it could not be performed.

The bypass label is read live inside the job rather than in its `if:`, because
the workflow deliberately does not listen for `labeled`: adding that activity
type would re-run the ~3 h add-on builds on every label change. Labelling the
PR and re-running this one job is the intended sequence.

Verified across 22 cases against real history and synthetic diffs: fails #2972
as originally opened and passes it after the bump, passes #2973/#2974 and the
current #2972; stays quiet on CHANGELOG-, stats.png-, updater.json-,
README-, .templates- and addon/images-only diffs; and fails on a changed
Dockerfile/rootfs, a config.yaml option change, rootfs/**/images, a
comment-only version edit, an indented nested version key, a mid-PR manifest
rename, a config.json add-on, and a missing version key.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 14:36:09 +02:00
147 changed files with 234 additions and 64 deletions

View File

@@ -29,8 +29,8 @@ Triage first, then one of two paths:
resolve bot comments.
- **Full loop** — performance/RAM/CPU work, diagnosis, anything changing a shipped default,
ladder levels 4-6, or an explicit Codex-check request: scope → measure → plan → Codex reviews
the plan → implement → simplify → Codex reviews the code → **simplify again** → PR → resolve
comments → verify in production → report.
the plan → implement → simplify → Codex reviews the code → PR → resolve comments → verify in
production → report.
Escalate mid-flight if a light task grows — touches a default, needs a new script or service, or
reveals a deeper problem.
@@ -42,10 +42,7 @@ where every add-on solves a problem the same way is worth more than a locally ni
design. Prefer reusing or extending over adding a parallel implementation, and when you must add
something new, spell it the way the rest of the repo spells it (naming, option names, script
numbering, file layout). Complexity is bought only by a **measurement** showing a concrete,
user-visible cost on a real host — never by reasoning about hypothetical performance, and never by
reasoning about a hypothetical *host* either. A defensive branch is complexity like any other: name
the input that reaches it and the image or host where that happens, or delete it and let the case
fail visibly instead.
user-visible cost on a real host — never by reasoning about hypothetical performance.
**Repo layout.** `alexbelgium/hassio-addons`; each add-on is a top-level directory. This skill is
checked in at `.claude/skills/hassio-addon-workflow/` (canonical copy). Set the skill root once,
@@ -116,9 +113,6 @@ Attack your own plan before implementing:
- What is the **blast radius** if the assumption underneath it is wrong?
- What am I **inferring** that I could instead **detect at runtime** or **record explicitly**?
Highest-yield question here — see `references/evidence.md`'s failure-mode section.
- For every branch that exists **only to survive something going wrong**: what input reaches it,
on which image? Go and look. A fallback for a configuration you cannot find an instance of is
not robustness, it is a second code path nobody will ever exercise or notice rotting.
Full loop only, before writing code: get Codex's independent read on the plan. Spawn a subagent
whose prompt includes the path `references/codex-review.md` and tells it to follow that file's
@@ -146,18 +140,11 @@ justify the divergence in the PR body — but never at the cost of an isolation
numbered script, not an edit. Case studies of what happens when this check is skipped:
`references/simplify.md`.
## 6. Codex attacks the code, then simplify what the review added (full loop only)
## 6. Codex attacks the code (full loop only)
Same delegated invocation, pointed at `git diff origin/master...HEAD` plus your reasoning per
hunk. Details in `references/codex-review.md`.
Then run step 5's checks again over the hunks the review changed. Adversarial review only ever
argues *for* another branch — that is its job — so accepting objections ratchets the diff upward,
and nothing else in the loop walks it back down. For each accepted objection: is the case it
defends one you have now demonstrated, or one you have merely been told about? Taking a
correctness objection often deletes the code that made it necessary, and a fix that collapses back
to fewer lines than you started the review with is the normal outcome, not a suspicious one.
## 7. Open the PR
CI gates on a PR: **`CHANGELOG.md` updated** (hard fail), the **HA add-on linter**

View File

@@ -39,10 +39,3 @@ codex exec --model gpt-5.6-sol --sandbox read-only --skip-git-repo-check \
**Codex agrees with confident premises.** It has confirmed a wrong conclusion stated too
confidently, and separately caught a genuine methodology error in the same review. Treat its
confirmations with the same scepticism as its objections — especially about the build.
**Its objections ratchet complexity upward.** An adversarial reviewer is asked to find what could
go wrong, so its output is a list of arguments for more code; it is never asked whether the branch
it wants is reachable. Separate "this is wrong" from "this is undefended" before you write
anything: the first is a bug and you fix it, the second is a claim about some host, and it needs
the same demonstration you would demand of a measurement. That is what step 6's second simplify
pass is for.

View File

@@ -13,15 +13,6 @@ Being able to build the complicated thing is not a reason to.
- A resolution cap shipped as a **new init script writing an s6 envdir** — the wrong mechanism
entirely (ladder level 4). Renaming the option to the env var the service already reads
(level 1) would have worked, and the new script did not.
- A `.templates/ha_entrypoint.sh` fix went to review at 25 lines of code and merged at 10. Two
sources of the excess, and neither was caught by the loop: a **pure-bash fallback** written at
implement time for images shipping `with-contenv` but not `s6-dumpenv` — reasoned from the two
binaries living in different s6 packages, never demonstrated on any real image, and the case it
defended would have degraded to the pre-fix behaviour anyway — and a **helper function plus a
second reset** that existed only to serve that fallback. Deleting the fallback deleted all of
it. The rest of the review's objections were correct and cost two tokens on an existing line.
It took the maintainer asking "is this the simplest way possible?" to run the pass that step 6
now requires.
## Checks worth running against your own diff
@@ -34,11 +25,6 @@ Being able to build the complicated thing is not a reason to.
A removal that depends on a host default still needs the same verification as an addition.
- **Is the fix bigger than the thing it fixes?** That is a smell, not a rule — but it usually
means the problem was framed one level too deep.
- **For each defensive branch: what input reaches it, on which image or host?** Go and check,
the way you would check a measurement. If you cannot produce the case, delete the branch — the
situation then fails the way it already fails today, visibly, instead of through a second path
that is never exercised and silently rots as the base images move. Write down in the PR body
what you cut and why, so the next person does not re-add it from the same reasoning.
- **How does this fail in three years**, when the base image, Electron, or upstream has moved?
Code that reads a documented knob keeps working. Code that reaches into private internals
does not.

143
.github/scripts/check_version_bump.sh vendored Executable file
View File

@@ -0,0 +1,143 @@
#!/usr/bin/env bash
# Destination: .github/scripts/check_version_bump.sh
#
# Fails a pull request that changes what an add-on ships without bumping that
# add-on's `version`. Supervisor only offers a rebuild when `version` changes,
# so a fix merged without one leaves every user on the old image: merged, inert,
# and the issue looks closed. Nothing else in CI checks this.
#
# It deliberately does NOT reuse check-addon-changes' `changedAddons`. That
# output is built from `^<addon>/config\.(json|ya?ml)$` alone, so it is empty
# for exactly the pull requests this check exists to catch — an add-on whose
# scripts changed while config.yaml did not. Reusing it would make this a no-op.
# This scan is local to this check and does not affect which add-ons get linted
# or built.
#
# Env:
# BASE_SHA (required) — commit this PR is diffed against
# HEAD_SHA (required) — the PR's merge commit
#
# Exit 0 = every add-on that needs a bump got one (or nothing relevant changed).
set -euo pipefail
: "${BASE_SHA:?BASE_SHA must be set}"
: "${HEAD_SHA:?HEAD_SHA must be set}"
# Files that ship to users only as repo metadata, or that bots rewrite on their
# own schedule. Changing one of these alone does not require anybody to receive
# a new image, so it must not demand a version bump — otherwise every changelog
# or stats-graph commit would fail CI.
#
# Everything else under an add-on directory counts: Dockerfile, rootfs/,
# build.json|yaml (base images), apparmor.txt and translations/ (Supervisor
# re-reads them on update), root-level *.sh that Dockerfiles COPY, and
# config.yaml itself — an added option or changed port needs the update offered
# just as much as a code change does. Allowlisting the ignorable and treating
# the remainder as significant fails closed: a new kind of file defaults to
# "needs a bump" rather than silently escaping the check.
is_ignorable() {
local rel="$1" # path relative to the add-on directory
# addon/images/** is artwork; addon/rootfs/**/images/** is shipped content,
# so this must be anchored at the add-on root rather than matching any
# path that happens to contain an "images" segment.
case "$rel" in
images/*) return 0 ;;
esac
# Anything else nested ships inside the image (rootfs/, translations/, ...).
case "$rel" in
*/*) return 1 ;;
esac
case "$rel" in
CHANGELOG.md | updater.json | stats.png | icon.png | logo.png | *.md) return 0 ;;
esac
return 1
}
# An add-on is a top-level directory with a config file. Tested against the
# BASE tree so a directory deleted by this PR is still recognised (and then
# skipped below), and .github/, .templates/ and .claude/ are excluded for free
# by simply not having one.
addon_config_at() {
local ref="$1" addon="$2" f
for f in config.yaml config.yml config.json; do
if git cat-file -e "${ref}:${addon}/${f}" 2> /dev/null; then
printf '%s' "$f"
return 0
fi
done
return 1
}
# Reads the add-on's declared version. JSON goes through jq so a minified or
# reordered config.json is read correctly rather than silently returning empty.
# YAML is matched at column 0 on purpose: an indented `version:` belongs to a
# nested mapping (a schema entry, an option literally named version) and
# comparing it would compare the wrong value. A trailing YAML comment is
# stripped before quotes so `version: "1.2.3" # note` does not read as a bump.
version_at() {
local ref="$1" path="$2" content
content=$(git show "${ref}:${path}" 2> /dev/null) || return 1
case "$path" in
*.json)
printf '%s' "$content" | jq -r '.version // empty' 2> /dev/null
;;
*)
printf '%s\n' "$content" |
grep -m1 -E '^version[[:space:]]*:' |
sed -E 's/^version[[:space:]]*:[[:space:]]*//; s/[[:space:]]+#.*$//; s/^["'\'']//; s/["'\'']$//; s/[[:space:]]*$//'
;;
esac
}
mapfile -t CHANGED < <(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
declare -A NEEDS_BUMP=()
for file in "${CHANGED[@]}"; do
[ -n "$file" ] || continue
case "$file" in */*) ;; *) continue ;; esac # top-level files are not add-ons
addon="${file%%/*}"
is_ignorable "${file#*/}" && continue
addon_config_at "$BASE_SHA" "$addon" > /dev/null 2>&1 || continue
NEEDS_BUMP["$addon"]=1
done
if [ "${#NEEDS_BUMP[@]}" -eq 0 ]; then
echo "No add-on changes that require a version bump."
exit 0
fi
FAILED=0
for addon in $(printf '%s\n' "${!NEEDS_BUMP[@]}" | sort); do
base_cfg=$(addon_config_at "$BASE_SHA" "$addon")
# Resolved separately at HEAD: an add-on that renames its manifest between
# supported names (config.yaml -> config.yml) while changing shipped files
# would otherwise be read at the old path, come back empty, and slip through.
if ! head_cfg=$(addon_config_at "$HEAD_SHA" "$addon"); then
echo " $addon: removed by this PR, skipping"
continue
fi
old=$(version_at "$BASE_SHA" "$addon/$base_cfg" || true)
new=$(version_at "$HEAD_SHA" "$addon/$head_cfg" || true)
# Fail closed. An unreadable version used to warn and skip, which let the
# job go green on exactly the add-ons whose manifest this check could not
# understand — the opposite of what it is for.
if [ -z "$old" ] || [ -z "$new" ]; then
echo "::error file=$addon/$head_cfg::$addon: could not read a version from $base_cfg (base) or $head_cfg (head). Refusing to pass a check that could not be performed."
FAILED=1
continue
fi
if [ "$old" = "$new" ]; then
echo "::error file=$addon/$head_cfg::$addon ships changed files but version is still $old. Supervisor only offers a rebuild when version changes, so this would merge without reaching anyone. Bump it following this add-on's own convention: for a local patch counter take the boundary from updater.json's upstream_version (append .1 when version equals it, otherwise increment the digits after it); date-based and LSIO-style versions have no counter and follow their own scheme."
FAILED=1
else
echo " $addon: $old -> $new"
fi
done
if [ "$FAILED" -ne 0 ]; then
echo "::error::One or more add-ons changed without a version bump. Add the 'skip-version-check' label and re-run this job if that is deliberate."
exit 1
fi
echo "All changed add-ons have a version bump."

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 404 KiB

BIN
.github/stats.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -58,6 +58,69 @@ jobs:
echo "Changed addons: $changed_addons"
echo "changed_addons=$changed_addons" >> "$GITHUB_OUTPUT"
# 1b. A pull request that changes what an add-on ships must bump that add-on's
# version. Supervisor only offers a rebuild when `version` changes, so without
# one the fix merges and no user ever receives it - inert, while the issue
# looks closed. Nothing else in CI checks this (the add-on linter validates
# the schema, not that the value moved).
#
# Deliberately independent of check-addon-changes above: that job derives
# `changedAddons` from `^<addon>/config.(json|ya?ml)$` alone, so it is empty
# for precisely the pull requests this catches - an add-on whose scripts
# changed while config.yaml did not. Gating this on its output would make it
# a no-op. It does its own scan and changes nothing about which add-ons are
# linted or built.
check-version-bump:
name: Check add-on version bumped
if: ${{ github.repository_owner == 'alexbelgium' }}
runs-on: ubuntu-latest
# Only reads git history and the PR's labels.
permissions:
contents: read
pull-requests: read
steps:
# The bypass label is read LIVE here rather than from the job's `if:`.
# The event payload is a snapshot from trigger time, and this workflow
# deliberately does not listen for `labeled` — adding that activity type
# would re-run the ~3 h add-on builds on every label change. Reading it
# at run time instead means "label the PR, then re-run this one job"
# works, which is the sequence the failure message asks for.
- name: Check for the bypass label
id: bypass
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
skip=$(gh pr view "$PR" --repo "$REPO" --json labels \
--jq '[.labels[].name] | index("skip-version-check") != null')
echo "skip=$skip" >> "$GITHUB_OUTPUT"
[ "$skip" = "true" ] && echo "skip-version-check label present; skipping." || true
- name: Checkout repo
if: steps.bypass.outputs.skip != 'true'
uses: actions/checkout@v7.0.1
with:
# Same reason as check-addon-changes: HEAD^1 must be resolvable.
fetch-depth: 2
# Nothing here pushes, and the repo is public, so an anonymous fetch
# of the base commit is enough - do not leave a token in .git/config.
persist-credentials: false
- name: Check every changed add-on bumped its version
if: steps.bypass.outputs.skip != 'true'
env:
HEAD_SHA: ${{ github.sha }}
run: |
set -euo pipefail
# HEAD^1, not pull_request.base.sha, for the same reason as above: the
# event payload's base can be stale if master advanced since trigger.
BASE_SHA=$(git rev-parse HEAD^1)
git fetch origin "$BASE_SHA"
export BASE_SHA
bash .github/scripts/check_version_bump.sh
check-changed-changelog:
name: Check if CHANGELOG.md changed
needs: check-addon-changes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,7 +1,4 @@
## 0.6.27.2 (2026-08-23)
- Fix: trust the whole supervisor network range for the ingress auth header instead of the addon's own address, which changes across restarts. The list is only written when that range is missing, so an entry added in the calibre-web admin page is no longer erased on every start (https://github.com/alexbelgium/hassio-addons/pull/3010)
## 0.6.27.1 (2026-08-23)
- Fix: Ingress login was rejected since 0.6.27, which only accepts the reverse proxy auth header from trusted source addresses. The addon now adds its own ip to that list (https://github.com/alexbelgium/hassio-addons/issues/3003)

View File

@@ -116,5 +116,5 @@ schema:
slug: calibre-web
udev: true
url: https://github.com/alexbelgium/hassio-addons/tree/master/calibre_web
version: "0.6.27.2"
version: "0.6.27.1"
video: true

View File

@@ -19,19 +19,25 @@ if [ ! -f /config/app.db ]; then
else
sqlite3 /config/app.db 'update settings set config_reverse_proxy_login_header_name="X-WebAuth-User",config_allow_reverse_proxy_header_login=1'
# Calibre-web 0.6.27 only accepts that header from a trusted source address, and defaults the
# list to "127.0.0.1,::1". Ingress reaches calibre-web from the addon's own address on the
# supervisor network (proxy_bind $server_addr in ingress.conf) and calibre-web listens
# dual-stack, so it sees ::ffff:<addon ip> and drops the header. The supervisor range is
# listed in both forms because an ipv4 entry never matches an ipv4-mapped address.
# Prepended to whatever is already there, and only when the mapped form is missing : that form
# is the one ingress needs and the one nobody types by hand, so it doubles as the marker that
# this already ran. Anything the user added is kept, the statement runs at most once, and the
# 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})"
# Calibre-web 0.6.27 only accepts the ingress auth header from a trusted source address, and
# defaults that list to "127.0.0.1,::1". Nginx binds its upstream socket to the addon ip
# (proxy_bind $server_addr in ingress.conf) and calibre-web listens dual-stack, so it sees
# ::ffff:<addon ip> and drops the header. Both the plain and the ipv4-mapped forms are listed
# because an ipv4 entry never matches an ipv6-mapped address on the calibre-web side.
# The column only exists once calibre-web 0.6.27+ has migrated app.db, so a failure here is
# not fatal : the next start applies it.
addon_ip=$(bashio::addon.ip_address)
trusted_ips="127.0.0.1,::1,::ffff:127.0.0.1"
if bashio::var.has_value "${addon_ip}"; then
trusted_ips="${trusted_ips},${addon_ip},::ffff:${addon_ip}"
fi
trusted_ips_error=$(sqlite3 /config/app.db "update settings set config_reverse_proxy_trusted_ips='${trusted_ips}'" 2>&1) || {
if echo "${trusted_ips_error}" | grep -q "no such column"; then
bashio::log.warning "Could not set the ingress trusted ip list, it will be applied at next start"
else
bashio::log.warning "Could not set the ingress trusted ip list: ${trusted_ips_error}"
fi
}
fi
bashio::log.info "Default username:password is admin:admin123"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,8 +1,3 @@
## 2.44.0 (2026-08-23)
- Fix: Use `portainer/agent:alpine-sts` to match the STS release channel configured in `updater.json`
- Fix: Align add-on versioning with Portainer STS releases
## 2025.12.7 (2026-08-23)
- Fix: Docker reported the addon as `unhealthy` in Portainer. The healthcheck script could never run because its shebang required the s6-overlay environment, which this addon's entrypoint does not set up (https://github.com/alexbelgium/hassio-addons/issues/3002)

View File

@@ -18,7 +18,7 @@
ARG BUILD_FROM
# Get agent
FROM portainer/agent:alpine-sts as original_agent
FROM portainer/agent:alpine as original_agent
ENV PORTAINER_AGENT_ARGS=""
# Build using base

View File

@@ -41,4 +41,4 @@ schema:
slug: portainer_agent
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "2.44.0"
version: "2025.12.7"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,6 +1,6 @@
{
"github_tagfilter": "sts",
"last_update": "23-08-2026",
"github_tagfilter": "alpine",
"last_update": "24-12-2025",
"repository": "alexbelgium/hassio-addons",
"slug": "portainer_agent",
"source": "dockerhub",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

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