Compare commits

..

1 Commits

Author SHA1 Message Date
alexbelgium
d9b92f4e70 docs(skill): simplify again after the code review, and demand a trigger for defensive branches
The full loop ran simplify (step 5) before Codex's code review (step 6) and never
again, so nothing walked back what the review added. Adversarial review is asked to
find what could go wrong, so its output is a list of arguments for more code and it
is never asked whether the branch it wants is reachable — accepting objections only
ratchets the diff upward. Step 6 now ends by re-running step 5's checks over the
hunks the review touched.

The other half was earlier than the review. The standing rule already said complexity
is bought only by a measurement, but it said it about performance, so a branch added
for robustness did not visibly fall under it. It now covers hypothetical hosts as
well as hypothetical performance: name the input that reaches a defensive branch and
the image it happens on, or delete it and let the case fail visibly. Step 3's
attack-your-own-plan list asks the same question before any code exists, which is
where it is cheapest to answer.

The case study in references/simplify.md is #3013: 25 lines of code at review, 10
merged. 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 a real image, and defending a case that would have
degraded to the pre-fix behaviour anyway — plus the helper function and second reset
that existed only to serve it. Deleting the fallback deleted all of it. The review's
own objections were correct and cost two tokens on an existing line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 14:11:35 +02:00
35 changed files with 101 additions and 380 deletions

View File

@@ -116,10 +116,9 @@ 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**: name the image or host
where that input actually arrives, and go and look. Naming is the bar, not reproducing it here —
`references/simplify.md` works the `/dev/shm` guard and the `s6-dumpenv` fallback through that
distinction.
- 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
@@ -159,19 +158,13 @@ defends one you have now demonstrated, or one you have merely been told about? T
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.
These edits land after step 4's checks already ran, so re-run them: `scripts/validate.sh <addon>
--vs-master` plus the behavioural tests, over the final diff. Deleting a branch is exactly the
kind of edit that leaves a stray `fi` behind.
## 7. Open the PR
CI gates on a PR: **`CHANGELOG.md` updated** (hard fail), the **HA add-on linter**
(`frenck/action-addon-linter`, blocking — not the weekly Super-Linter, which is non-blocking), and
the **add-on image build**. Bump `version` anyway (`X.Y.Z.N`, never `X.Y.Z-N`, see
`references/traps.md#versioning`) — Supervisor won't offer a rebuild without it. Update
`README.md` if you added options; write the CHANGELOG heading as `## <version> (<date>)`,
matching the date format already in that file — almost always ISO `YYYY-MM-DD`, see
`references/traps.md#ci-and-review-bots`.
`README.md` if you added options; match the CHANGELOG heading format `## X.Y (DD-MM-YYYY)`.
Write the body to a file, `gh pr create --body-file`: state what was measured, what changed,
**what is not verified**, and how to roll back the riskiest hunk alone.
@@ -192,12 +185,7 @@ work" — either it was exercised, or say plainly it wasn't.
Light path: verification is `validate.sh` plus CI; anything beyond that is Assumed. Full loop: CI
passing proves the build works, not that the change does anything — re-run the measurement that
motivated the work once the rebuilt add-on is running. After merge, `git fetch origin master`
(the tracking ref is stale otherwise), then confirm the *changes* survived — `git diff
origin/master -- <the paths you touched>` comes back empty. Ancestry is not the check: a revert
leaves your commit in history and undoes its tree, so `--contains` reports success either way. The
builder's revert-on-failure job can revert a merge for reasons unrelated to your diff (see
`references/traps.md#ci-and-review-bots`). Real "merged and inert" examples, and what
motivated the work once the rebuilt add-on is running. Real "merged and inert" examples, and what
to do when a fix can't be self-verified: `references/evidence.md`.
## 10. Calibrate and report
@@ -216,9 +204,5 @@ Risk + rollback — the riskiest hunk and how to revert it alone
Lead with anything that did not work — a merged PR that achieved nothing is the single most
important sentence in the report. Give confidence per claim, not one blanket number.
**Feed the skill.** When a shipped fix needed a follow-up PR, or a reviewer caught something this
skill should have, add the distilled lesson to the matching `references/` file in that follow-up
PR — one entry, with the PR numbers. That loop is what keeps this file short and the traps real.
Scripts are meant to be **run, not read** — each is cited at its point of use above; read one
only if its output surprises you.

View File

@@ -1,7 +1,7 @@
# Simplify — case studies
Evidence for why the mechanism ladder in SKILL.md step 3 exists, and why levels 4-6 need a reason
that survives being said out loud. In each case the simpler option existed and was skipped.
that survives being said out loud. In all three cases the simpler option existed and was skipped.
Being able to build the complicated thing is not a reason to.
- A rejected PR spent a **388-line TCP proxy plus a 142-line monkeypatch of a private upstream
@@ -13,13 +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 calibre-web trusted-ips fix injected the add-on's **per-boot IP**, which forced a
rewrite-every-boot design that erased user entries; preserving them then needed merge logic
plus a state file recording what was injected (+34 lines, PR #3009 — closed unmerged). Asking
"is there a constant that makes the rewrite unnecessary?" gave the shipped fix: trust the
static supervisor range `172.30.32.0/23`, one idempotent statement, **net −6 lines**
(PR #3010). Complexity spent working around a changing value is a sign to hunt for the
constant instead.
- 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
@@ -42,16 +35,10 @@ Being able to build the complicated thing is not a reason to.
- **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. The bar is being able to **name** the case, not to
reproduce it here: Docker's 64 MB `/dev/shm` default is documented behaviour that HA does not
override, so the bullet above keeps that guard even though this host measured 7.7 GB. Nobody
could name a single image shipping `with-contenv` without `s6-dumpenv`, so that fallback went.
If you cannot name 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. Weigh the cost too: a one-flag guard against a crash you cannot
rule out is cheap, a second code path that degrades to the pre-fix behaviour anyway is not.
Write down in the PR body what you cut and why, so the next person does not re-add it from the
same reasoning.
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.

View File

@@ -11,7 +11,6 @@ workflows and lint rules — that is not repeated here.
- [Environment and workspace](#environment-and-workspace)
- [Measurement](#measurement)
- [Passing values into base-image services](#passing-values-into-base-image-services)
- [Writing into an app's own config](#writing-into-an-apps-own-config)
- [Shell and bashio](#shell-and-bashio)
- [Dockerfile and architecture](#dockerfile-and-architecture)
- [Versioning](#versioning)
@@ -117,26 +116,6 @@ place for filesystem and permission setup.
the openbox autostart. (ANGLE's OpenGL backend, for instance, fails with "Could not open the
default X display".)
## Writing into an app's own config
**A field your cont-init script writes may also be user-editable in the app's UI.** An
unconditional `UPDATE`/overwrite on every boot silently erases whatever the user added there,
and containers are recreated on restart so it re-erases forever (calibre-web
`config_reverse_proxy_trusted_ips`, #3004 — flagged by two review bots, fixed in #3010).
Prepend/merge with an idempotence guard instead of assigning.
**Prefer values that are constant across boots.** The add-on's own IP changes every restart,
so injecting it forces a rewrite-every-boot design plus stale-entry cleanup (a stale trusted IP
can be handed to a *different* add-on later). Trusting the whole supervisor range
`172.30.32.0/23` is constant, written once. For dual-stack listeners the IPv4 form never
matches IPv4-mapped addresses — also list the mapped form (`::ffff:172.30.32.0/119`).
Constant is not free when the value gates **authentication**: trusting the whole range means any
add-on on the supervisor network can send the auth header and impersonate a user. #3010 shipped
that as an explicit, stated trade-off with the maintainer's sign-off. State the blast radius in
the PR body and get the maintainer's call before widening trust — never present it as a neutral
simplification.
## Shell and bashio
**`bashio::config` for lists**: `while read ... < <(bashio::config ...)` silently yields an empty
@@ -216,17 +195,6 @@ All three hard gates below are matrixed over `check-addon-changes.outputs.change
- **Version bump** — no workflow checks it. It is repo convention, and required for Supervisor to
offer the rebuild, but it will not fail CI.
**"Merged" is not "on master".** The push builder's revert-on-failure job reverts the merge
commit when its prebuild step fails — including failures unrelated to your diff. A seerr fix
merged at 05:15 and was reverted one minute later because `EndBug/add-and-commit`'s floating
`v11` tag had moved to a broken release (#2993, reapplied verbatim in #2997). After merge,
`git fetch origin master` first — the remote-tracking ref is stale otherwise and would "confirm"
against pre-merge state — then check that `git diff origin/master -- <the paths you touched>` is
empty before declaring done. Scope it to your paths: master moves under you, so whole-tree
equality fails on unrelated commits. Ancestry is not the check either — this repo squash-merges,
so a merged PR head is never an ancestor of `master` (verified on #3010, whose fix is live), and
a revert leaves the original commit an ancestor anyway.
**CI rewrites your shell scripts.** `lint.yml` runs `shfmt -w -i 4 -ci -bn -sr` over every `*.sh`
and `run`, plus a `chmod +x` pass, on schedule. Repo-wide reformatting commits land on master
without your involvement — another reason a shared checkout goes stale mid-task.
@@ -241,18 +209,6 @@ account. Note it and move on rather than guessing.
**Resolving a review thread requires GraphQL** (`resolveReviewThread`); the REST API cannot do it.
`scripts/pr_review.sh` wraps fetch / reply / resolve.
**Most CHANGELOG heading dates are ISO, whatever the bots' defaults say.** Match the format
already in the add-on's file — a `DD-MM-YYYY` file stays `DD-MM-YYYY`. Where you have no
precedent, ISO is the house style: as of 2026-08-25, `## <version> (YYYY-MM-DD)` accounts for
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 gets a `DD-MM-YYYY` entry (#3019). `DD-MM-YYYY` is not
invented — it is what `onpush_builder.yaml` writes with `date '+%d-%m-%Y'` when it has to insert
a heading you forgot, and what the addons_updater bot writes when its `date_iso8601` option is
off (`99-run.sh`; it is on in production here) — but neither is a reason to write it yourself.
The builder's duplicate check is `grep -q "^## ${version} ("` — an unescaped BRE, so the dots in
a version match any character, and it does not look at the date at all. Either way an ISO heading
you wrote yourself still suppresses the bot's insertion.
**The repo's `.markdownlint.yaml` does not disable MD022/MD032**, so a CHANGELOG will show
dozens of pre-existing heading/list findings. They are noise because lint is `continue-on-error`,
not because the config exempts them — don't cite the config as a reason to ignore a finding.

View File

@@ -90,7 +90,6 @@ resolve)
;;
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
@@ -103,14 +102,11 @@ watch)
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 ;;
*) echo "settled — all passing"; break ;;
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"
exit "${wfail:-0}"
;;
*) echo "unknown: $CMD"; exit 1 ;;
esac

View File

@@ -64,8 +64,7 @@ if [ -n "${BUILD_VERSION:-}" ]; then
echo " $SLUG/config.yaml version = $here"
echo " running image BUILD_VERSION = $BUILD_VERSION"
if [ "$here" = "$BUILD_VERSION" ]; then
# version is bumped once per PR, so a later commit or a dirty tree matches here.
echo " VERSION MATCH — source revision itself is not verified."
echo " MATCH — this checkout corresponds to the running image."
else
echo " MISMATCH — this branch is NOT what is running."
git fetch origin master --quiet 2> /dev/null

View File

@@ -4,13 +4,10 @@
# rather than implying the build was checked.
#
# --vs-master re-lints each changed file at origin/master and prints only findings your diff
# ADDED, and fails if there are any. Without it you will chase warnings that were already there.
# ADDED. Without it you will chase warnings that were already in the file.
#
# Usage: validate.sh [addon-dir] [--vs-master]
set -uo pipefail
# git diff prints repo-root-relative paths and $ADDON is a top-level directory name: neither
# resolves from a subdirectory, where the -f guard below would skip every file and report clean.
if root=$(git rev-parse --show-toplevel 2> /dev/null); then cd "$root" || exit 1; fi
ADDON="${1:-}"
[ "${ADDON:-}" = "--vs-master" ] && { ADDON=""; set -- --vs-master; }
@@ -34,21 +31,17 @@ echo "== validating $ADDON =="
fail=0
note() { printf ' %-13s %s\n' "$1" "$2"; }
# execline `run`/`finish` files are not shell (25 of them here, across 21 add-ons). Neither
# linter below can read one, so anything either says about it is noise.
is_execline() { local l; IFS= read -r l < "$1" 2> /dev/null; [[ $l == '#!'*execlineb* ]]; }
# Shell: bash -n then shellcheck -x (follows sourced files, as CI does). One list for both.
files=()
while IFS= read -r f; do is_execline "$f" || files+=("$f"); done \
< <(find "$ADDON" -type f \( -name '*.sh' -o -name 'run' -o -name 'finish' -o -name 'autostart' \) 2> /dev/null)
for f in "${files[@]}"; do
# Shell: bash -n then shellcheck -x (follows sourced files, as CI does).
while IFS= read -r f; do
[ -f "$f" ] || continue
if ! out=$(bash -n "$f" 2>&1); then note "bash -n" "FAIL $f"; echo "$out" | sed 's/^/ /'; fail=1; fi
done
[ "$fail" -eq 0 ] && note "bash -n" "${#files[@]} file(s) checked"
done < <(find "$ADDON" -type f \( -name '*.sh' -o -name 'run' -o -name 'finish' -o -name 'autostart' \) 2> /dev/null)
[ "$fail" -eq 0 ] && note "bash -n" "ok"
if [ "${#files[@]}" -gt 0 ] && command -v shellcheck > /dev/null 2>&1; then
sc=$(shellcheck -x -f gcc "${files[@]}" 2>&1)
if command -v shellcheck > /dev/null 2>&1; then
sc=$(find "$ADDON" -type f \( -name '*.sh' -o -name 'autostart' -o -name 'run' -o -name 'finish' \) -print0 2> /dev/null |
xargs -0 -r shellcheck -x -f gcc 2>&1)
if [ -n "$sc" ]; then
note "shellcheck" "$(printf '%s\n' "$sc" | grep -c .) finding(s)"
printf '%s\n' "$sc" | sed 's/^/ /' | head -20
@@ -57,10 +50,7 @@ fi
command -v hadolint > /dev/null 2>&1 && [ -f "$ADDON/Dockerfile" ] && {
hl=$(hadolint "$ADDON/Dockerfile" 2>&1)
if [ -n "$hl" ]; then
note "hadolint" "$(printf '%s\n' "$hl" | grep -c .) finding(s)"
printf '%s\n' "$hl" | sed 's/^/ /' | head -10
else note "hadolint" "clean"; fi
[ -n "$hl" ] && { note "hadolint" "$(printf '%s\n' "$hl" | grep -c .) finding(s)"; printf '%s\n' "$hl" | sed 's/^/ /' | head -10; } || note "hadolint" "clean"
}
if [ -f "$ADDON/config.yaml" ]; then
@@ -89,14 +79,11 @@ $VS_MASTER && command -v npx > /dev/null 2>&1 && [ -f "$ADDON/CHANGELOG.md" ] &&
echo
echo "== CI requirements =="
# -Fxq, not -q: unanchored, seerr's is matched by zzz_archived_overseerr's, and . is a wildcard.
# Stricter than the gate itself, whose quoted =~ accepts that same collision
# (onpr_check-pr.yaml:75), so this can fail where CI passes — the wrong add-on's is still wrong.
if git diff --name-only origin/master...HEAD 2> /dev/null | grep -Fxq "$ADDON/CHANGELOG.md"; then
if git diff --name-only origin/master...HEAD 2> /dev/null | grep -q "$ADDON/CHANGELOG.md"; then
note "CHANGELOG" "updated"
else
# This one IS gated: onpr_check-pr.yaml exits 1 without it.
note "CHANGELOG" "NOT UPDATED for $ADDON — CI hard-gates this"; fail=1
note "CHANGELOG" "NOT UPDATED — this is the one CI hard-gate"; fail=1
fi
if git diff origin/master...HEAD -- "$ADDON/config.yaml" 2> /dev/null | grep -q '^+version:'; then
note "version" "bumped"
@@ -111,15 +98,8 @@ if $VS_MASTER; then
echo
echo "== findings ADDED by this diff (pre-existing ones filtered out) =="
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
added=0
# Fed by process substitution, not a pipe: a pipeline runs this in a subshell, where the
# findings below could never reach $fail and the verdict would contradict the list.
while IFS= read -r f; do
# Deleted: linting the path that is gone invents a finding. Added: no base, and an
# empty one says the right thing — every finding in it is one this diff added.
[ -f "$f" ] || continue
is_execline "$f" && continue
git show "origin/master:$f" > "$tmp/base" 2> /dev/null || : > "$tmp/base"
git diff --name-only origin/master...HEAD -- "$ADDON" 2> /dev/null | while IFS= read -r f; do
git show "origin/master:$f" > "$tmp/base" 2> /dev/null || continue
# A missing linter must be a visible skip, not a silent "no new findings":
# its "command not found" error is identical for base and head, so comm would
# cancel it out and report a false clean.
@@ -135,16 +115,12 @@ if $VS_MASTER; then
cmd() { hadolint "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*//'; } ;;
*) continue ;;
esac
b=$(cmd "$tmp/base" | sort)
cp "$tmp/base" "$tmp/base_f"; b=$(cmd "$tmp/base_f" | sort)
a=$(cmd "$f" | sort)
new=$(comm -13 <(printf '%s\n' "$b") <(printf '%s\n' "$a"))
[ -n "$new" ] && {
echo " $f: $(printf '%s\n' "$new" | grep -c .) NEW finding(s)"
printf '%s\n' "$new" | sed 's/^/ /' | head -5
added=1; fail=1
}
done < <(git diff --name-only origin/master...HEAD -- "$ADDON" 2> /dev/null)
[ "$added" -eq 0 ] && echo " (none — this diff introduced no new lint findings)"
new=$(comm -13 <(printf '%s\n' "$b") <(printf '%s\n' "$a") | grep -c .)
[ "$new" -gt 0 ] && { echo " $f: $new NEW finding(s)"; comm -13 <(printf '%s\n' "$b") <(printf '%s\n' "$a") | sed 's/^/ /' | head -5; }
done
echo " (nothing listed above = your diff introduced no new lint findings)"
fi
echo

View File

@@ -228,7 +228,7 @@ jobs:
echo "... done"
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "GitHub bot : README updated"
default_author: github_actions

View File

@@ -125,7 +125,7 @@ jobs:
- name: Analyse and fix
if: steps.batch.outputs.count != '0'
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange. The scheduled path

View File

@@ -237,7 +237,7 @@ jobs:
# Get stars evolution
wget -S -O .github/starsevol.svg "https://api.star-history.com/svg?repos=alexbelgium/hassio-addons&type=Date" || true
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "GitHub bot : graphs updated"
default_author: github_actions

View File

@@ -64,7 +64,7 @@ jobs:
fetch-depth: 1
- name: Run Claude Code
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# AI_PR_TOKEN, not GITHUB_TOKEN, so a PR Claude opens triggers CI.

View File

@@ -135,7 +135,7 @@ jobs:
- name: Execute the plan
if: steps.bundle.outputs.has_plan == 'true'
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange, which 401s whenever

View File

@@ -59,7 +59,7 @@ jobs:
# Remove issues list
rm issueslist
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "Github bot : issues linked to readme"
default_author: github_actions

View File

@@ -166,7 +166,7 @@ jobs:
id: classify
if: github.event_name != 'issue_comment' || steps.claim.outputs.go == 'true'
continue-on-error: true
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Without this the action falls back to the OIDC -> Claude App token

View File

@@ -79,7 +79,7 @@ jobs:
- name: Address CodeRabbit comments
if: steps.claim.outputs.go == 'true'
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Skip the OIDC -> Claude App token exchange, which 401s whenever

View File

@@ -95,7 +95,7 @@ jobs:
- name: Commit sanitize changes
id: sanitize_commit
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
commit: -u
message: "GitHub bot: sanitize (spaces + LF endings) & chmod [nobuild]"
@@ -410,7 +410,7 @@ jobs:
done
- name: Commit changelog changes
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
commit: -u
message: "GitHub bot: changelog [nobuild]"

View File

@@ -18,7 +18,7 @@ jobs:
uses: erclu/check-crlf@v1
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "Github bot : CRLF corrected"
default_author: github_actions
@@ -50,7 +50,7 @@ jobs:
dos2unix -k "$f"
done
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "Github bot : CRLF corrected"
default_author: github_actions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Commit if needed
if: steps.calibre.outputs.markdown != ''
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
message: "Github bot : image compressed"
default_author: github_actions

View File

@@ -109,7 +109,7 @@ jobs:
#TOTAL3="$(awk '{SUM+=$2}END{print SUM}' Stats)"
- name: Commit if needed
uses: EndBug/add-and-commit@v11.1.1
uses: EndBug/add-and-commit@v11.0.0
with:
default_author: github_actions
message : "Github bot : stats updated"

View File

@@ -168,59 +168,6 @@ if [ -z "$shebang" ]; then
exit 1
fi
#####################################
# Seed the s6 container environment #
#####################################
# s6-overlay's stage 1 dumps the container's environment into /run/s6/container_environment, and
# `with-contenv` reads it back (emptyenv -p; s6-envdir). Add-ons that override the base image's
# ENTRYPOINT ["/init"] with ENTRYPOINT ["/usr/bin/env"] plus CMD ["/ha_entrypoint.sh"] never run
# stage 1, so nothing creates that directory and every #!/usr/bin/with-contenv script outside the
# three globs whose shebang is rewritten below either exits non-zero before its first line
# (directory missing: s6-envdir errors) or runs against whatever a cont-init script happened to
# leave there -- measured at 16 variables instead of 110, SUPERVISOR_TOKEN among the missing.
# Neither says anything, so all that surfaces is what the caller makes of it: a HEALTHCHECK
# reporting "unhealthy", a cron job doing nothing. So dump the environment here instead.
#
# Deliberately after the shebang probe above, not next to the other PID 1 setup: the probe's first
# candidate is "/command/with-contenv bashio" and it fails today in exactly these add-ons, so the
# probe falls through to "/usr/bin/env bashio". Seeding earlier would make that candidate start
# succeeding and flip the shebang of every cont-init and service script here, so scripts launched
# directly would lose what an earlier sourced script exported -- a far larger change than this.
#
# It does switch on two dormant writes: 00-global_var.sh and 01-config_yaml.sh push their values
# into the envdir, but only `if [ -d ]`. That is what those lines are for, and it means out-of-glob
# scripts now see the user's configured options too.
S6_CONTAINER_ENV="/run/s6/container_environment"
# Only when this script is PID 1 -- under /init it is the stage-2 hook and stage 1 has already
# written the directory -- and only where with-contenv exists to care.
if $PID1 && { [ -x /command/with-contenv ] || [ -x /usr/bin/with-contenv ]; }; then
# Filled in a sibling and renamed into place, never written to live. A half-populated envdir is
# worse than an absent one: s6-envdir accepts it, so a with-contenv script starts and runs
# against an environment quietly missing SUPERVISOR_TOKEN, where an absent one stops it at its
# shebang. rename(2) means a concurrent reader -- a HEALTHCHECK can run alongside PID 1 -- sees
# the directory either absent or complete, never mid-dump.
#
# Cleared rather than written over: /run is not a tmpfs here, so an image layer can persist
# entries, and writing name by name would merge into them and leave variables PID 1 does not
# have, a stale SUPERVISOR_TOKEN among them. A failed rm has to abort the chain, because mkdir -p
# accepts a surviving symlink-to-directory and would let the dump follow it. rm does not traverse
# a symlink, but it would empty anything bind-mounted at this exact path -- not a configuration
# any add-on uses, and not one s6 would tolerate either.
if rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" && mkdir -p "$S6_CONTAINER_ENV.tmp" &&
s6-dumpenv -- "$S6_CONTAINER_ENV.tmp" && mv "$S6_CONTAINER_ENV.tmp" "$S6_CONTAINER_ENV"; then
echo "Populated $S6_CONTAINER_ENV for with-contenv"
else
# Leaves the directory absent, which is exactly how this fails today -- so the failure mode is
# unchanged, not newly degraded. Never fatal, a read-only /run must still let the add-on boot,
# but never silent either, since the shebang failure it leaves behind says nothing on its own.
rm -rf "$S6_CONTAINER_ENV" "$S6_CONTAINER_ENV.tmp" 2>/dev/null || true
echo -e "\e[38;5;214m$(date) WARNING: could not populate $S6_CONTAINER_ENV; scripts with a with-contenv shebang will fail at their shebang, as they did before this was attempted\e[0m"
fi
fi
####################################
# Bashio library for source fallback
####################################

View File

@@ -427,7 +427,7 @@ If you want to do add the repository manually, please follow the procedure highl
![aarch64][aarch64-badge]
![amd64][amd64-badge]
&#10003; [Free Games Claimer](free_games_claimer/) : Claims free games from Epic, Prime, GOG, Steam, Ubisoft and more
&#10003; [Free Games Claimer](free_games_claimer/) : Automatically claims free games from Epic Games Store, Amazon Prime Gaming, GOG, Steam, and optional GamerPower-supported stores
&emsp;&emsp;![Version](https://img.shields.io/badge/dynamic/yaml?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Ffree_games_claimer%2Fconfig.yaml)
![Update](https://img.shields.io/badge/dynamic/json?label=Updated&query=%24.last_update&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Ffree_games_claimer%2Fupdater.json)

View File

@@ -1,28 +1,3 @@
## 2.1.0 (2026-08-24)
- Updated the pinned upstream from Free Games Claimer Remaster 1.1 to 1.6,
which adds the Ubisoft giveaway, Fab, AliExpress and Epic mobile stores,
fixed daily scheduler times, the `VNC_URL` notification link override, and a
fix for the `--accept-lang` flag that made every store's browser detectable
as automated.
- Mirrored upstream's Chromium hardening: `xdg-open` is neutralised and an
`AutoLaunchProtocolsFromOrigins` policy is installed, so app-scheme links
cannot open a blocking dialog in the VNC session.
- Disabled upstream's release-update notification by default. It tells the user
to run `docker compose pull`, while the add-on is updated through the Home
Assistant add-on store. Set `NOTIFY_UPDATES=true` in `config.env` to receive
it anyway.
- Reworded the add-on description so the store list reads as partial rather
than exhaustive; the full list is in the README.
- The default store selection is unchanged: existing installations keep
claiming from Epic, Prime Gaming and GOG until `STORES` is edited.
- Replaced the pinned upstream commit with `ARG BUILD_UPSTREAM`, which names an
upstream release tag, and re-enabled the repository updater for this add-on.
Upstream releases are now picked up automatically instead of requiring a
manual commit pin. Upstream's development tags are excluded: `lastversion`
reports the `v1.7d` development tag as release `1.7`, for which no source
archive exists, so an unfiltered update would have broken the build.
## 2.0.1 (2026-07-17)
- Aligned the pull-request build context with the production builder by copying

View File

@@ -17,7 +17,7 @@ ARG BUILD_REF
ARG BUILD_REPOSITORY
ARG BUILD_VERSION
ARG UPSTREAM_REPOSITORY="P-Adamiec/Free-Games-Claimer-Remaster"
ARG BUILD_UPSTREAM="1.6"
ARG UPSTREAM_REF="cca4992354215cef8807b748575af797606627f4"
ENV S6_CMD_WAIT_FOR_SERVICES="1" \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME="0" \
@@ -27,7 +27,10 @@ ENV S6_CMD_WAIT_FOR_SERVICES="1" \
WIDTH="1280" \
HEIGHT="720" \
DEPTH="24" \
SHOW="1"
SHOW="1" \
COMMIT="${UPSTREAM_REF}" \
BRANCH="main" \
NOW="${BUILD_DATE}"
# Install the dependencies used by Free Games Claimer Remaster. The upstream
# Dockerfile supports both amd64 (Google Chrome) and arm64 (Chromium), so the
@@ -84,11 +87,6 @@ RUN apt-get update \
else \
apt-get install -y --no-install-recommends chromium; \
fi \
&& printf '#!/bin/sh\nexit 0\n' > /usr/bin/xdg-open \
&& chmod +x /usr/bin/xdg-open \
&& mkdir -p /etc/opt/chrome/policies/managed /etc/chromium/policies/managed \
&& printf '%s\n' '{"AutoLaunchProtocolsFromOrigins":[{"protocol":"aliexpress","allowed_origins":["*"]},{"protocol":"aliexpresshd","allowed_origins":["*"]},{"protocol":"aecmd","allowed_origins":["*"]},{"protocol":"alibaba","allowed_origins":["*"]},{"protocol":"alipay","allowed_origins":["*"]},{"protocol":"alipays","allowed_origins":["*"]},{"protocol":"tmall","allowed_origins":["*"]},{"protocol":"taobao","allowed_origins":["*"]},{"protocol":"market","allowed_origins":["*"]},{"protocol":"intent","allowed_origins":["*"]}]}' \
| tee /etc/opt/chrome/policies/managed/fgc-autolaunch.json /etc/chromium/policies/managed/fgc-autolaunch.json > /dev/null \
&& ln -sf /usr/share/novnc/vnc_auto.html /usr/share/novnc/index.html \
&& ln -sf /usr/bin/python3 /usr/bin/python \
&& apt-get purge -y gnupg \
@@ -96,20 +94,13 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/cache/* /var/tmp/* /tmp/* /usr/share/doc/*
# Install the upstream release named by BUILD_UPSTREAM. The repository updater
# bumps that value together with the add-on version. A release tag is a mutable
# reference: rebuilding the same BUILD_UPSTREAM installs whatever the tag points
# at today, which is the accepted cost of tracking upstream automatically.
# Upstream tags its releases "v1.6", but the tag name is downloaded without the
# prefix as well so that an unattended update does not fail the build if
# upstream ever drops it.
# Install a deterministic snapshot of the replacement upstream. Updating the
# upstream reference is intentionally explicit so image contents cannot change
# without an add-on version bump.
WORKDIR /fgc
RUN (curl --proto "=https" --tlsv1.2 -fsSL \
"https://github.com/${UPSTREAM_REPOSITORY}/archive/v${BUILD_UPSTREAM}.tar.gz" \
RUN curl --proto "=https" --tlsv1.2 -fsSL \
"https://github.com/${UPSTREAM_REPOSITORY}/archive/${UPSTREAM_REF}.tar.gz" \
-o /tmp/free-games-claimer-remaster.tar.gz \
|| curl --proto "=https" --tlsv1.2 -fsSL \
"https://github.com/${UPSTREAM_REPOSITORY}/archive/${BUILD_UPSTREAM}.tar.gz" \
-o /tmp/free-games-claimer-remaster.tar.gz) \
&& tar -xzf /tmp/free-games-claimer-remaster.tar.gz --strip-components=1 -C /fgc \
&& python3 -m pip install --no-cache-dir --break-system-packages -r requirements.txt \
&& dos2unix ./*.sh \
@@ -164,4 +155,4 @@ LABEL \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${BUILD_REF}" \
org.opencontainers.image.version="${BUILD_VERSION}" \
io.hass.upstream="https://github.com/${UPSTREAM_REPOSITORY}/releases"
io.hass.upstream="https://github.com/${UPSTREAM_REPOSITORY}/tree/${UPSTREAM_REF}"

View File

@@ -24,19 +24,14 @@ This add-on is based on
[Free Games Claimer Remaster](https://github.com/P-Adamiec/Free-Games-Claimer-Remaster).
It can claim free games from:
- Epic Games Store, including its weekly free mobile game
- Fab, Epic's asset marketplace (`fab`)
- Epic Games Store
- Amazon Prime Gaming
- GOG
- Steam
- Ubisoft giveaways (`ubisoft`)
- AliExpress daily coin check-in (`aliexpress`)
- GamerPower-supported stores, when explicitly enabled
For compatibility with previous add-on releases, the default store selection
remains Epic Games, Prime Gaming, and GOG. The other stores are enabled by
adding them to `STORES`, for example `epic,prime,gog,fab,ubisoft`, and each
needs its own credentials in `config.env`.
remains Epic Games, Prime Gaming, and GOG.
## Web interface
@@ -103,11 +98,6 @@ NOTIFY=tgram://bot-token/chat-id
# DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
```
Upstream's release-update notification (`NOTIFY_UPDATES`) is disabled by the
add-on, because it advises running `docker compose pull` while the add-on is
actually updated through the Home Assistant add-on store. Setting
`NOTIFY_UPDATES=true` in `config.env` re-enables it.
Existing variables such as `EG_EMAIL`, `EG_PASSWORD`, `PG_EMAIL`,
`PG_PASSWORD`, `PG_OTPKEY`, `GOG_EMAIL`, `GOG_PASSWORD`, `SHOW`, `WIDTH`,
`HEIGHT`, `TIMEOUT`, `LOGIN_TIMEOUT`, `DRYRUN`, and `NOTIFY` remain compatible.
@@ -142,31 +132,16 @@ normally uses port `7080`.
## Upstream update policy
The image is built from the upstream release named by `ARG BUILD_UPSTREAM` in
the Dockerfile, downloaded as the matching `v<version>` source tarball. The
repository updater tracks upstream releases and bumps that value, the add-on
version and `CHANGELOG.md` together, so a new upstream release reaches the
add-on without a manual edit.
The image is built from an explicit upstream commit in the Dockerfile. This
keeps amd64 and aarch64 images reproducible and prevents an upstream branch or
container tag from changing without an add-on review and version bump.
A release tag is a mutable reference. Rebuilding the same `BUILD_UPSTREAM`
installs whatever that tag points at, so an upstream tag that is force-moved or
deleted would change or fail the build without an add-on change. That is the
accepted cost of automatic tracking, and it is the same trade-off every other
automatically updated add-on in this repository makes; the previous commit pin
was immutable but could only be advanced by hand.
Upstream's development tags (`v1.7d` and similar) are filtered out through
`"github_exclude": "d"` in `updater.json`. Without it the updater reports the
`v1.7d` tag as release `1.7`, for which GitHub serves no source archive, and
the build would fail.
The add-on version does not track the upstream version. The add-on uses a `2.x`
series while upstream is on `1.x`, and Home Assistant only offers an update
when the new version sorts strictly higher, so the updater increments the
add-on version (`2.1.0` to `2.1.1`) instead of publishing a lower-sorting
upstream number. The upstream release actually installed is recorded in
`upstream_version` in `updater.json`, in `CHANGELOG.md`, and in the add-on's
startup banner.
The repository updater is intentionally paused for this add-on because the
add-on uses its own `2.x` version series while the replacement upstream uses a
`1.x` version series. An automatic replacement would risk a Home Assistant
version regression and would not safely update the pinned commit. A maintainer
upstream update must therefore update `UPSTREAM_REF`, `upstream_version`, the
add-on version, and `CHANGELOG.md` together.
## Installation

View File

@@ -2,7 +2,7 @@
arch:
- aarch64
- amd64
description: "Claims free games from Epic, Prime, GOG, Steam, Ubisoft and more"
description: "Automatically claims free games from Epic Games Store, Amazon Prime Gaming, GOG, Steam, and optional GamerPower-supported stores"
devices:
- /dev/dri
- /dev/dri/card0
@@ -96,5 +96,5 @@ schema:
slug: free_games_claimer
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "2.1.0"
version: "2.0.1"
webui: "[PROTO:ssl]://[HOST]:[PORT:6080]"

View File

@@ -36,12 +36,6 @@ set -a
source "${RUNTIME_CONFIG}"
set +a
# Upstream checks GitHub for newer releases and then tells the user to run
# "docker compose pull", which is wrong here: the add-on is updated through the
# Home Assistant add-on store. Default the check off, but honour an explicit
# NOTIFY_UPDATES from config.env.
export NOTIFY_UPDATES="${NOTIFY_UPDATES:-false}"
# The Home Assistant port mapping is intentionally kept at 6080 for a seamless
# upgrade from the previous add-on, even though the new upstream defaults to 7080.
if [ -n "${NOVNC_PORT:-}" ] && [ "${NOVNC_PORT}" != "6080" ]; then

View File

@@ -10,17 +10,12 @@ TIMEOUT=60
LOGIN_TIMEOUT=180
VNC_LOGIN_TIMEOUT=180
NOVNC_PORT=6080
# Public address used in notification links when behind a reverse proxy.
# VNC_URL=https://fgc.example.tld
# Keep the previous add-on's default stores. The upstream also supports
# steam, fab, ubisoft, aliexpress and gamerpower; add them here if wanted.
# Keep the previous add-on's default stores. Add steam or gamerpower if wanted.
STORES=epic,prime,gog
# Used only when RUN_ONCE is disabled in the add-on options.
SCHEDULER_HOURS=12
# SCHEDULER_TIMEZONE=UTC
# SCHEDULER_FIXED_TIMES=17:00,21:30
# Common credentials can be used as fallbacks for all stores.
# EMAIL=
@@ -38,27 +33,7 @@ SCHEDULER_HOURS=12
# GOG_PASSWORD=
# STEAM_USERNAME=
# STEAM_PASSWORD=
# UBI_EMAIL=
# UBI_PASSWORD=
# UBI_OTPKEY=
# AE_EMAIL=
# AE_PASSWORD=
# Epic's weekly free mobile game, claimed with the epic store.
# EG_MOBILE=true
# EG_MOBILE_PLATFORMS=android,ios
# Fab assets sign in with the Epic account. Accepting the licence agreement is
# required to claim; set to false to be notified instead of accepting.
# FAB_ACCEPT_EULA=true
# Notifications (Apprise or Discord)
# NOTIFY=
# DISCORD_WEBHOOK=
# NOTIFY_ALREADY_CLAIMED=false
# NOTIFY_SKIP_STORES=
# Upstream release notifications are disabled by the add-on: updates are
# delivered through the Home Assistant add-on store, not by docker compose.
# Set to true to receive them anyway.
# NOTIFY_UPDATES=false

View File

@@ -1,13 +1,11 @@
{
"dockerhub_by_date": false,
"dockerhub_list_size": 2,
"github_exclude": "d",
"github_fulltag": false,
"last_update": "24-08-2026",
"paused": false,
"last_update": "17-07-2026",
"paused": true,
"repository": "alexbelgium/hassio-addons",
"slug": "free_games_claimer",
"source": "github",
"upstream_repo": "P-Adamiec/Free-Games-Claimer-Remaster",
"upstream_version": "1.6"
"upstream_version": "1.1"
}

View File

@@ -1,7 +1,3 @@
## 2.44.0.1 (2026-08-24)
- Rebuild to pick up a fix in the shared entrypoint: when `ha_entrypoint.sh` runs as PID 1, as it does in this add-on, it now populates `/run/s6/container_environment`, so a script carrying a `#!/usr/bin/with-contenv` shebang gets a populated environment including `SUPERVISOR_TOKEN` instead of failing at its shebang. Nothing shipped in this add-on still uses that shebang (the healthcheck moved to plain bash in 2.44.0), so this changes nothing about the agent itself; it matters for a `script.sh` added by the user, and it is the add-on that build-tests the shared change
## 2.44.0 (2026-08-23)
- Fix: Use `portainer/agent:alpine-sts` to match the STS release channel configured in `updater.json`

View File

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

View File

@@ -1,8 +1,4 @@
## 5.1.1.2 (2026-08-25)
- Ingress is now enabled: the WebUI opens directly in the Home Assistant sidebar, and the "Open Web UI" button now goes there. Access by ip:port is unchanged, but has to be typed rather than clicked, as Home Assistant does not allow an add-on to offer both.
- Note for users who set a "Host verification" whitelist in SABnzbd: ingress sends `Host: 127.0.0.1:8080` upstream, because SABnzbd rejects any Host that is not an IP literal. That whitelist therefore no longer filters the ingress route, which is gated by Home Assistant authentication instead. Direct ip:port access is unchanged and still filtered.
## 5.1.1 (2026-08-22)
- Update to latest version from linuxserver/docker-sabnzbd (changelog : https://github.com/linuxserver/docker-sabnzbd/releases)

View File

@@ -70,7 +70,7 @@ environment:
PGID: "0"
PUID: "0"
image: ghcr.io/alexbelgium/sabnzbd-{arch}
ingress: true
ingress_entry: sabnzbd
init: false
map:
- addon_config:rw
@@ -106,4 +106,5 @@ schema:
slug: sabnzbd
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "5.1.1.2"
version: "5.1.1"
webui: http://[HOST]:[PORT:8080]

View File

@@ -1,14 +1,21 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# shellcheck disable=SC2317
set -e
#################
# NGINX SETTING #
#################
exit 0
ingress_port=$(bashio::addon.ingress_port)
ingress_interface=$(bashio::addon.ip_address)
ingress_entry=$(bashio::addon.ingress_entry)
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ingress.conf
# Allows serving js
sed -i 's/<!-- %if-not-debug% -->/<!-- %if-not-debug% /g' /app/sabnzbd/webui/index.html
sed -i 's/<!-- %end% -->/ %end% -->/g' /app/sabnzbd/webui/index.html
sed -i 's/<!-- %if-debug%/<!-- %if-debug% -->/g' /app/sabnzbd/webui/index.html
sed -i 's/ %end% -->/<!-- %end% -->/g' /app/sabnzbd/webui/index.html

View File

@@ -2,34 +2,22 @@ server {
listen %%interface%%:%%port%% default_server;
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
client_max_body_size 0;
location / {
proxy_pass http://127.0.0.1:8080;
location / {
add_header Access-Control-Allow-Origin *;
proxy_connect_timeout 30m;
proxy_send_timeout 30m;
proxy_read_timeout 30m;
proxy_pass http://127.0.0.1:8080;
# SABnzbd refuses any request whose Host is not an IP literal
# ("Access denied - Hostname verification failed"), so send the
# upstream socket rather than the browser's host. X-Forwarded-For is
# the only other header it reads (for its verify_xff_header option).
proxy_set_header Host $proxy_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
# Correct url without port when using https
sub_filter_once off;
sub_filter_types *;
sub_filter /sabnzbd %%ingress_entry%%/sabnzbd;
}
# The interface itself only emits relative links, so no body
# rewriting is needed. Redirects are the exception: Raiser() emits
# a path under url_base, so prefix them with the ingress entry.
# absolute_redirect must stay off, or nginx expands the rewritten
# Location into http://<browser host>:<ingress port>/..., a port the
# browser cannot reach.
proxy_redirect / %%ingress_entry%%/;
absolute_redirect off;
# The login cookie is hardcoded to Path=/, which on the ingress
# origin would send it to every other add-on's ingress path too.
proxy_cookie_path / %%ingress_entry%%/;
proxy_http_version 1.1;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}

View File

@@ -1,9 +0,0 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Stop the container when Nginx fails, so ingress does not silently go dead
# ==============================================================================
if [[ "$1" -ne 0 && "$1" -ne 256 ]]; then
bashio::log.error "Nginx exited with code $1"
kill -15 1
fi

View File

@@ -1,11 +0,0 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
# ==============================================================================
# Wait for sabnzbd to become available
bashio::net.wait_for 8080 localhost 900
bashio::log.info "Starting NGinx..."
exec nginx