mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-09-15 14:19:09 +02:00
Compare commits
10 Commits
docs/skill
...
docs/skill
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
833d0d3758 | ||
|
|
138e7d7c33 | ||
|
|
8341e542fb | ||
|
|
a68ef0dde2 | ||
|
|
23fad482b8 | ||
|
|
5a87daf3e4 | ||
|
|
f12cfa9b78 | ||
|
|
afbab942a6 | ||
|
|
0cb360d04a | ||
|
|
7cd844ac9f |
@@ -116,9 +116,10 @@ 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.
|
||||
- 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.
|
||||
|
||||
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
|
||||
@@ -158,13 +159,19 @@ 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; match the CHANGELOG heading format `## X.Y (DD-MM-YYYY)`.
|
||||
`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`.
|
||||
|
||||
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.
|
||||
@@ -185,7 +192,12 @@ 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. Real "merged and inert" examples, and what
|
||||
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
|
||||
to do when a fix can't be self-verified: `references/evidence.md`.
|
||||
|
||||
## 10. Calibrate and report
|
||||
@@ -204,5 +216,9 @@ 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.
|
||||
|
||||
@@ -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 all three cases the simpler option existed and was skipped.
|
||||
that survives being said out loud. In each case 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,6 +13,13 @@ 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
|
||||
@@ -35,10 +42,16 @@ 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. 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.
|
||||
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.
|
||||
- **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.
|
||||
|
||||
@@ -11,6 +11,7 @@ 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)
|
||||
@@ -116,6 +117,26 @@ 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
|
||||
@@ -195,6 +216,17 @@ 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.
|
||||
@@ -209,6 +241,18 @@ 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.
|
||||
|
||||
@@ -90,6 +90,7 @@ 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
|
||||
@@ -102,11 +103,14 @@ watch)
|
||||
case "$c" in
|
||||
*pending*) sleep 60 ;;
|
||||
*fail* | *error* | *cancel*) echo "settled — with FAILURES (see above)"; wfail=1; break ;;
|
||||
*) echo "settled — all passing"; break ;;
|
||||
*) echo "settled — all passing"; wfail=0; 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:-0}"
|
||||
exit "$wfail"
|
||||
;;
|
||||
*) echo "unknown: $CMD"; exit 1 ;;
|
||||
esac
|
||||
|
||||
@@ -64,7 +64,8 @@ if [ -n "${BUILD_VERSION:-}" ]; then
|
||||
echo " $SLUG/config.yaml version = $here"
|
||||
echo " running image BUILD_VERSION = $BUILD_VERSION"
|
||||
if [ "$here" = "$BUILD_VERSION" ]; then
|
||||
echo " MATCH — this checkout corresponds to the running image."
|
||||
# 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."
|
||||
else
|
||||
echo " MISMATCH — this branch is NOT what is running."
|
||||
git fetch origin master --quiet 2> /dev/null
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
# rather than implying the build was checked.
|
||||
#
|
||||
# --vs-master re-lints each changed file at origin/master and prints only findings your diff
|
||||
# ADDED. Without it you will chase warnings that were already in the file.
|
||||
# ADDED, and fails if there are any. Without it you will chase warnings that were already there.
|
||||
#
|
||||
# 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; }
|
||||
@@ -31,17 +34,21 @@ 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).
|
||||
while IFS= read -r f; do
|
||||
[ -f "$f" ] || continue
|
||||
# Shell: bash -n then shellcheck -x (follows sourced files, as CI does). One list for both.
|
||||
files=()
|
||||
while IFS= read -r f; do is_execline "$f" || files+=("$f"); done \
|
||||
< <(find "$ADDON" -type f \( -name '*.sh' -o -name 'run' -o -name 'finish' -o -name 'autostart' \) 2> /dev/null)
|
||||
for f in "${files[@]}"; do
|
||||
if ! out=$(bash -n "$f" 2>&1); then note "bash -n" "FAIL $f"; echo "$out" | sed 's/^/ /'; fail=1; fi
|
||||
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"
|
||||
done
|
||||
[ "$fail" -eq 0 ] && note "bash -n" "${#files[@]} file(s) checked"
|
||||
|
||||
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 [ "${#files[@]}" -gt 0 ] && command -v shellcheck > /dev/null 2>&1; then
|
||||
sc=$(shellcheck -x -f gcc "${files[@]}" 2>&1)
|
||||
if [ -n "$sc" ]; then
|
||||
note "shellcheck" "$(printf '%s\n' "$sc" | grep -c .) finding(s)"
|
||||
printf '%s\n' "$sc" | sed 's/^/ /' | head -20
|
||||
@@ -50,7 +57,10 @@ fi
|
||||
|
||||
command -v hadolint > /dev/null 2>&1 && [ -f "$ADDON/Dockerfile" ] && {
|
||||
hl=$(hadolint "$ADDON/Dockerfile" 2>&1)
|
||||
[ -n "$hl" ] && { note "hadolint" "$(printf '%s\n' "$hl" | grep -c .) finding(s)"; printf '%s\n' "$hl" | sed 's/^/ /' | head -10; } || note "hadolint" "clean"
|
||||
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
|
||||
}
|
||||
|
||||
if [ -f "$ADDON/config.yaml" ]; then
|
||||
@@ -79,11 +89,14 @@ $VS_MASTER && command -v npx > /dev/null 2>&1 && [ -f "$ADDON/CHANGELOG.md" ] &&
|
||||
|
||||
echo
|
||||
echo "== CI requirements =="
|
||||
if git diff --name-only origin/master...HEAD 2> /dev/null | grep -q "$ADDON/CHANGELOG.md"; then
|
||||
# -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
|
||||
note "CHANGELOG" "updated"
|
||||
else
|
||||
# This one IS gated: onpr_check-pr.yaml exits 1 without it.
|
||||
note "CHANGELOG" "NOT UPDATED — this is the one CI hard-gate"; fail=1
|
||||
note "CHANGELOG" "NOT UPDATED for $ADDON — CI hard-gates this"; fail=1
|
||||
fi
|
||||
if git diff origin/master...HEAD -- "$ADDON/config.yaml" 2> /dev/null | grep -q '^+version:'; then
|
||||
note "version" "bumped"
|
||||
@@ -98,8 +111,15 @@ if $VS_MASTER; then
|
||||
echo
|
||||
echo "== findings ADDED by this diff (pre-existing ones filtered out) =="
|
||||
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
|
||||
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
|
||||
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"
|
||||
# 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.
|
||||
@@ -115,12 +135,16 @@ if $VS_MASTER; then
|
||||
cmd() { hadolint "$1" 2>&1 | sed 's/^[^:]*//; s/^:[0-9]*//'; } ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
cp "$tmp/base" "$tmp/base_f"; b=$(cmd "$tmp/base_f" | sort)
|
||||
b=$(cmd "$tmp/base" | sort)
|
||||
a=$(cmd "$f" | sort)
|
||||
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)"
|
||||
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)"
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
2
.github/workflows/daily_README.yaml
vendored
2
.github/workflows/daily_README.yaml
vendored
@@ -228,7 +228,7 @@ jobs:
|
||||
echo "... done"
|
||||
|
||||
- name: Commit if needed
|
||||
uses: EndBug/add-and-commit@v11.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
message: "GitHub bot : README updated"
|
||||
default_author: github_actions
|
||||
|
||||
2
.github/workflows/daily_ai_fix.yaml
vendored
2
.github/workflows/daily_ai_fix.yaml
vendored
@@ -125,7 +125,7 @@ jobs:
|
||||
|
||||
- name: Analyse and fix
|
||||
if: steps.batch.outputs.count != '0'
|
||||
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
|
||||
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# Skip the OIDC -> Claude App token exchange. The scheduled path
|
||||
|
||||
2
.github/workflows/helper_stats_graphs.yaml
vendored
2
.github/workflows/helper_stats_graphs.yaml
vendored
@@ -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.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
message: "GitHub bot : graphs updated"
|
||||
default_author: github_actions
|
||||
|
||||
2
.github/workflows/on_claude_mention.yml
vendored
2
.github/workflows/on_claude_mention.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
|
||||
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# AI_PR_TOKEN, not GITHUB_TOKEN, so a PR Claude opens triggers CI.
|
||||
|
||||
2
.github/workflows/on_issue_approved.yaml
vendored
2
.github/workflows/on_issue_approved.yaml
vendored
@@ -135,7 +135,7 @@ jobs:
|
||||
|
||||
- name: Execute the plan
|
||||
if: steps.bundle.outputs.has_plan == 'true'
|
||||
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
|
||||
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# Skip the OIDC -> Claude App token exchange, which 401s whenever
|
||||
|
||||
2
.github/workflows/on_issues.yml
vendored
2
.github/workflows/on_issues.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
# Remove issues list
|
||||
rm issueslist
|
||||
- name: Commit if needed
|
||||
uses: EndBug/add-and-commit@v11.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
message: "Github bot : issues linked to readme"
|
||||
default_author: github_actions
|
||||
|
||||
2
.github/workflows/on_issues_ai_triage.yaml
vendored
2
.github/workflows/on_issues_ai_triage.yaml
vendored
@@ -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@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
|
||||
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# Without this the action falls back to the OIDC -> Claude App token
|
||||
|
||||
2
.github/workflows/on_pr_coderabbit.yml
vendored
2
.github/workflows/on_pr_coderabbit.yml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
- name: Address CodeRabbit comments
|
||||
if: steps.claim.outputs.go == 'true'
|
||||
uses: anthropics/claude-code-action@9d7150bc8a3dae8149739a88019d192b579ad90c # v1
|
||||
uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# Skip the OIDC -> Claude App token exchange, which 401s whenever
|
||||
|
||||
4
.github/workflows/onpush_builder.yaml
vendored
4
.github/workflows/onpush_builder.yaml
vendored
@@ -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.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
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.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
commit: -u
|
||||
message: "GitHub bot: changelog [nobuild]"
|
||||
|
||||
4
.github/workflows/weekly_crlftolf.yaml
vendored
4
.github/workflows/weekly_crlftolf.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: erclu/check-crlf@v1
|
||||
|
||||
- name: Commit if needed
|
||||
uses: EndBug/add-and-commit@v11.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
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.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
message: "Github bot : CRLF corrected"
|
||||
default_author: github_actions
|
||||
|
||||
2
.github/workflows/weekly_reduceimagesize.yml
vendored
2
.github/workflows/weekly_reduceimagesize.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
- name: Commit if needed
|
||||
if: steps.calibre.outputs.markdown != ''
|
||||
uses: EndBug/add-and-commit@v11.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
message: "Github bot : image compressed"
|
||||
default_author: github_actions
|
||||
|
||||
2
.github/workflows/weekly_stats.yaml
vendored
2
.github/workflows/weekly_stats.yaml
vendored
@@ -109,7 +109,7 @@ jobs:
|
||||
#TOTAL3="$(awk '{SUM+=$2}END{print SUM}' Stats)"
|
||||
|
||||
- name: Commit if needed
|
||||
uses: EndBug/add-and-commit@v11.0.0
|
||||
uses: EndBug/add-and-commit@v11.1.1
|
||||
with:
|
||||
default_author: github_actions
|
||||
message : "Github bot : stats updated"
|
||||
|
||||
@@ -168,6 +168,59 @@ 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
|
||||
####################################
|
||||
|
||||
@@ -427,7 +427,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![aarch64][aarch64-badge]
|
||||
![amd64][amd64-badge]
|
||||
|
||||
✓ [Free Games Claimer](free_games_claimer/) : Automatically claims free games from Epic Games Store, Amazon Prime Gaming, GOG, Steam, and optional GamerPower-supported stores
|
||||
✓ [Free Games Claimer](free_games_claimer/) : Claims free games from Epic, Prime, GOG, Steam, Ubisoft and more
|
||||
|
||||
  
|
||||

|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
## 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
|
||||
|
||||
@@ -17,7 +17,7 @@ ARG BUILD_REF
|
||||
ARG BUILD_REPOSITORY
|
||||
ARG BUILD_VERSION
|
||||
ARG UPSTREAM_REPOSITORY="P-Adamiec/Free-Games-Claimer-Remaster"
|
||||
ARG UPSTREAM_REF="cca4992354215cef8807b748575af797606627f4"
|
||||
ARG BUILD_UPSTREAM="1.6"
|
||||
|
||||
ENV S6_CMD_WAIT_FOR_SERVICES="1" \
|
||||
S6_CMD_WAIT_FOR_SERVICES_MAXTIME="0" \
|
||||
@@ -27,10 +27,7 @@ ENV S6_CMD_WAIT_FOR_SERVICES="1" \
|
||||
WIDTH="1280" \
|
||||
HEIGHT="720" \
|
||||
DEPTH="24" \
|
||||
SHOW="1" \
|
||||
COMMIT="${UPSTREAM_REF}" \
|
||||
BRANCH="main" \
|
||||
NOW="${BUILD_DATE}"
|
||||
SHOW="1"
|
||||
|
||||
# Install the dependencies used by Free Games Claimer Remaster. The upstream
|
||||
# Dockerfile supports both amd64 (Google Chrome) and arm64 (Chromium), so the
|
||||
@@ -87,6 +84,11 @@ 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 \
|
||||
@@ -94,13 +96,20 @@ RUN apt-get update \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/* /var/tmp/* /tmp/* /usr/share/doc/*
|
||||
|
||||
# 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.
|
||||
# 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.
|
||||
WORKDIR /fgc
|
||||
RUN curl --proto "=https" --tlsv1.2 -fsSL \
|
||||
"https://github.com/${UPSTREAM_REPOSITORY}/archive/${UPSTREAM_REF}.tar.gz" \
|
||||
RUN (curl --proto "=https" --tlsv1.2 -fsSL \
|
||||
"https://github.com/${UPSTREAM_REPOSITORY}/archive/v${BUILD_UPSTREAM}.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 \
|
||||
@@ -155,4 +164,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}/tree/${UPSTREAM_REF}"
|
||||
io.hass.upstream="https://github.com/${UPSTREAM_REPOSITORY}/releases"
|
||||
|
||||
@@ -24,14 +24,19 @@ 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
|
||||
- Epic Games Store, including its weekly free mobile game
|
||||
- Fab, Epic's asset marketplace (`fab`)
|
||||
- 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.
|
||||
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`.
|
||||
|
||||
## Web interface
|
||||
|
||||
@@ -98,6 +103,11 @@ 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.
|
||||
@@ -132,16 +142,31 @@ normally uses port `7080`.
|
||||
|
||||
## Upstream update policy
|
||||
|
||||
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.
|
||||
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 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.
|
||||
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.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
arch:
|
||||
- aarch64
|
||||
- amd64
|
||||
description: "Automatically claims free games from Epic Games Store, Amazon Prime Gaming, GOG, Steam, and optional GamerPower-supported stores"
|
||||
description: "Claims free games from Epic, Prime, GOG, Steam, Ubisoft and more"
|
||||
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.0.1"
|
||||
version: "2.1.0"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:6080]"
|
||||
|
||||
@@ -36,6 +36,12 @@ 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
|
||||
|
||||
@@ -10,12 +10,17 @@ 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. Add steam or gamerpower if wanted.
|
||||
# Keep the previous add-on's default stores. The upstream also supports
|
||||
# steam, fab, ubisoft, aliexpress and gamerpower; add them here 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=
|
||||
@@ -33,7 +38,27 @@ 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
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"dockerhub_by_date": false,
|
||||
"dockerhub_list_size": 2,
|
||||
"last_update": "17-07-2026",
|
||||
"paused": true,
|
||||
"github_exclude": "d",
|
||||
"github_fulltag": false,
|
||||
"last_update": "24-08-2026",
|
||||
"paused": false,
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "free_games_claimer",
|
||||
"source": "github",
|
||||
"upstream_repo": "P-Adamiec/Free-Games-Claimer-Remaster",
|
||||
"upstream_version": "1.1"
|
||||
"upstream_version": "1.6"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 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`
|
||||
|
||||
@@ -41,4 +41,4 @@ schema:
|
||||
slug: portainer_agent
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "2.44.0"
|
||||
version: "2.44.0.1"
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ environment:
|
||||
PGID: "0"
|
||||
PUID: "0"
|
||||
image: ghcr.io/alexbelgium/sabnzbd-{arch}
|
||||
ingress_entry: sabnzbd
|
||||
ingress: true
|
||||
init: false
|
||||
map:
|
||||
- addon_config:rw
|
||||
@@ -106,5 +106,4 @@ schema:
|
||||
slug: sabnzbd
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "5.1.1"
|
||||
webui: http://[HOST]:[PORT:8080]
|
||||
version: "5.1.1.2"
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
#!/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
|
||||
|
||||
# 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
|
||||
sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ingress.conf
|
||||
|
||||
@@ -2,22 +2,34 @@ 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 / {
|
||||
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;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
|
||||
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;
|
||||
}
|
||||
# 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;
|
||||
|
||||
# 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;
|
||||
}
|
||||
}
|
||||
|
||||
9
sabnzbd/rootfs/etc/services.d/nginx/finish
Executable file
9
sabnzbd/rootfs/etc/services.d/nginx/finish
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/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
|
||||
11
sabnzbd/rootfs/etc/services.d/nginx/run
Executable file
11
sabnzbd/rootfs/etc/services.d/nginx/run
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user