mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-10-15 12:39:23 +02:00
Compare commits
112 Commits
486d25b655
...
agent/clau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2afef8cf05 | ||
|
|
b59beb2aad | ||
|
|
4f5d0fa6b6 | ||
|
|
2497729f14 | ||
|
|
57aac487c5 | ||
|
|
0664e00ad1 | ||
|
|
307e2687f2 | ||
|
|
c7583ac4ce | ||
|
|
519fc02b60 | ||
|
|
4084a57b6f | ||
|
|
504d86fa50 | ||
|
|
0ce79bf3ec | ||
|
|
e464608155 | ||
|
|
800e4a9d89 | ||
|
|
d9e6213c1a | ||
|
|
17ba56100a | ||
|
|
b01196b768 | ||
|
|
41f01bf1c5 | ||
|
|
81c93687c3 | ||
|
|
cd2b6ec707 | ||
|
|
4101033a09 | ||
|
|
4a1904852a | ||
|
|
efe3512970 | ||
|
|
b1b472782c | ||
|
|
31c0b32c6c | ||
|
|
76a0ac6253 | ||
|
|
2b230c4aa1 | ||
|
|
910662db46 | ||
|
|
8cbcba64a6 | ||
|
|
c100e8bfe8 | ||
|
|
193ff5cca4 | ||
|
|
0050e67bca | ||
|
|
0f742d23d0 | ||
|
|
22151348a6 | ||
|
|
1920ecbb54 | ||
|
|
2606fd65dc | ||
|
|
b677f35864 | ||
|
|
4b7943fb84 | ||
|
|
548258cb86 | ||
|
|
5a2b6fec25 | ||
|
|
9d50d04910 | ||
|
|
d17c5c0a34 | ||
|
|
74121509cd | ||
|
|
a989e7fbdc | ||
|
|
857d4d2984 | ||
|
|
c042e2cb5c | ||
|
|
23017d1b28 | ||
|
|
ab3cc64bb4 | ||
|
|
5dc1a5179b | ||
|
|
287fb820b7 | ||
|
|
8350db333a | ||
|
|
3f6b174218 | ||
|
|
18e25f5b70 | ||
|
|
7a7c1e2d63 | ||
|
|
b2eee03e9c | ||
|
|
5fe3d6e1d8 | ||
|
|
c9c5d694a6 | ||
|
|
47604a3ede | ||
|
|
5215bc7b7b | ||
|
|
2ecb7146ff | ||
|
|
9e4cc10589 | ||
|
|
ed00161f2c | ||
|
|
98cb041866 | ||
|
|
63533b4122 | ||
|
|
dd3e1f373f | ||
|
|
404799a215 | ||
|
|
a57e0e511a | ||
|
|
d273eb47dc | ||
|
|
b3c4e46dd7 | ||
|
|
759ecfebd2 | ||
|
|
5c784fff45 | ||
|
|
25d8e3767d | ||
|
|
6cd2da1fad | ||
|
|
4419a12380 | ||
|
|
e37e3b3e2d | ||
|
|
9f592e58e4 | ||
|
|
28cb4c0ffb | ||
|
|
1ca2bc8e52 | ||
|
|
0e1f27bbfa | ||
|
|
3a1baaabfa | ||
|
|
976603c2a6 | ||
|
|
9d003935c0 | ||
|
|
d7c9567e6c | ||
|
|
78cf88e436 | ||
|
|
b32e38109b | ||
|
|
b1b5782247 | ||
|
|
b5e440b579 | ||
|
|
1b041afe37 | ||
|
|
805687ad2c | ||
|
|
c7ca9b92df | ||
|
|
e48702b30a | ||
|
|
d9e62d7d5e | ||
|
|
940a75f17d | ||
|
|
d858f6c09b | ||
|
|
4dc694e4a0 | ||
|
|
ce58551af8 | ||
|
|
af51a486c5 | ||
|
|
fb6a85bf09 | ||
|
|
d5c20b72d7 | ||
|
|
72f40bb069 | ||
|
|
59b700e713 | ||
|
|
c87443054e | ||
|
|
185e5d6016 | ||
|
|
fedb38d333 | ||
|
|
31c8ef9a94 | ||
|
|
30b2e4eb3d | ||
|
|
f28ceb5cd4 | ||
|
|
a4273620ec | ||
|
|
0b8ce41fce | ||
|
|
3933291a53 | ||
|
|
7c356e7305 | ||
|
|
e54e4656e8 |
16
.github/stargazer_countries.csv
vendored
16
.github/stargazer_countries.csv
vendored
@@ -73,6 +73,7 @@ Antianti2016,
|
||||
Antjrobles,
|
||||
Arbel-arad,Israel
|
||||
Areppa,
|
||||
ArjunZe,India
|
||||
Arrgh106,Germany
|
||||
Art-Ev,France
|
||||
Asahi21,
|
||||
@@ -106,6 +107,7 @@ Bigdaddy1990,Germany
|
||||
BilboBagCoder,
|
||||
Bill-Dung,Germany
|
||||
Blueyzachary,
|
||||
Bo1jo,
|
||||
BobMcCloy,
|
||||
Bobdugrand8,France
|
||||
Boilerplate4u,
|
||||
@@ -391,7 +393,7 @@ Kderyck,
|
||||
KecskeTech,
|
||||
Keiran90,
|
||||
KellsonM,
|
||||
KenKilty,
|
||||
KenKilty,United States
|
||||
KennyStier,United States
|
||||
Kensingtonn,
|
||||
KerstenAlheit,
|
||||
@@ -429,6 +431,7 @@ Lemonade455,
|
||||
LeoLTM,
|
||||
Leoin1,
|
||||
Leolion2023,
|
||||
Leonkatze1,
|
||||
LifeofDan-EL,Nigeria
|
||||
Likqez,Germany
|
||||
Lizzardis,
|
||||
@@ -541,6 +544,7 @@ Omni-Engineering,
|
||||
OneByJorah,
|
||||
OnkelKiwi,
|
||||
Oraind,
|
||||
Orbernator,Australia
|
||||
OsipovOV,
|
||||
Ozzoman30,
|
||||
P4uLT,France
|
||||
@@ -597,6 +601,7 @@ RandomUsernameThatIsNotYetTaken,Belgium
|
||||
RaneyDazed,United States
|
||||
Rapozillha,Portugal
|
||||
Raw2x2,
|
||||
Raynstorm96,
|
||||
Rchtr,
|
||||
RdN-J,
|
||||
RealJustMe,
|
||||
@@ -1373,6 +1378,7 @@ francoarendholz,Germany
|
||||
frankkoornstra,Netherlands
|
||||
frankythedog,
|
||||
fransixles,Canada
|
||||
fredct15,
|
||||
fredleger,France
|
||||
freechw,
|
||||
frizzi42,
|
||||
@@ -1426,6 +1432,7 @@ git-mdumont,
|
||||
git-rafael,Brazil
|
||||
gitapisup,
|
||||
gitmotion,
|
||||
gkrenn,Austria
|
||||
glgghit,Italy
|
||||
gli-james-roland,
|
||||
glxzander,
|
||||
@@ -1673,6 +1680,7 @@ katta07,
|
||||
kayserlein,Germany
|
||||
kcbergmo,
|
||||
kcm117,United States
|
||||
keatonp-inahoodie,
|
||||
keefey,Australia
|
||||
keikoro,
|
||||
keithcroshaw,
|
||||
@@ -1815,6 +1823,7 @@ mapeje,Sweden
|
||||
maphouse,Canada
|
||||
maratbakirov,
|
||||
marcetad,
|
||||
marcjay,United Kingdom
|
||||
marcoboers,Netherlands
|
||||
marcodangelo,Italy
|
||||
marcschraepler,Austria
|
||||
@@ -1855,6 +1864,7 @@ mbinks,
|
||||
mcalpine87,
|
||||
mccollege-au,Australia
|
||||
mcflexus,Netherlands
|
||||
mchaplyak,United States
|
||||
mclala22,
|
||||
mcmastermind,United States
|
||||
md-ray,Indonesia
|
||||
@@ -1906,6 +1916,7 @@ minscof,
|
||||
mirsalimov,
|
||||
miruoy,
|
||||
misterdev,Italy
|
||||
misterkeats,
|
||||
miumida,
|
||||
mjendza,Poland
|
||||
mking47,
|
||||
@@ -1957,6 +1968,7 @@ naor1,Israel
|
||||
natbrood,
|
||||
nate-marshall,
|
||||
nduest,Germany
|
||||
ne7runner,
|
||||
nedsined,
|
||||
neildotwilliams,
|
||||
neilpercy,
|
||||
@@ -2511,8 +2523,10 @@ vespexx,
|
||||
vetcher,
|
||||
vikavorkin,Israel
|
||||
vikt-v,Israel
|
||||
vincegre,Switzerland
|
||||
vinnyduke,
|
||||
vlad36N,United States
|
||||
vlanwalker,Poland
|
||||
vlassen,
|
||||
vortexua,
|
||||
vova-musin,
|
||||
|
||||
|
BIN
.github/stargazer_map.png
vendored
BIN
.github/stargazer_map.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 331 KiB |
@@ -65,7 +65,7 @@ for files in "/etc/cont-init.d" "/etc/services.d"; do
|
||||
if grep -q -rnw "$files/" -e "$COMMAND"; then
|
||||
[ "$VERBOSE" = true ] && echo "$COMMAND required"
|
||||
[ "$PACKMANAGER" = "apk" ] && PACKAGES="$PACKAGES exfatprogs ntfs-3g ntfs-3g-progs squashfs-tools fuse lsblk"
|
||||
[ "$PACKMANAGER" = "apt" ] && PACKAGES="$PACKAGES exfat* ntfs* squashfs-tools util-linux"
|
||||
[ "$PACKMANAGER" = "apt" ] && PACKAGES="$PACKAGES exfat* ntfs-3g squashfs-tools util-linux"
|
||||
#[ "$PACKMANAGER" = "pacman" ] && PACKAGES="$PACKAGES ntfs-3g"
|
||||
fi
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -57,18 +57,18 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
### Number of addons
|
||||
|
||||
- In the repository : 137
|
||||
- Installed : 591099
|
||||
- Installed : 612843
|
||||
|
||||
### Top 3
|
||||
|
||||
1. Arpspoof (102260x)
|
||||
2. Sponsorblockcast (98515x)
|
||||
3. Jellyfin (88478x)
|
||||
1. Arpspoof (102274x)
|
||||
2. Sponsorblockcast (98951x)
|
||||
3. Jellyfin (88956x)
|
||||
|
||||
### Architectures used
|
||||
|
||||
- amd64: 90%
|
||||
- aarch64: 10%
|
||||
- amd64: 89%
|
||||
- aarch64: 11%
|
||||
|
||||
### Stars evolution
|
||||
|
||||
@@ -77,7 +77,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
## Add-ons provided by this repository
|
||||
|
||||
%%ADDONS_LIST%%
|
||||
✓ [Arpspoof (102260x)](arpspoof/) : block internet connection for local network devices
|
||||
✓ [Arpspoof (102274x)](arpspoof/) : block internet connection for local network devices
|
||||
|
||||
  
|
||||

|
||||
@@ -522,7 +522,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![smb][smb-badge]
|
||||
![localdisks][localdisks-badge]
|
||||
|
||||
✓  [Jellyfin (88478x) NAS](jellyfin/) : A free Software Media System that puts you in control of managing and streaming your media
|
||||
✓  [Jellyfin (88956x) NAS](jellyfin/) : A free Software Media System that puts you in control of managing and streaming your media
|
||||
|
||||
  
|
||||

|
||||
@@ -585,7 +585,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![aarch64][aarch64-badge]
|
||||
![amd64][amd64-badge]
|
||||
|
||||
✓  [Maintainerr](maintainerr/) : Rule-based media cleanup tool for Plex, Jellyfin (88478x) and Emby. Creates collections and optionally deletes unwatched content.
|
||||
✓  [Maintainerr](maintainerr/) : Rule-based media cleanup tool for Plex, Jellyfin (88956x) and Emby. Creates collections and optionally deletes unwatched content.
|
||||
|
||||
  
|
||||

|
||||
@@ -907,7 +907,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![smb][smb-badge]
|
||||
![localdisks][localdisks-badge]
|
||||
|
||||
✓  [Seerr](seerr/) : Open-source media request and discovery manager for Jellyfin (88478x), Plex, and Emby
|
||||
✓  [Seerr](seerr/) : Open-source media request and discovery manager for Jellyfin (88956x), Plex, and Emby
|
||||
|
||||
  
|
||||

|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 2026.08 (2026-08-01)
|
||||
|
||||
- Addon versions written in config.yaml now always comply with Home Assistant versioning: an upstream tag Home Assistant cannot order (`version-bf9e0b4f`, `ubuntu-2026-06-01`, ...) or would sort as older (`1.2.3-2`, `1.2.3+4`) no longer lands in config.yaml. The addon number is incremented instead, while the raw upstream tag stays in updater.json so the same release is never published twice
|
||||
- Pre-release markers become a version section, `5.0.0b5` is published as `5.0.0.5`
|
||||
- A tag Home Assistant cannot order keeps every number it carries, `v26.2-ls256` is published as `v26.2.256`
|
||||
- Upstream tags are escaped before being replaced in Dockerfile/build files
|
||||
|
||||
## 2026.06 (05-06-2026)
|
||||
- Minor bugs fixed
|
||||
## 2026.05 (30-05-2026)
|
||||
|
||||
@@ -56,6 +56,11 @@ ENV PACKAGES=""
|
||||
COPY ha_autoapps.sh /ha_autoapps.sh
|
||||
RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh
|
||||
|
||||
# Library used by Home Assistant to order addon versions, so that the version
|
||||
# published by the updater is one Home Assistant offers as an update. Keep the
|
||||
# pin aligned with the Supervisor requirements.txt
|
||||
RUN pip install --no-cache-dir awesomeversion==25.8.0
|
||||
|
||||
################
|
||||
# 4 Entrypoint #
|
||||
################
|
||||
|
||||
@@ -73,6 +73,22 @@ You can add the following tags in the file :
|
||||
- dockerhub_by_date: in dockerhub, uses the last_update date instead of the version
|
||||
- dockerhub_list_size: in dockerhub, how many containers to consider for latest version
|
||||
|
||||
### Addon version numbering
|
||||
|
||||
The `version` written in the addon `config.yaml` is the one Home Assistant compares to decide whether an update is available. Home Assistant hides the update when it can order both versions and the new one is not strictly newer (`1.2.3` -> `1.2.3-2` is a semver pre-release, so it is *older*), and it cannot order tags such as `version-bf9e0b4f` or `ubuntu-2026-06-01` at all.
|
||||
|
||||
The addon version is therefore derived from the upstream tag:
|
||||
|
||||
- a tag Home Assistant can order and that is newer is used as it is
|
||||
- `1.2.3-4` and `1.2.3+4` become `1.2.3.4`
|
||||
- a pre-release marker becomes a section of its own, so the number it carries keeps ordering the addon: `5.0.0b5` -> `5.0.0.5`
|
||||
- a tag it cannot order keeps every number it carries, in order: `v26.2-ls256` -> `v26.2.256`, `nightly-2.6.1.5509-ls8` -> `2.6.1.5509.8`, `4.16-r0-ls94` -> `4.16.0.94`, `ubuntu-2026-07-28` -> `2026.07.28`. Words holding no number, an architecture, and anything else such as a commit hash are left out
|
||||
- a tag holding no number at all (`version-bf9e0b4f`, `sts`) increments the current addon version (`1.37` -> `1.38`), or uses the date when there is nothing to increment (`2026.08.01`, then `2026.08.01.1` for a second update the same day)
|
||||
|
||||
`updater.json` always keeps the raw upstream tag, so the next run still compares upstream with upstream and a single upstream release never triggers two addon updates. The raw tag is also kept in the Dockerfile and the build files, and is added to the changelog entry when it differs from the addon version.
|
||||
|
||||
These rules are checked by `python3 /usr/bin/ha_version.py --selftest`, which can be run from a terminal in the addon container.
|
||||
|
||||
### Addon configuration
|
||||
|
||||
Here you define the values that will allow the addon to connect to your repository.
|
||||
|
||||
@@ -30,4 +30,4 @@ schema:
|
||||
slug: updater
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/addons_updater
|
||||
version: "2026.06"
|
||||
version: "2026.08"
|
||||
|
||||
@@ -20,6 +20,29 @@ else
|
||||
DATE_FORMAT="+%d-%m-%Y"
|
||||
fi
|
||||
|
||||
# Version published in the addon configuration, which is the one Home
|
||||
# Assistant compares; the upstream tag lives in updater.json instead
|
||||
function config_version() {
|
||||
local folder="$1"
|
||||
if [ -f "$folder/config.json" ]; then
|
||||
jq -r '.version // empty' "$folder/config.json"
|
||||
elif [ -f "$folder/config.yaml" ]; then
|
||||
sed -n 's/^version:[[:space:]]*//p' "$folder/config.yaml" \
|
||||
| head -n 1 \
|
||||
| sed -e 's/[[:space:]]*#.*$//' -e 's/^"//' -e 's/"$//' \
|
||||
| sed -e "s/^'//" -e "s/'$//"
|
||||
fi
|
||||
}
|
||||
|
||||
# Escape a version so that sed treats it as plain text on both sides
|
||||
function sed_pattern() {
|
||||
printf '%s' "$1" | sed -e 's/[]\/$*.^[]/\\&/g'
|
||||
}
|
||||
|
||||
function sed_replacement() {
|
||||
printf '%s' "$1" | sed -e 's/[\/&]/\\&/g'
|
||||
}
|
||||
|
||||
#Defining github value
|
||||
LOGINFO="... github authentification" && if [ "$VERBOSE" = true ]; then bashio::log.info "$LOGINFO"; fi
|
||||
|
||||
@@ -315,10 +338,6 @@ for f in */; do
|
||||
# Add brackets
|
||||
LASTVERSION='"'${LASTVERSION}'"'
|
||||
|
||||
# Avoid characters incompatible with HomeAssistant version name
|
||||
LASTVERSION2=${LASTVERSION//+/-}
|
||||
CURRENT2=${CURRENT//+/-}
|
||||
|
||||
# Skip if current or last version is empty (would corrupt files by replacing all "" occurrences)
|
||||
if [ "${CURRENT}" = '""' ] || [ "${LASTVERSION}" = '""' ]; then
|
||||
bashio::log.warning "... $SLUG : skipping update due to empty version string (current=${CURRENT}, latest=${LASTVERSION})"
|
||||
@@ -326,43 +345,91 @@ for f in */; do
|
||||
fi
|
||||
|
||||
# Update if needed
|
||||
if [ "${CURRENT2}" != "${LASTVERSION2}" ]; then
|
||||
if [ "${CURRENT}" != "${LASTVERSION}" ]; then
|
||||
LOGINFO="... $SLUG : update from ${CURRENT} to ${LASTVERSION}" && if [ "$VERBOSE" = true ]; then bashio::log.info "$LOGINFO"; fi
|
||||
|
||||
#Change all instances of version
|
||||
ADDONFOLDER="/data/${BASENAME}/${SLUG}"
|
||||
|
||||
# Version currently published, before any file is touched
|
||||
CONFIGVERSION="$(config_version "$ADDONFOLDER" || true)"
|
||||
if [ -z "$CONFIGVERSION" ]; then
|
||||
bashio::log.error "... $SLUG : no version found in the addon config, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Home Assistant hides an update when it can compare both
|
||||
# versions and the new one is not strictly newer, and cannot
|
||||
# order tags such as "version-bf9e0b4f" at all. The addon
|
||||
# version is therefore derived from the upstream tag, which
|
||||
# stays untouched in updater.json so that the same upstream
|
||||
# release is never published twice
|
||||
if ! ADDONVERSION="$(python3 /usr/bin/ha_version.py --current "$CONFIGVERSION" --upstream "${LASTVERSION//\"/}")"; then
|
||||
bashio::log.error "... $SLUG : no Home Assistant compliant version derived from ${LASTVERSION}, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$ADDONVERSION" != "${LASTVERSION//\"/}" ]; then
|
||||
bashio::log.blue "... $SLUG : Home Assistant would not offer ${LASTVERSION//\"/} as an update of $CONFIGVERSION, addon version set to $ADDONVERSION"
|
||||
fi
|
||||
|
||||
#Change all instances of version, the addon config excluded as
|
||||
#its version can now differ from the upstream tag
|
||||
LOGINFO="... $SLUG : updating files" && if [ "$VERBOSE" = true ]; then bashio::log.info "$LOGINFO"; fi
|
||||
for files in "config.json" "config.yaml" "Dockerfile" "build.json" "build.yaml"; do
|
||||
if [ -f /data/"${BASENAME}"/"${SLUG}"/$files ]; then
|
||||
sed -i "s/${CURRENT}/${LASTVERSION}/g" /data/"${BASENAME}"/"${SLUG}"/"$files"
|
||||
CURRENTPATTERN="$(sed_pattern "$CURRENT")"
|
||||
LASTVERSIONTEXT="$(sed_replacement "$LASTVERSION")"
|
||||
for files in "Dockerfile" "build.json" "build.yaml"; do
|
||||
if [ -f "$ADDONFOLDER/$files" ]; then
|
||||
sed -i "s/${CURRENTPATTERN}/${LASTVERSIONTEXT}/g" "$ADDONFOLDER/$files"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove " and modify version
|
||||
LASTVERSION=${LASTVERSION//\"/}
|
||||
CURRENT=${CURRENT//\"/}
|
||||
if [ -f /data/"${BASENAME}"/"${SLUG}"/config.json ]; then
|
||||
jq --arg variable "$LASTVERSION" '.version = $variable' /data/"${BASENAME}"/"${SLUG}"/config.json | sponge /data/"${BASENAME}"/"${SLUG}"/config.json # Replace version tag
|
||||
elif [ -f /data/"${BASENAME}"/"${SLUG}"/config.yaml ]; then
|
||||
sed -i "/version:/c\version: \"$LASTVERSION\"" /data/"${BASENAME}"/"${SLUG}"/config.yaml
|
||||
if [ -f "$ADDONFOLDER/config.json" ]; then
|
||||
# Piping jq into sponge would empty the file if jq fails
|
||||
if CONFIGJSON="$(jq --arg variable "$ADDONVERSION" '.version = $variable' "$ADDONFOLDER/config.json")"; then
|
||||
printf '%s\n' "$CONFIGJSON" > "$ADDONFOLDER/config.json" # Replace version tag
|
||||
fi
|
||||
elif [ -f "$ADDONFOLDER/config.yaml" ]; then
|
||||
sed -i "/^version:/c\version: \"$ADDONVERSION\"" "$ADDONFOLDER/config.yaml"
|
||||
fi
|
||||
jq --arg variable "$LASTVERSION" '.upstream_version = $variable' /data/"${BASENAME}"/"${SLUG}"/updater.json | sponge /data/"${BASENAME}"/"${SLUG}"/updater.json # Replace upstream tag
|
||||
jq --arg variable "$DATE" '.last_update = $variable' /data/"${BASENAME}"/"${SLUG}"/updater.json | sponge /data/"${BASENAME}"/"${SLUG}"/updater.json # Replace date tag
|
||||
|
||||
# Leave the addon untouched rather than committing a version
|
||||
# Home Assistant would not offer
|
||||
if [ "$(config_version "$ADDONFOLDER")" != "$ADDONVERSION" ]; then
|
||||
bashio::log.error "... $SLUG : version $ADDONVERSION could not be written in the addon config, reverting"
|
||||
git checkout -- "$ADDONFOLDER"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Replace upstream tag and date, keeping the file intact if jq
|
||||
# fails as a truncated updater.json would lose the addon source
|
||||
if ! UPDATERJSON="$(jq --arg version "$LASTVERSION" --arg date "$DATE" '.upstream_version = $version | .last_update = $date' "$ADDONFOLDER/updater.json")"; then
|
||||
bashio::log.error "... $SLUG : updater.json could not be updated, reverting"
|
||||
git checkout -- "$ADDONFOLDER"
|
||||
continue
|
||||
fi
|
||||
printf '%s\n' "$UPDATERJSON" > "$ADDONFOLDER/updater.json"
|
||||
|
||||
#Update changelog
|
||||
touch "/data/${BASENAME}/${SLUG}/CHANGELOG.md"
|
||||
if [[ "$SOURCE" == *"github"* ]]; then
|
||||
sed -i "1i - Update to latest version from $UPSTREAM (changelog : https://github.com/${UPSTREAM%/}/releases)" "/data/${BASENAME}/${SLUG}/CHANGELOG.md"
|
||||
else
|
||||
sed -i "1i - Update to latest version from $UPSTREAM" "/data/${BASENAME}/${SLUG}/CHANGELOG.md"
|
||||
touch "$ADDONFOLDER/CHANGELOG.md"
|
||||
if [ "$ADDONVERSION" != "$LASTVERSION" ]; then
|
||||
sed -i "1i - Upstream tag : $LASTVERSION" "$ADDONFOLDER/CHANGELOG.md"
|
||||
fi
|
||||
sed -i "1i ## ${LASTVERSION} (${DATE})" "/data/${BASENAME}/${SLUG}/CHANGELOG.md"
|
||||
sed -i "1i\ " "/data/${BASENAME}/${SLUG}/CHANGELOG.md"
|
||||
if [[ "$SOURCE" == *"github"* ]]; then
|
||||
sed -i "1i - Update to latest version from $UPSTREAM (changelog : https://github.com/${UPSTREAM%/}/releases)" "$ADDONFOLDER/CHANGELOG.md"
|
||||
else
|
||||
sed -i "1i - Update to latest version from $UPSTREAM" "$ADDONFOLDER/CHANGELOG.md"
|
||||
fi
|
||||
sed -i "1i ## ${ADDONVERSION} (${DATE})" "$ADDONFOLDER/CHANGELOG.md"
|
||||
sed -i "1i\ " "$ADDONFOLDER/CHANGELOG.md"
|
||||
LOGINFO="... $SLUG : files updated" && if [ "$VERBOSE" = true ]; then bashio::log.info "$LOGINFO"; fi
|
||||
|
||||
#Git commit and push
|
||||
git add -A # add all modified files
|
||||
|
||||
git commit -m "Updater bot : $SLUG updated to ${LASTVERSION}" > /dev/null
|
||||
git commit -m "Updater bot : $SLUG updated to ${ADDONVERSION} (upstream ${LASTVERSION})" > /dev/null
|
||||
|
||||
LOGINFO="... $SLUG : push to github" && if [ "$VERBOSE" = true ]; then bashio::log.info "$LOGINFO"; fi
|
||||
|
||||
@@ -374,7 +441,7 @@ for f in */; do
|
||||
fi
|
||||
|
||||
#Log
|
||||
bashio::log.yellow "... $SLUG updated from ${CURRENT} to ${LASTVERSION}"
|
||||
bashio::log.yellow "... $SLUG updated to ${ADDONVERSION} (upstream ${CURRENT} to ${LASTVERSION})"
|
||||
|
||||
else
|
||||
bashio::log.green "... $SLUG is up-to-date ${CURRENT}"
|
||||
|
||||
334
addons_updater/rootfs/usr/bin/ha_version.py
Normal file
334
addons_updater/rootfs/usr/bin/ha_version.py
Normal file
@@ -0,0 +1,334 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compute a Home Assistant compliant addon version."""
|
||||
|
||||
# Home Assistant orders addon versions with awesomeversion. Its update
|
||||
# entity hides the update whenever the two versions *can* be compared and
|
||||
# the new one is not strictly newer, and it cannot order a version at all
|
||||
# when the string follows no known scheme. Whatever lands in the addon
|
||||
# config.yaml must therefore be recognisable and strictly greater than the
|
||||
# version users already have installed.
|
||||
#
|
||||
# Upstream tags do not always cooperate:
|
||||
#
|
||||
# * "1.2.3-4" is a semver pre-release, i.e. older than "1.2.3"
|
||||
# * "1.2.3+4" only differs by build metadata, which semver ignores
|
||||
# * "version-bf9e0b4f" or "ubuntu-2026-06-01" follow no scheme at all
|
||||
#
|
||||
# This helper therefore decides what to write in config.yaml, while the raw
|
||||
# upstream tag stays in updater.json: the next run keeps comparing upstream
|
||||
# with upstream, so a single upstream release never triggers two addon
|
||||
# updates.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import Iterator
|
||||
from datetime import date
|
||||
|
||||
from awesomeversion import (
|
||||
AwesomeVersion,
|
||||
AwesomeVersionCompareException,
|
||||
AwesomeVersionStrategy,
|
||||
)
|
||||
|
||||
# "5.0.0b5" -> "5.0.0.5": a pre-release marker is not a version section,
|
||||
# so it is turned into one rather than left for Home Assistant to guess.
|
||||
MARKER = re.compile(r"^(v?\d+(?:\.\d+)*)(?:alpha|beta|rc|a|b)(\d+)(?=$|-)")
|
||||
# "1.2.3-4" -> "1.2.3.4": a numeric suffix behind a dash is a semver
|
||||
# pre-release and sorts before the version it is meant to supersede.
|
||||
PRERELEASE = re.compile(r"^(v?\d+(?:\.\d+)*)-(\d+(?:\.\d+)*)$")
|
||||
# A version made of numbers only, e.g. "1.37" or "v2026.07.10.2".
|
||||
DOTTED_NUMBER = re.compile(r"^(?P<prefix>v?)(?P<number>\d+(?:\.\d+)*)$")
|
||||
# A number carrying a name, e.g. "ls256", "r0" or the bare "2026".
|
||||
NAMED_NUMBER = re.compile(r"^[A-Za-z]*(\d+(?:\.\d+)*)$")
|
||||
# Words holding a number that says nothing about the release.
|
||||
NOT_A_NUMBER = frozenset(
|
||||
(
|
||||
"aarch64",
|
||||
"amd64",
|
||||
"arm64",
|
||||
"armhf",
|
||||
"armv6",
|
||||
"armv7",
|
||||
"i386",
|
||||
"i486",
|
||||
"i586",
|
||||
"i686",
|
||||
"mips64",
|
||||
"ppc64",
|
||||
"riscv64",
|
||||
"win32",
|
||||
"win64",
|
||||
"x64",
|
||||
"x86",
|
||||
)
|
||||
)
|
||||
|
||||
# Upper bound for the ".1", ".2", ... local rebuild counters.
|
||||
MAX_COUNTER = 100
|
||||
|
||||
|
||||
def normalise(version: str) -> str:
|
||||
"""Rewrite the parts of a tag Home Assistant would sort wrongly."""
|
||||
version = version.strip()
|
||||
# Build metadata is ignored by semver precedence, a section is not.
|
||||
version = version.replace("+", ".")
|
||||
# In both, group 1 is the release and group 2 the number to keep.
|
||||
version = MARKER.sub(r"\1.\2", version)
|
||||
return PRERELEASE.sub(r"\1.\2", version)
|
||||
|
||||
|
||||
def is_sortable(version: str) -> bool:
|
||||
"""Return True when awesomeversion recognises the version scheme."""
|
||||
if not version:
|
||||
return False
|
||||
return AwesomeVersion(version).strategy != AwesomeVersionStrategy.UNKNOWN
|
||||
|
||||
|
||||
def is_newer(candidate: str, current: str) -> bool:
|
||||
"""Return True when Home Assistant would offer the candidate."""
|
||||
if not candidate or candidate == current:
|
||||
return False
|
||||
if not current:
|
||||
return True
|
||||
try:
|
||||
return AwesomeVersion(candidate) > AwesomeVersion(current)
|
||||
except AwesomeVersionCompareException:
|
||||
# Home Assistant shows the update when it cannot compare, and it
|
||||
# is the only way out of a version following no known scheme.
|
||||
return True
|
||||
|
||||
|
||||
def is_acceptable(candidate: str, current: str) -> bool:
|
||||
"""Return True for a version safe to write in the addon config."""
|
||||
return is_sortable(candidate) and is_newer(candidate, current)
|
||||
|
||||
|
||||
def is_year(section: str) -> bool:
|
||||
"""Return True for a section that can only be a year."""
|
||||
return len(section) == 4 and 2000 <= int(section) <= 2999
|
||||
|
||||
|
||||
def is_date_like(number: str) -> bool:
|
||||
"""Return True for a real "YYYY.MM.DD", with or without a counter."""
|
||||
parts = number.split(".")
|
||||
if len(parts) < 3 or not is_year(parts[0]):
|
||||
return False
|
||||
try:
|
||||
date(*(int(part) for part in parts[:3]))
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def increment(number: str) -> str:
|
||||
"""Increment the last section, keeping any zero padding."""
|
||||
parts = number.split(".")
|
||||
parts[-1] = str(int(parts[-1]) + 1).zfill(len(parts[-1]))
|
||||
return ".".join(parts)
|
||||
|
||||
|
||||
def counters(base: str) -> Iterator[str]:
|
||||
"""Yield "<base>.1", "<base>.2", ... local rebuild counters."""
|
||||
for counter in range(1, MAX_COUNTER):
|
||||
yield f"{base}.{counter}"
|
||||
|
||||
|
||||
def skeleton(version: str) -> str:
|
||||
"""Return every number of a tag, in order, as dotted sections."""
|
||||
# "v26.2-ls256" -> "v26.2.256", "nightly-2.6.1.5509-ls8" ->
|
||||
# "2.6.1.5509.8", "4.16-r0-ls94" -> "4.16.0.94". Words carrying no
|
||||
# number and anything else, a commit hash in particular, are dropped.
|
||||
numbers = []
|
||||
for word in re.split(r"[-/]", normalise(version)):
|
||||
if word.lower() in NOT_A_NUMBER:
|
||||
continue
|
||||
named = NAMED_NUMBER.match(word)
|
||||
if named:
|
||||
numbers.append(named.group(1))
|
||||
if not numbers:
|
||||
return ""
|
||||
prefix = "v" if version.startswith("v") else ""
|
||||
return prefix + ".".join(numbers)
|
||||
|
||||
|
||||
def upstream_candidates(upstream: str) -> Iterator[str]:
|
||||
"""Yield the upstream tag, then the numbers hidden in it."""
|
||||
yield normalise(upstream)
|
||||
# "v26.3-ls256" -> "v26.3.256": the numbers of a tag Home Assistant
|
||||
# cannot sort still order the addon better than one of our making.
|
||||
yield skeleton(upstream)
|
||||
|
||||
|
||||
def local_candidates(current: str, today: date, release: str) -> Iterator[str]:
|
||||
"""Yield sortable versions derived from the current addon version."""
|
||||
calver = today.strftime("%Y.%m.%d")
|
||||
normalised = normalise(current)
|
||||
dotted = DOTTED_NUMBER.match(normalised)
|
||||
|
||||
# An upstream that rebuilds the release the current version was built
|
||||
# from, such as "v26.3-ls257" after "v26.3-ls256", gets a local
|
||||
# counter ("26.3.1") instead of a release it never published.
|
||||
if release and normalised.startswith(release):
|
||||
yield from counters(release)
|
||||
|
||||
if dotted and is_date_like(dotted.group("number")):
|
||||
# Calendar versioned addon: move to today, then count up when
|
||||
# several upstream releases land on the same day.
|
||||
yield calver
|
||||
if dotted.group("number").count(".") > 2:
|
||||
yield dotted.group("prefix") + increment(dotted.group("number"))
|
||||
yield from counters(normalised)
|
||||
elif dotted:
|
||||
# "1.37" -> "1.38": the number already in use simply moves on,
|
||||
# unless it ends on a year, which belongs to a date the addon
|
||||
# does not choose.
|
||||
if not is_year(dotted.group("number").split(".")[-1]):
|
||||
yield dotted.group("prefix") + increment(dotted.group("number"))
|
||||
yield from counters(normalised)
|
||||
else:
|
||||
# Nothing sortable to build on, e.g. "version-bf9e0b4f": keep the
|
||||
# numbers when the version has some, else switch to calendar
|
||||
# versioning, which is ordered and never runs out of numbers.
|
||||
numbers = skeleton(normalised)
|
||||
if numbers:
|
||||
yield numbers
|
||||
yield from counters(numbers)
|
||||
yield calver
|
||||
yield from counters(calver)
|
||||
|
||||
|
||||
def resolve(current: str, upstream: str, today: date) -> str:
|
||||
"""Return the version to write in the addon configuration."""
|
||||
for candidate in upstream_candidates(upstream):
|
||||
if is_acceptable(candidate, current):
|
||||
return candidate
|
||||
for candidate in local_candidates(current, today, skeleton(upstream)):
|
||||
if is_acceptable(candidate, current):
|
||||
return candidate
|
||||
return ""
|
||||
|
||||
|
||||
# Date the expectations below are written against.
|
||||
SELFTEST_DATE = date(2026, 8, 1)
|
||||
|
||||
SELFTESTS = (
|
||||
# (current, upstream, expected)
|
||||
# Sortable upstream releases are used as they are.
|
||||
("3.0.3", "3.0.4", "3.0.4"),
|
||||
("v3.21.0", "v3.22.0", "v3.22.0"),
|
||||
("2026.02.28", "2026.03.01", "2026.03.01"),
|
||||
("1.43.1.10611", "1.43.2.10650", "1.43.2.10650"),
|
||||
# Tags Home Assistant compares as older than what is installed.
|
||||
("1.2.3", "1.2.3-2", "1.2.3.2"),
|
||||
("1.2.3.2", "1.2.3-3", "1.2.3.3"),
|
||||
("1.2.3", "1.2.3+4", "1.2.3.4"),
|
||||
("1.2.3.4", "1.2.3+5", "1.2.3.5"),
|
||||
("1.2.4", "1.2.3", "1.2.5"),
|
||||
# Pre-release markers become a section of their own, so that the
|
||||
# number they carry keeps ordering the addon versions.
|
||||
("5.0.0b5-3", "5.0.0b5", "5.0.0.5"),
|
||||
("5.0.0.5", "5.0.0b6", "5.0.0.6"),
|
||||
("5.0.0.6", "5.0.0", "5.0.0.7"),
|
||||
("1.2.3", "1.2.4rc2", "1.2.4.2"),
|
||||
("1.2.3", "2.0.0beta1", "2.0.0.1"),
|
||||
("1.2.3", "1.2.4a1-2", "1.2.4.1.2"),
|
||||
# Tags Home Assistant cannot order keep every number they carry.
|
||||
("v26.2-ls255", "v26.3-ls256", "v26.3.256"),
|
||||
("v26.3.256", "v26.3-ls257", "v26.3.257"),
|
||||
("v1.67.0.8", "nightly-2.6.1.5509-ls8", "2.6.1.5509.8"),
|
||||
("4.16.0.93", "4.16-r0-ls94", "4.16.0.94"),
|
||||
("1.43.3.10828.315", "1.43.3.10828-00f62d37d-ls316", "1.43.3.10828.316"),
|
||||
("2026.06.01", "ubuntu-2026-07-01", "2026.07.01"),
|
||||
("20260729.1", "nightly-20260801", "20260801"),
|
||||
("20260801", "nightly-20260801-2", "20260801.2"),
|
||||
# A word holding a number that is not part of the release is left out.
|
||||
("5.3.2025.11.08", "5.3-amd64-2025-11-09", "5.3.2025.11.09"),
|
||||
("1.2.3", "nightly-1.2.4-i686", "1.2.4"),
|
||||
("1.2.3", "nightly-1.2.4-armv6", "1.2.4"),
|
||||
# A date that does not exist is a number like any other.
|
||||
("2026.02.31", "version-1a2b3c4d", "2026.02.32"),
|
||||
("2026.02.29", "version-1a2b3c4d", "2026.02.30"),
|
||||
("2028.02.29", "version-1a2b3c4d", "2028.02.29.1"),
|
||||
# Dockerhub tags dated by the updater itself.
|
||||
("1.2.3.2026.07.25", "1.2.3-2026-08-01", "1.2.3.2026.08.01"),
|
||||
# The same, dated the other way round: the date cannot order the
|
||||
# addon, so a local counter does.
|
||||
("1.2.3.25.07.2026", "1.2.3-01-08-2026", "1.2.3.25.07.2026.1"),
|
||||
# Tags holding no number at all: the addon number moves on...
|
||||
("1.37", "ubunturesolute-version-8208e985", "1.38"),
|
||||
("1.4", "sha-2b71a1c", "1.5"),
|
||||
("2025.12-6", "alpine-sts", "2026.08.01"),
|
||||
("version-bf9e0b4f", "version-1a2b3c4d", "2026.08.01"),
|
||||
# ... and calendar versions count up on the same day.
|
||||
("2026.08.01", "version-1a2b3c4d", "2026.08.01.1"),
|
||||
("2026.08.01.1", "version-2b3c4d5e", "2026.08.01.2"),
|
||||
("2026.07.30.2", "version-3c4d5e6f", "2026.08.01"),
|
||||
# A version dated in the future is never downgraded.
|
||||
("2026.09.15", "version-4d5e6f70", "2026.09.15.1"),
|
||||
# Switching between schemes, in both directions.
|
||||
("version-bf9e0b4f", "3.0.4", "3.0.4"),
|
||||
("3.0.4", "version-bf9e0b4f", "3.0.5"),
|
||||
# Tags holding characters that are special to sed.
|
||||
("1.2.3", "1.2.4+build[1]", "1.2.4"),
|
||||
("1.2.3", "release/2.0", "2.0"),
|
||||
("2.0", "release/2.0.1", "2.0.1"),
|
||||
("26.3", "v26.2-ls260", "26.4"),
|
||||
# No current version to build on.
|
||||
("", "3.0.4", "3.0.4"),
|
||||
("", "1.2.3-2", "1.2.3.2"),
|
||||
("", "version-bf9e0b4f", "2026.08.01"),
|
||||
)
|
||||
|
||||
|
||||
def selftest(today: date) -> int:
|
||||
"""Check the rules above against known addon version histories."""
|
||||
failures = 0
|
||||
for current, upstream, expected in SELFTESTS:
|
||||
result = resolve(current, upstream, today)
|
||||
if result != expected:
|
||||
failures += 1
|
||||
print(
|
||||
f"FAIL {current!r} + {upstream!r} -> "
|
||||
f"{result!r}, expected {expected!r}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(f"{len(SELFTESTS) - failures}/{len(SELFTESTS)} checks passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Parse the arguments and print the resulting version."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--current", default="", help="version in use")
|
||||
parser.add_argument("--upstream", default="", help="upstream tag")
|
||||
parser.add_argument("--today", default="", help="YYYY-MM-DD override")
|
||||
parser.add_argument(
|
||||
"--selftest", action="store_true", help="run the built-in checks"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.selftest:
|
||||
# The expectations above are written against a fixed date, so the
|
||||
# checks keep passing whenever they are run.
|
||||
if args.today:
|
||||
return selftest(date.fromisoformat(args.today))
|
||||
return selftest(SELFTEST_DATE)
|
||||
|
||||
today = date.fromisoformat(args.today) if args.today else date.today()
|
||||
|
||||
if not args.upstream:
|
||||
parser.error("--upstream is required")
|
||||
|
||||
version = resolve(args.current.strip(), args.upstream.strip(), today)
|
||||
if not version:
|
||||
print("no Home Assistant compliant version found", file=sys.stderr)
|
||||
return 1
|
||||
print(version)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
## 2.0.3 (2026-08-01)
|
||||
- Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases)
|
||||
|
||||
## 2.0.2 (2026-08-01)
|
||||
- Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases)
|
||||
|
||||
## 2.0.0 (2026-07-22)
|
||||
- Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Aurral
|
||||
version: "2.0.0"
|
||||
version: "2.0.3"
|
||||
slug: aurral
|
||||
description: >-
|
||||
Self-hosted music discovery, request management, flows, and playlist
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "aurral",
|
||||
"source": "github",
|
||||
"upstream_repo": "lklynet/aurral",
|
||||
"upstream_version": "2.0.0",
|
||||
"upstream_version": "2.0.3",
|
||||
"github_beta": false
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.83.0 (2026-08-01)
|
||||
- Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases)
|
||||
|
||||
## 1.82.1 (2026-07-16)
|
||||
- Update to latest version from autobrr/autobrr (changelog : https://github.com/autobrr/autobrr/releases)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
ARG BUILD_UPSTREAM="1.82.1"
|
||||
ARG BUILD_UPSTREAM="1.83.0"
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
##################
|
||||
|
||||
@@ -108,4 +108,4 @@ schema:
|
||||
slug: autobrr
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/autobrr
|
||||
version: "1.82.1"
|
||||
version: "1.83.0"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-16",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "autborr",
|
||||
"source": "github",
|
||||
"upstream_repo": "autobrr/autobrr",
|
||||
"upstream_version": "1.82.1"
|
||||
"upstream_version": "1.83.0"
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ ARG BUILD_UPSTREAM="0.0.101"
|
||||
ARG BUILD_FROM
|
||||
FROM $BUILD_FROM
|
||||
|
||||
# The upstream image now defaults to an unprivileged user, while this add-on
|
||||
# needs root during image customization and for its S6-managed services.
|
||||
USER root
|
||||
|
||||
##################
|
||||
# 2 Modify Image #
|
||||
##################
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 20260729.1 (2026-08-01)
|
||||
|
||||
- Version renamed from `source-20260729.1`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged
|
||||
|
||||
## source-20260729.1 (29-07-2026)
|
||||
- Minor bugs fixed
|
||||
## source-20260717 (17-07-2026)
|
||||
|
||||
@@ -127,5 +127,5 @@ slug: birdnet-go-dev
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "source-20260729.1"
|
||||
version: "20260729.1"
|
||||
video: true
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 0.8.5 (2026-08-01)
|
||||
- Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases)
|
||||
|
||||
## 0.8.4 (2026-07-13)
|
||||
- Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases)
|
||||
|
||||
|
||||
@@ -96,4 +96,4 @@ schema:
|
||||
ssl: bool?
|
||||
slug: birdnet-pipy
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-pipy
|
||||
version: "0.8.4"
|
||||
version: "0.8.5"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-13",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "birdnet-pipy",
|
||||
"source": "github",
|
||||
"upstream_repo": "Suncuss/BirdNET-PiPy",
|
||||
"upstream_version": "0.8.4"
|
||||
"upstream_version": "0.8.5"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.37.1 (2026-08-01)
|
||||
- Update to latest version from dani-garcia/bitwarden_rs (changelog : https://github.com/dani-garcia/bitwarden_rs/releases)
|
||||
|
||||
## 1.37.0 (2026-07-25)
|
||||
- Update to latest version from dani-garcia/bitwarden_rs (changelog : https://github.com/dani-garcia/bitwarden_rs/releases)
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ schema:
|
||||
slug: bitwarden
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/bitwarden
|
||||
version: "1.37.0"
|
||||
version: "1.37.1"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:7277]"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-01",
|
||||
"paused": false,
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "bitwarden",
|
||||
"source": "github",
|
||||
"upstream_repo": "dani-garcia/bitwarden_rs",
|
||||
"upstream_version": "1.37.0"
|
||||
"upstream_version": "1.37.1"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.93.129-ls119 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases)
|
||||
|
||||
## 1.92.144-ls117 (2026-07-25)
|
||||
- Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases)
|
||||
|
||||
|
||||
@@ -69,5 +69,5 @@ slug: brave
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.92.144-ls117"
|
||||
version: "1.93.129-ls119"
|
||||
video: true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_fulltag": "true",
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "brave",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-brave",
|
||||
"upstream_version": "1.92.144-ls117"
|
||||
"upstream_version": "1.93.129-ls119"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
|
||||
## 2026.08.01.1 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases)
|
||||
- Upstream tag : version-e2e1ec9c
|
||||
## 2026.08.01 (2026-08-01)
|
||||
|
||||
- Version renamed from `version-bf9e0b4f`, which Home Assistant could not order and therefore could not reliably offer as an update: the previous version held no number that could order it, so the date of this change is used. The addon itself and the upstream version it tracks are unchanged
|
||||
|
||||
|
||||
## version-bf9e0b4f (2026-07-25)
|
||||
- Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases)
|
||||
|
||||
|
||||
@@ -71,5 +71,5 @@ slug: chromium
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "version-bf9e0b4f"
|
||||
version: "2026.08.01.1"
|
||||
video: true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_fulltag": "true",
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "chromium",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-chromium",
|
||||
"upstream_version": "version-bf9e0b4f"
|
||||
"upstream_version": "version-e2e1ec9c"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.55.2 (2026-08-01)
|
||||
- Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases)
|
||||
|
||||
## 2.55.0 (2026-07-16)
|
||||
- Update to latest version from browserless/chrome (changelog : https://github.com/browserless/chrome/releases)
|
||||
|
||||
|
||||
@@ -86,5 +86,5 @@ schema:
|
||||
slug: browserless_chrome
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/browserless_chrome
|
||||
version: "2.55.0"
|
||||
version: "2.55.2"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:3000]/docs"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_tagfilter": "v",
|
||||
"last_update": "2026-07-16",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "browserless_chrome",
|
||||
"source": "github",
|
||||
"upstream_repo": "browserless/chrome",
|
||||
"upstream_version": "2.55.0"
|
||||
"upstream_version": "2.55.2"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 9.12.0 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases)
|
||||
|
||||
## 9.11.0 (2026-07-04)
|
||||
- Update to latest version from linuxserver/docker-calibre (changelog : https://github.com/linuxserver/docker-calibre/releases)
|
||||
|
||||
|
||||
@@ -117,5 +117,5 @@ schema:
|
||||
slug: calibre
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/calibre
|
||||
version: "9.11.0"
|
||||
version: "9.12.0"
|
||||
video: true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_fulltag": "false",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "calibre",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-calibre",
|
||||
"upstream_version": "9.11.0"
|
||||
"upstream_version": "9.12.0"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
## 1.38 (02-08-2026)
|
||||
|
||||
- Reduce Claude Desktop add-on RAM use in three places without removing Headroom, RTK, TokenSave, Cowork, Dispatch, or the streamed desktop. Headroom's heavy HTTP proxy is no longer started at container boot: a standard-library TCP gate stays on port 8787, starts the real proxy on the first request, and stops it after 15 minutes without traffic (`HEADROOM_IDLE_TIMEOUT_SECONDS` remains overridable through `env_vars`). This releases the proxy's Python, ONNX Runtime, tokenizer, and Kompress model allocations while the add-on is idle; a later request starts a clean backend transparently.
|
||||
- Prevent Headroom from loading a second Kompress model inside every Claude Desktop/Claude Code MCP process. The add-on now intercepts `headroom mcp serve` with a lightweight adapter that retains upstream MCP retrieval/statistics behavior but delegates `headroom_compress` to the shared proxy's loopback `/v1/compress` endpoint. The MCP process explicitly disables local Kompress and never imports `headroom.compress`, so only the proxy backend can own the ML runtime.
|
||||
- Remove the add-on-wide `tmpfs: true` mount and undo the shared Selkies script's `/tmp/cache` redirection for this add-on. Electron/Chromium, Mesa, and application caches now live under the selected persistent home (`$HOME/.cache`) as reclaimable filesystem cache instead of RAM-backed cgroup shmem. Runtime sockets and XDG runtime state remain under `/run`.
|
||||
|
||||
|
||||
## ubunturesolute-version-3a10bef7 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases)
|
||||
|
||||
## kali-version-e963b19b (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases)
|
||||
## 1.37 (28-07-2026)
|
||||
|
||||
- Fix the recurring "For your security, sign in again to keep using Claude." prompt for real. The v1.35 fix was ineffective: `--password-store=basic` was reaching the process (confirmed on a live install's `/proc/<pid>/cmdline`), yet the app kept logging `safeStorage not available, tokens will not persist` and `Encryption not available, returning empty env vars` on every launch. The missing half is an application-side opt-in — Electron refuses its built-in `basic_text` backend unless the app calls `safeStorage.setUsePlainTextEncryption(true)` before `ready`, and Claude Desktop never calls it (the symbol is present in the Electron binary but absent from `resources/app.asar`). So `isEncryptionAvailable()` stayed `false` and the auth token was never persisted, exactly as when the keyring backend was forced without a keyring daemon. Verified against a standalone Electron of the same generation: with `--password-store=basic` and no opt-in `isEncryptionAvailable()` is `false`; with the opt-in it is `true`, and a *separate later process* decrypts a blob written by an earlier one — which is the restart survival this add-on needs.
|
||||
|
||||
@@ -119,8 +119,7 @@ schema:
|
||||
tokensave_project_paths:
|
||||
- str
|
||||
slug: claude_desktop
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.37"
|
||||
version: "1.38"
|
||||
video: true
|
||||
|
||||
58
claude_desktop/rootfs/etc/cont-init.d/22-persistent_cache.sh
Normal file
58
claude_desktop/rootfs/etc/cont-init.d/22-persistent_cache.sh
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
|
||||
# 20-folders.sh is shared with the Webtop add-ons and historically redirects
|
||||
# XDG_CACHE_HOME to /tmp/cache. This Claude-specific follow-up runs before the
|
||||
# graphical longruns and moves general application caches back under the
|
||||
# persistent home. Removing config.yaml's `tmpfs: true` then ensures Chromium,
|
||||
# Electron, Mesa and other cache pages are reclaimable filesystem cache instead
|
||||
# of permanently charged cgroup shmem.
|
||||
LOCATION="$(getent passwd abc 2> /dev/null | cut -d: -f6 || true)"
|
||||
if [ -z "$LOCATION" ] || [ "$LOCATION" = "/" ]; then
|
||||
bashio::log.warning "Unable to resolve abc home; leaving XDG cache configuration unchanged"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CACHE_DIR="$LOCATION/.cache"
|
||||
if [ -L "$CACHE_DIR" ]; then
|
||||
rm -f "$CACHE_DIR"
|
||||
fi
|
||||
mkdir -p "$CACHE_DIR"
|
||||
chown "$(id -u abc):$(id -g abc)" "$CACHE_DIR"
|
||||
chmod 700 "$CACHE_DIR"
|
||||
|
||||
CACHE_DIR="$CACHE_DIR" LOCATION="$LOCATION" python3 - <<'PY'
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
cache = os.environ["CACHE_DIR"]
|
||||
quoted = cache.replace("\\", "\\\\").replace('"', '\\"')
|
||||
replacement = f'export XDG_CACHE_HOME="{quoted}"'
|
||||
|
||||
for path in Path("/etc/s6-overlay/s6-rc.d").glob("*/run"):
|
||||
try:
|
||||
text = path.read_text()
|
||||
except (OSError, UnicodeDecodeError):
|
||||
continue
|
||||
updated = re.sub(r"^export XDG_CACHE_HOME=.*$", replacement, text, flags=re.MULTILINE)
|
||||
if updated != text:
|
||||
path.write_text(updated)
|
||||
|
||||
bashrc = Path(os.environ["LOCATION"]) / ".bashrc"
|
||||
if bashrc.exists():
|
||||
text = bashrc.read_text()
|
||||
updated = re.sub(r"^export XDG_CACHE_HOME=.*$", replacement, text, flags=re.MULTILINE)
|
||||
if updated != text:
|
||||
bashrc.write_text(updated)
|
||||
PY
|
||||
|
||||
S6_ENVDIR="/run/s6/container_environment"
|
||||
mkdir -p "$S6_ENVDIR"
|
||||
printf '%s' "$CACHE_DIR" > "$S6_ENVDIR/XDG_CACHE_HOME"
|
||||
|
||||
# Safe here: no graphical longrun has started yet, and the former directory was
|
||||
# only a boot-created target for the now-removed persistent-home symlink.
|
||||
rm -rf /tmp/cache
|
||||
bashio::log.info "Application cache moved from RAM-backed /tmp to $CACHE_DIR"
|
||||
59
claude_desktop/rootfs/etc/cont-init.d/81-headroom_wrapper.sh
Normal file
59
claude_desktop/rootfs/etc/cont-init.d/81-headroom_wrapper.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
|
||||
# Keep every existing Headroom CLI command unchanged, but intercept the MCP
|
||||
# server entrypoint so it uses the add-on's single-runtime adapter. The real
|
||||
# binary is intentionally outside /usr/local/bin; writing the wrapper there
|
||||
# makes it the command 82-claude_tools.sh registers in Claude Desktop/Code.
|
||||
REAL_HEADROOM=""
|
||||
for candidate in /usr/bin/headroom /lsiopy/bin/headroom; do
|
||||
if [ -x "$candidate" ]; then
|
||||
REAL_HEADROOM="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$REAL_HEADROOM" ]; then
|
||||
bashio::log.warning "Headroom executable was not found; MCP adapter wrapper was not installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The adapter intentionally uses a small, stable subset of the upstream MCP
|
||||
# server. Since Headroom is installed unpinned at image build time, verify that
|
||||
# subset before replacing the command. A future incompatible Headroom release
|
||||
# therefore keeps its native MCP server instead of breaking Claude startup.
|
||||
if ! /lsiopy/bin/python3 - <<'PY'
|
||||
from headroom.ccr.mcp_server import HeadroomMCPServer
|
||||
|
||||
for name in ("run_stdio", "cleanup", "_compress_content"):
|
||||
if not callable(getattr(HeadroomMCPServer, name, None)):
|
||||
raise SystemExit(f"HeadroomMCPServer.{name} is unavailable")
|
||||
PY
|
||||
then
|
||||
bashio::log.warning "Installed Headroom is incompatible with the single-runtime MCP adapter; preserving the native MCP server"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
wrapper="$(mktemp /usr/local/bin/.headroom-wrapper.XXXXXX)"
|
||||
cleanup() {
|
||||
rm -f "$wrapper"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cat > "$wrapper" <<EOF
|
||||
#!/bin/sh
|
||||
REAL_HEADROOM="$REAL_HEADROOM"
|
||||
if [ "\${1:-}" = "mcp" ] && [ "\${2:-}" = "serve" ]; then
|
||||
shift 2
|
||||
unset HF_HOME
|
||||
export HEADROOM_DISABLE_KOMPRESS=1
|
||||
exec /lsiopy/bin/python3 /usr/local/bin/headroom-mcp-proxy.py "\$@"
|
||||
fi
|
||||
exec "\$REAL_HEADROOM" "\$@"
|
||||
EOF
|
||||
chmod 0755 "$wrapper"
|
||||
mv -f "$wrapper" /usr/local/bin/headroom
|
||||
trap - EXIT
|
||||
|
||||
bashio::log.info "Headroom MCP compression is delegated to the shared lazy proxy runtime"
|
||||
@@ -1,32 +1,43 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# Headroom optimization proxy — local backend for Claude Desktop MCP and Claude Code.
|
||||
# Headroom optimization proxy — lazy backend for Claude Desktop MCP and Claude Code.
|
||||
declare port=8787
|
||||
declare backend_port=8789
|
||||
declare host=127.0.0.1
|
||||
|
||||
# The dashboard is unauthenticated. Keep it container-local by default and bind all
|
||||
# interfaces only when the user explicitly opts in and maps port 8787.
|
||||
# The dashboard is unauthenticated. Keep the gate container-local by default and
|
||||
# bind all interfaces only when the user explicitly opts in and maps port 8787.
|
||||
if bashio::config.true 'expose_headroom_dashboard'; then
|
||||
host=0.0.0.0
|
||||
fi
|
||||
|
||||
if bashio::config.true 'install_headroom' && command -v headroom > /dev/null 2>&1; then
|
||||
# Kompress (the ONNX compression engine) needs its model in the local HF cache: the
|
||||
# proxy's startup preload is deliberately cache-only, and the default HF cache lands
|
||||
# under ~/.cache, which the add-on points at tmpfs (/tmp/cache) — wiped on every
|
||||
# restart. Without a warm persistent cache the proxy ran forever in "deferred" mode
|
||||
# and recorded zero compression savings. Point the cache at persistent storage;
|
||||
# nothing else is needed here — the proxy's own request path already downloads a
|
||||
# missing model in the background on first use (ensure_background_load) and passes
|
||||
# requests through uncompressed until it lands, so this self-heals within a couple of
|
||||
# requests on the first boot and loads instantly (eager preload) on every boot after.
|
||||
# A synchronous pre-warm was tried here and removed: it blocked the port bind for up
|
||||
# to the download's duration, which left the settings-managed ANTHROPIC_BASE_URL
|
||||
# (see 82-claude_tools.sh) pointing at a proxy that wasn't listening yet.
|
||||
export HF_HOME="${HOME}/.headroom/hf"
|
||||
mkdir -p "$HF_HOME"
|
||||
chown abc:abc "$HF_HOME" 2> /dev/null || true
|
||||
bashio::log.info "svc-headroom: starting local Headroom proxy on ${host}:${port}"
|
||||
exec s6-setuidgid abc headroom proxy --host "${host}" --port "${port}" --code-aware
|
||||
if bashio::config.true 'install_headroom'; then
|
||||
real_headroom=""
|
||||
for candidate in /usr/bin/headroom /lsiopy/bin/headroom; do
|
||||
if [ -x "$candidate" ]; then
|
||||
real_headroom="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -n "$real_headroom" ]; then
|
||||
# Keep model artifacts persistent, but do not import Headroom or load the
|
||||
# model in this longrun. The standard-library gate starts the real proxy
|
||||
# on the first request and terminates it after the idle timeout, releasing
|
||||
# Python/ONNX/model allocations. HEADROOM_IDLE_TIMEOUT_SECONDS is
|
||||
# overridable through env_vars; 900 seconds is the default.
|
||||
export HF_HOME="${HOME}/.headroom/hf"
|
||||
mkdir -p "$HF_HOME" "${HOME}/.headroom"
|
||||
chown abc:abc "${HOME}/.headroom" "$HF_HOME" 2> /dev/null || true
|
||||
bashio::log.info "svc-headroom: starting lazy gate on ${host}:${port} (backend ${backend_port}, idle timeout ${HEADROOM_IDLE_TIMEOUT_SECONDS:-900}s)"
|
||||
exec s6-setuidgid abc env \
|
||||
HEADROOM_REAL_BIN="$real_headroom" \
|
||||
HEADROOM_GATE_HOST="$host" \
|
||||
HEADROOM_GATE_PORT="$port" \
|
||||
HEADROOM_BACKEND_HOST=127.0.0.1 \
|
||||
HEADROOM_BACKEND_PORT="$backend_port" \
|
||||
HEADROOM_IDLE_TIMEOUT_SECONDS="${HEADROOM_IDLE_TIMEOUT_SECONDS:-900}" \
|
||||
HF_HOME="$HF_HOME" \
|
||||
/lsiopy/bin/python3 /usr/local/bin/headroom-proxy-gate.py
|
||||
fi
|
||||
fi
|
||||
|
||||
bashio::log.info "svc-headroom: install_headroom disabled or headroom not found; idling"
|
||||
|
||||
142
claude_desktop/rootfs/usr/local/bin/headroom-mcp-proxy.py
Normal file
142
claude_desktop/rootfs/usr/local/bin/headroom-mcp-proxy.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/lsiopy/bin/python3
|
||||
"""Run Headroom MCP with compression delegated to the single proxy backend.
|
||||
|
||||
Upstream Headroom MCP normally performs ``headroom_compress`` in its own
|
||||
process. That imports the compression pipeline and can load a second copy of the
|
||||
Kompress ONNX model in addition to the HTTP proxy. This adapter preserves the
|
||||
upstream MCP protocol and retrieve/stats implementations, but replaces only its
|
||||
local compression method with a call to the proxy's loopback-only
|
||||
``/v1/compress`` endpoint.
|
||||
|
||||
The proxy gate starts the heavy backend on this first request and later unloads
|
||||
it after the configured idle timeout. The MCP process therefore remains a
|
||||
lightweight protocol bridge and never imports ``headroom.compress``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from types import MethodType
|
||||
from typing import Any
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Headroom MCP single-runtime adapter")
|
||||
parser.add_argument(
|
||||
"--proxy-url",
|
||||
default=os.environ.get("HEADROOM_PROXY_URL", "http://127.0.0.1:8787"),
|
||||
)
|
||||
parser.add_argument("--transport", default="stdio")
|
||||
parser.add_argument("--host", default="127.0.0.1")
|
||||
parser.add_argument("--port", type=int, default=8788)
|
||||
parser.add_argument("--path", default="/mcp")
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
parser.add_argument("--direct", action="store_true")
|
||||
args, unknown = parser.parse_known_args()
|
||||
if unknown:
|
||||
print(f"headroom-mcp-proxy: ignoring unsupported arguments: {unknown}", file=sys.stderr)
|
||||
if args.transport.lower() != "stdio":
|
||||
parser.error("only stdio transport is supported by the add-on adapter")
|
||||
return args
|
||||
|
||||
|
||||
def _number(data: dict[str, Any], key: str) -> int:
|
||||
value = data.get(key, 0)
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return 0
|
||||
|
||||
|
||||
def make_proxy_compressor(proxy_url: str):
|
||||
endpoint = f"{proxy_url.rstrip('/')}/v1/compress"
|
||||
model = os.environ.get("HEADROOM_MCP_MODEL", "claude-sonnet-4-5-20250929")
|
||||
|
||||
def compress_via_proxy(_self, content: str) -> dict[str, Any]:
|
||||
# Imported here, not at module import, so MCP startup stays small. httpx is
|
||||
# already a dependency of Headroom's MCP server for retrieve/stats.
|
||||
import httpx
|
||||
|
||||
response = httpx.post(
|
||||
endpoint,
|
||||
json={
|
||||
"messages": [{"role": "tool", "content": content}],
|
||||
"model": model,
|
||||
},
|
||||
timeout=httpx.Timeout(180.0, connect=75.0),
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if not isinstance(data, dict):
|
||||
raise RuntimeError("Headroom proxy returned a non-object compression response")
|
||||
|
||||
messages = data.get("messages")
|
||||
compressed: Any = content
|
||||
if isinstance(messages, list) and messages:
|
||||
last = messages[-1]
|
||||
if isinstance(last, dict) and "content" in last:
|
||||
compressed = last["content"]
|
||||
if not isinstance(compressed, str):
|
||||
compressed = json.dumps(compressed, ensure_ascii=False)
|
||||
|
||||
hashes = data.get("ccr_hashes")
|
||||
hash_key = next((item for item in hashes if isinstance(item, str)), None) if isinstance(hashes, list) else None
|
||||
before = _number(data, "tokens_before")
|
||||
after = _number(data, "tokens_after")
|
||||
saved = _number(data, "tokens_saved")
|
||||
if saved <= 0:
|
||||
saved = max(0, before - after)
|
||||
savings_percent = round(saved / before * 100, 1) if before > 0 else 0.0
|
||||
transforms = data.get("transforms_applied")
|
||||
if not isinstance(transforms, list):
|
||||
transforms = []
|
||||
|
||||
note = "Compression was executed by the shared Headroom proxy runtime."
|
||||
if hash_key:
|
||||
note += (
|
||||
f" Original stored with hash={hash_key}. "
|
||||
"Use mcp__headroom__headroom_retrieve to recover it."
|
||||
)
|
||||
return {
|
||||
"compressed": compressed,
|
||||
"hash": hash_key,
|
||||
"original_tokens": before,
|
||||
"compressed_tokens": after,
|
||||
"tokens_saved": saved,
|
||||
"savings_percent": savings_percent,
|
||||
"transforms": transforms,
|
||||
"note": note,
|
||||
}
|
||||
|
||||
return compress_via_proxy
|
||||
|
||||
|
||||
async def run() -> None:
|
||||
args = parse_args()
|
||||
|
||||
# Defense in depth. The adapter never calls the local compressor, but keep
|
||||
# the MCP process explicitly unable to initialize Kompress if upstream code
|
||||
# changes or an unrelated import probes the compression pipeline.
|
||||
os.environ.pop("HF_HOME", None)
|
||||
os.environ["HEADROOM_DISABLE_KOMPRESS"] = "1"
|
||||
os.environ["HEADROOM_PROXY_URL"] = args.proxy_url
|
||||
|
||||
from headroom.ccr.mcp_server import HeadroomMCPServer
|
||||
|
||||
server = HeadroomMCPServer(proxy_url=args.proxy_url, check_proxy=True)
|
||||
server._compress_content = MethodType(make_proxy_compressor(args.proxy_url), server)
|
||||
try:
|
||||
await server.run_stdio()
|
||||
finally:
|
||||
await server.cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(run())
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
388
claude_desktop/rootfs/usr/local/bin/headroom-proxy-gate.py
Normal file
388
claude_desktop/rootfs/usr/local/bin/headroom-proxy-gate.py
Normal file
@@ -0,0 +1,388 @@
|
||||
#!/lsiopy/bin/python3
|
||||
"""Lazy TCP gate for the Headroom HTTP proxy.
|
||||
|
||||
The gate remains resident on the public Headroom port while the heavy Headroom
|
||||
proxy (and its optional ONNX Kompress model) is started only for real traffic.
|
||||
After an idle period the backend process is terminated, releasing its Python,
|
||||
ONNX and model allocations. The next connection starts a fresh backend.
|
||||
|
||||
Only the Python standard library is imported here deliberately: the idle path
|
||||
must not import Headroom, ONNX Runtime, transformers or the MCP SDK.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _int_env(name: str, default: int, minimum: int = 1) -> int:
|
||||
try:
|
||||
value = int(os.environ.get(name, str(default)))
|
||||
except (TypeError, ValueError):
|
||||
value = default
|
||||
return max(minimum, value)
|
||||
|
||||
|
||||
GATE_HOST = os.environ.get("HEADROOM_GATE_HOST", "127.0.0.1")
|
||||
GATE_PORT = _int_env("HEADROOM_GATE_PORT", 8787)
|
||||
BACKEND_HOST = os.environ.get("HEADROOM_BACKEND_HOST", "127.0.0.1")
|
||||
BACKEND_PORT = _int_env("HEADROOM_BACKEND_PORT", 8789)
|
||||
HEADROOM_BIN = os.environ.get("HEADROOM_REAL_BIN", "/usr/bin/headroom")
|
||||
IDLE_TIMEOUT = _int_env("HEADROOM_IDLE_TIMEOUT_SECONDS", 900)
|
||||
START_TIMEOUT = _int_env("HEADROOM_START_TIMEOUT_SECONDS", 60)
|
||||
STOP_TIMEOUT = _int_env("HEADROOM_STOP_TIMEOUT_SECONDS", 10)
|
||||
HF_HOME = os.environ.get("HF_HOME", str(Path.home() / ".headroom/hf"))
|
||||
MAX_HEADER_BYTES = 128 * 1024
|
||||
COPY_CHUNK = 64 * 1024
|
||||
|
||||
|
||||
def log(message: str) -> None:
|
||||
stamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{stamp}] headroom-gate: {message}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
class BackendUnavailable(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class ProxyGate:
|
||||
def __init__(self) -> None:
|
||||
self._lock = asyncio.Lock()
|
||||
self._process: asyncio.subprocess.Process | None = None
|
||||
self._last_activity = time.monotonic()
|
||||
self._active_connections = 0
|
||||
self._stopping = False
|
||||
self._watch_task: asyncio.Task[None] | None = None
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
process = self._process
|
||||
if process is None:
|
||||
return "dormant"
|
||||
if process.returncode is None:
|
||||
return "running"
|
||||
return "stopped"
|
||||
|
||||
def touch(self) -> None:
|
||||
self._last_activity = time.monotonic()
|
||||
|
||||
async def _backend_healthy(self) -> bool:
|
||||
for path in ("/livez", "/health"):
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(BACKEND_HOST, BACKEND_PORT), timeout=1.0
|
||||
)
|
||||
request = (
|
||||
f"GET {path} HTTP/1.1\r\n"
|
||||
f"Host: {BACKEND_HOST}:{BACKEND_PORT}\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
).encode()
|
||||
writer.write(request)
|
||||
await writer.drain()
|
||||
line = await asyncio.wait_for(reader.readline(), timeout=1.0)
|
||||
writer.close()
|
||||
with contextlib.suppress(Exception):
|
||||
await writer.wait_closed()
|
||||
if line.startswith(b"HTTP/") and b" 2" in line[:16]:
|
||||
return True
|
||||
except (OSError, asyncio.TimeoutError):
|
||||
continue
|
||||
return False
|
||||
|
||||
async def ensure_backend(self) -> None:
|
||||
if await self._backend_healthy():
|
||||
self.touch()
|
||||
return
|
||||
|
||||
async with self._lock:
|
||||
if await self._backend_healthy():
|
||||
self.touch()
|
||||
return
|
||||
|
||||
process = self._process
|
||||
if process is not None and process.returncode is None:
|
||||
await self._wait_until_healthy()
|
||||
self.touch()
|
||||
return
|
||||
|
||||
if not os.path.isfile(HEADROOM_BIN) or not os.access(HEADROOM_BIN, os.X_OK):
|
||||
raise BackendUnavailable(f"Headroom executable is unavailable: {HEADROOM_BIN}")
|
||||
|
||||
Path(HF_HOME).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
environment = os.environ.copy()
|
||||
environment.update(
|
||||
{
|
||||
"HF_HOME": HF_HOME,
|
||||
"HEADROOM_PROXY_GATE": "1",
|
||||
}
|
||||
)
|
||||
command = [
|
||||
HEADROOM_BIN,
|
||||
"proxy",
|
||||
"--host",
|
||||
BACKEND_HOST,
|
||||
"--port",
|
||||
str(BACKEND_PORT),
|
||||
"--code-aware",
|
||||
]
|
||||
log(f"starting backend on {BACKEND_HOST}:{BACKEND_PORT}")
|
||||
try:
|
||||
self._process = await asyncio.create_subprocess_exec(
|
||||
*command,
|
||||
env=environment,
|
||||
start_new_session=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
self._process = None
|
||||
raise BackendUnavailable(f"unable to start Headroom: {exc}") from exc
|
||||
|
||||
self._watch_task = asyncio.create_task(self._watch_backend(self._process))
|
||||
try:
|
||||
await self._wait_until_healthy()
|
||||
except Exception:
|
||||
await self._terminate_backend_locked("startup failure")
|
||||
raise
|
||||
self.touch()
|
||||
|
||||
async def _wait_until_healthy(self) -> None:
|
||||
deadline = time.monotonic() + START_TIMEOUT
|
||||
while time.monotonic() < deadline:
|
||||
process = self._process
|
||||
if process is not None and process.returncode is not None:
|
||||
raise BackendUnavailable(
|
||||
f"Headroom exited during startup with status {process.returncode}; "
|
||||
"see the add-on log"
|
||||
)
|
||||
if await self._backend_healthy():
|
||||
log("backend is ready")
|
||||
return
|
||||
await asyncio.sleep(0.25)
|
||||
raise BackendUnavailable(
|
||||
f"Headroom did not become ready within {START_TIMEOUT}s; see the add-on log"
|
||||
)
|
||||
|
||||
async def _watch_backend(self, process: asyncio.subprocess.Process) -> None:
|
||||
returncode = await process.wait()
|
||||
async with self._lock:
|
||||
was_current = self._process is process
|
||||
if was_current:
|
||||
self._process = None
|
||||
if not self._stopping and was_current:
|
||||
log(f"backend exited with status {returncode}")
|
||||
|
||||
async def stop_backend(self, reason: str) -> None:
|
||||
async with self._lock:
|
||||
await self._terminate_backend_locked(reason)
|
||||
|
||||
async def _terminate_backend_locked(self, reason: str) -> None:
|
||||
process = self._process
|
||||
if process is None:
|
||||
return
|
||||
if process.returncode is not None:
|
||||
self._process = None
|
||||
return
|
||||
|
||||
log(f"stopping backend ({reason})")
|
||||
try:
|
||||
os.killpg(process.pid, signal.SIGTERM)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=STOP_TIMEOUT)
|
||||
except asyncio.TimeoutError:
|
||||
log("backend did not stop after SIGTERM; sending SIGKILL")
|
||||
with contextlib.suppress(ProcessLookupError):
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
with contextlib.suppress(Exception):
|
||||
await process.wait()
|
||||
self._process = None
|
||||
|
||||
async def idle_monitor(self) -> None:
|
||||
interval = max(5, min(30, IDLE_TIMEOUT // 4))
|
||||
while not self._stopping:
|
||||
await asyncio.sleep(interval)
|
||||
process = self._process
|
||||
idle_for = time.monotonic() - self._last_activity
|
||||
if (
|
||||
process is not None
|
||||
and process.returncode is None
|
||||
and idle_for >= IDLE_TIMEOUT
|
||||
):
|
||||
await self.stop_backend(f"idle for {int(idle_for)}s")
|
||||
|
||||
def status_payload(self) -> bytes:
|
||||
process = self._process
|
||||
payload = {
|
||||
"status": self.state,
|
||||
"backend_pid": process.pid if process is not None and process.returncode is None else None,
|
||||
"active_connections": self._active_connections,
|
||||
"idle_seconds": round(time.monotonic() - self._last_activity, 1),
|
||||
"idle_timeout_seconds": IDLE_TIMEOUT,
|
||||
"backend": f"{BACKEND_HOST}:{BACKEND_PORT}",
|
||||
}
|
||||
body = json.dumps(payload, separators=(",", ":")).encode()
|
||||
return (
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
b"Content-Type: application/json\r\n"
|
||||
+ f"Content-Length: {len(body)}\r\n".encode()
|
||||
+ b"Connection: close\r\n\r\n"
|
||||
+ body
|
||||
)
|
||||
|
||||
async def handle_client(
|
||||
self,
|
||||
client_reader: asyncio.StreamReader,
|
||||
client_writer: asyncio.StreamWriter,
|
||||
) -> None:
|
||||
self._active_connections += 1
|
||||
real_traffic = False
|
||||
peer = client_writer.get_extra_info("peername")
|
||||
try:
|
||||
try:
|
||||
initial = await asyncio.wait_for(
|
||||
client_reader.readuntil(b"\r\n\r\n"), timeout=15.0
|
||||
)
|
||||
except (
|
||||
asyncio.IncompleteReadError,
|
||||
asyncio.LimitOverrunError,
|
||||
asyncio.TimeoutError,
|
||||
):
|
||||
return
|
||||
if len(initial) > MAX_HEADER_BYTES:
|
||||
await self._send_error(client_writer, 431, "request headers too large")
|
||||
return
|
||||
|
||||
first_line = initial.split(b"\r\n", 1)[0]
|
||||
parts = first_line.split(b" ")
|
||||
target = parts[1].split(b"?", 1)[0] if len(parts) >= 2 else b""
|
||||
if target == b"/gate/status":
|
||||
client_writer.write(self.status_payload())
|
||||
await client_writer.drain()
|
||||
return
|
||||
|
||||
real_traffic = True
|
||||
self.touch()
|
||||
try:
|
||||
await self.ensure_backend()
|
||||
backend_reader, backend_writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(BACKEND_HOST, BACKEND_PORT), timeout=5.0
|
||||
)
|
||||
except (BackendUnavailable, OSError, asyncio.TimeoutError) as exc:
|
||||
log(f"backend unavailable for {peer}: {exc}")
|
||||
await self._send_error(client_writer, 503, str(exc))
|
||||
return
|
||||
|
||||
backend_writer.write(initial)
|
||||
await backend_writer.drain()
|
||||
self.touch()
|
||||
|
||||
upstream = asyncio.create_task(self._pipe(client_reader, backend_writer))
|
||||
downstream = asyncio.create_task(self._pipe(backend_reader, client_writer))
|
||||
done, pending = await asyncio.wait(
|
||||
{upstream, downstream}, return_when=asyncio.FIRST_COMPLETED
|
||||
)
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
for task in done | pending:
|
||||
with contextlib.suppress(
|
||||
asyncio.CancelledError,
|
||||
ConnectionError,
|
||||
OSError,
|
||||
):
|
||||
await task
|
||||
backend_writer.close()
|
||||
with contextlib.suppress(Exception):
|
||||
await backend_writer.wait_closed()
|
||||
finally:
|
||||
self._active_connections = max(0, self._active_connections - 1)
|
||||
if real_traffic:
|
||||
self.touch()
|
||||
client_writer.close()
|
||||
with contextlib.suppress(Exception):
|
||||
await client_writer.wait_closed()
|
||||
|
||||
async def _pipe(
|
||||
self,
|
||||
reader: asyncio.StreamReader,
|
||||
writer: asyncio.StreamWriter,
|
||||
) -> None:
|
||||
while True:
|
||||
data = await reader.read(COPY_CHUNK)
|
||||
if not data:
|
||||
with contextlib.suppress(Exception):
|
||||
writer.write_eof()
|
||||
return
|
||||
writer.write(data)
|
||||
await writer.drain()
|
||||
self.touch()
|
||||
|
||||
@staticmethod
|
||||
async def _send_error(
|
||||
writer: asyncio.StreamWriter,
|
||||
status: int,
|
||||
detail: str,
|
||||
) -> None:
|
||||
reason = (
|
||||
"Service Unavailable"
|
||||
if status == 503
|
||||
else "Request Header Fields Too Large"
|
||||
)
|
||||
body = json.dumps({"error": detail}).encode()
|
||||
response = (
|
||||
f"HTTP/1.1 {status} {reason}\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
f"Content-Length: {len(body)}\r\n"
|
||||
"Connection: close\r\n\r\n"
|
||||
).encode() + body
|
||||
writer.write(response)
|
||||
with contextlib.suppress(Exception):
|
||||
await writer.drain()
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
self._stopping = True
|
||||
await self.stop_backend("gate shutdown")
|
||||
|
||||
|
||||
async def async_main() -> None:
|
||||
gate = ProxyGate()
|
||||
loop = asyncio.get_running_loop()
|
||||
stop_event = asyncio.Event()
|
||||
for sig in (signal.SIGTERM, signal.SIGINT):
|
||||
with contextlib.suppress(NotImplementedError):
|
||||
loop.add_signal_handler(sig, stop_event.set)
|
||||
|
||||
server = await asyncio.start_server(
|
||||
gate.handle_client,
|
||||
GATE_HOST,
|
||||
GATE_PORT,
|
||||
limit=MAX_HEADER_BYTES + 1,
|
||||
family=socket.AF_INET,
|
||||
)
|
||||
addresses = ", ".join(str(sock.getsockname()) for sock in server.sockets or [])
|
||||
log(
|
||||
f"listening on {addresses}; backend is lazy and stops after {IDLE_TIMEOUT}s idle; "
|
||||
"status endpoint: /gate/status"
|
||||
)
|
||||
monitor = asyncio.create_task(gate.idle_monitor())
|
||||
async with server:
|
||||
await stop_event.wait()
|
||||
monitor.cancel()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await monitor
|
||||
await gate.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(async_main())
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
@@ -4,6 +4,6 @@
|
||||
"github_fulltag": true,
|
||||
"slug": "claude_desktop",
|
||||
"paused": false,
|
||||
"upstream_version": "ubunturesolute-version-8208e985",
|
||||
"last_update": "2026-07-21"
|
||||
"upstream_version": "ubunturesolute-version-3a10bef7",
|
||||
"last_update": "2026-08-01"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.10.2 (2026-08-01)
|
||||
- Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases)
|
||||
|
||||
## 2.9.16 (2026-07-11)
|
||||
- Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#=== Home Assistant Addon ===#
|
||||
|
||||
# ARGs used in FROM must be declared before any FROM instruction
|
||||
ARG BUILD_UPSTREAM="2.9.16"
|
||||
ARG BUILD_UPSTREAM="2.10.2"
|
||||
|
||||
#################
|
||||
# 1 Build Image #
|
||||
|
||||
@@ -91,5 +91,5 @@ schema:
|
||||
TZ: str?
|
||||
slug: cleanuparr
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/cleanuparr
|
||||
version: "2.9.16"
|
||||
version: "2.10.2"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:11011]"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-11",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "cleanuparr",
|
||||
"source": "github",
|
||||
"upstream_repo": "Cleanuparr/Cleanuparr",
|
||||
"upstream_version": "2.9.16"
|
||||
"upstream_version": "2.10.2"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
## 2.2.5 (2026-08-02)
|
||||
- Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases)
|
||||
|
||||
## 2.2.4 (2026-08-01)
|
||||
- Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases)
|
||||
|
||||
## 2.2.2 (2026-07-25)
|
||||
- Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases)
|
||||
|
||||
|
||||
@@ -101,4 +101,4 @@ schema:
|
||||
slug: codex
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "2.2.2"
|
||||
version: "2.2.5"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": "true",
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-02",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "codex",
|
||||
"source": "github",
|
||||
"upstream_repo": "ajslater/codex",
|
||||
"upstream_version": "2.2.2"
|
||||
"upstream_version": "2.2.5"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
## 4.10.0.22 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
## 4.10.0.21 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
## 4.10.0.20 (2026-07-18)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
ARG BUILD_UPSTREAM="4.10.0.20"
|
||||
ARG BUILD_UPSTREAM="4.10.0.22"
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
##################
|
||||
|
||||
@@ -122,5 +122,5 @@ schema:
|
||||
slug: emby_nas
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/emby
|
||||
version: "4.10.0.20"
|
||||
version: "4.10.0.22"
|
||||
video: true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": "true",
|
||||
"last_update": "2026-07-18",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "emby",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-emby",
|
||||
"upstream_version": "4.10.0.20"
|
||||
"upstream_version": "4.10.0.22"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
|
||||
## 2026.08.01 (2026-08-01)
|
||||
- Update to latest version from charlocharlie/epicgames-freegames
|
||||
- Upstream tag : debian-2026-08-01
|
||||
## 2026.07.27 (2026-08-01)
|
||||
|
||||
- Version renamed from `debian-2026-07-27`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged
|
||||
|
||||
|
||||
## debian-2026-07-27 (2026-07-27)
|
||||
- Update to latest version from charlocharlie/epicgames-freegames
|
||||
|
||||
## debian-2026-07-24 (2026-07-24)
|
||||
- Update to latest version from charlocharlie/epicgames-freegames
|
||||
|
||||
|
||||
@@ -88,5 +88,5 @@ schema:
|
||||
slug: epicgamesfree
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "debian-2026-07-24"
|
||||
version: "2026.08.01"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:3000]"
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"dockerhub_by_date": true,
|
||||
"dockerhub_list_size": 2,
|
||||
"github_exclude": "-",
|
||||
"last_update": "2026-07-24",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "epicgamesfree",
|
||||
"source": "dockerhub",
|
||||
"upstream_repo": "charlocharlie/epicgames-freegames",
|
||||
"upstream_version": "debian-2026-07-24"
|
||||
"upstream_version": "debian-2026-08-01"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
## 2.63.23 (2026-08-01)
|
||||
- Update to latest version from filebrowser/filebrowser (changelog : https://github.com/filebrowser/filebrowser/releases)
|
||||
## 2.63.18.5 (26-07-2026)
|
||||
- Fixed healthcheck log spam when ssl is enabled (#2881)
|
||||
## 2.63.18.4 (22-07-2026)
|
||||
|
||||
@@ -126,4 +126,4 @@ schema:
|
||||
slug: filebrowser
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "2.63.18.5"
|
||||
version: "2.63.23"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"github_beta": "true",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-08-01",
|
||||
"paused": false,
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "filebrowser",
|
||||
"source": "github",
|
||||
"upstream_repo": "filebrowser/filebrowser",
|
||||
"upstream_version": "2.63.18"
|
||||
"upstream_version": "2.63.23"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 3.19.31 (2026-08-01)
|
||||
- Update to latest version from wiserain/flexget
|
||||
|
||||
## 3.19.30 (2026-07-25)
|
||||
- Update to latest version from wiserain/flexget
|
||||
|
||||
|
||||
@@ -95,5 +95,5 @@ schema:
|
||||
slug: flexget
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "3.19.30"
|
||||
version: "3.19.31"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:5050]"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"dockerhub_list_size": "10",
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "flexget",
|
||||
"source": "dockerhub",
|
||||
"upstream_repo": "wiserain/flexget",
|
||||
"upstream_version": "3.19.30"
|
||||
"upstream_version": "3.19.31"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.27.1 (2026-08-01)
|
||||
- Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases)
|
||||
|
||||
## 1.27.0 (2026-07-22)
|
||||
- Update to latest version from go-gitea/gitea (changelog : https://github.com/go-gitea/gitea/releases)
|
||||
## 1.27 (15-07-2026)
|
||||
|
||||
@@ -97,5 +97,5 @@ schema:
|
||||
slug: gitea
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/gitea
|
||||
version: "1.27.0"
|
||||
version: "1.27.1"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:3000]"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "gitea",
|
||||
"source": "github",
|
||||
"upstream_repo": "go-gitea/gitea",
|
||||
"upstream_version": "1.27.0"
|
||||
"upstream_version": "1.27.1"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 26.7.1 (2026-08-01)
|
||||
- Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases)
|
||||
|
||||
## 26.7.0 (2026-07-22)
|
||||
- Update to latest version from gramps-project/gramps-web (changelog : https://github.com/gramps-project/gramps-web/releases)
|
||||
|
||||
|
||||
@@ -115,5 +115,5 @@ services:
|
||||
slug: grampsweb
|
||||
tmpfs: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/grampsweb
|
||||
version: "26.7.0"
|
||||
version: "26.7.1"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:5001]"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": true,
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "grampsweb",
|
||||
"source": "github",
|
||||
"upstream_repo": "gramps-project/gramps-web",
|
||||
"upstream_version": "26.7.0"
|
||||
"upstream_version": "26.7.1"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.0.14 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases)
|
||||
|
||||
## 2.0.12 (2026-07-22)
|
||||
- Update to latest version from linuxserver/docker-grav (changelog : https://github.com/linuxserver/docker-grav/releases)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
ARG BUILD_UPSTREAM="2.0.12"
|
||||
ARG BUILD_UPSTREAM="2.0.14"
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
##################
|
||||
|
||||
@@ -89,5 +89,5 @@ schema:
|
||||
slug: grav
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "2.0.12"
|
||||
version: "2.0.14"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:80]"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": false,
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "grav",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-grav",
|
||||
"upstream_version": "2.0.12"
|
||||
"upstream_version": "2.0.14"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 3.1.0 (2026-08-01)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
|
||||
## 3.0.3 (2026-07-22)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
## 3.0.1-2 (04-07-2026)
|
||||
|
||||
@@ -141,6 +141,6 @@ slug: immich
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "3.0.3"
|
||||
version: "3.1.0"
|
||||
video: true
|
||||
webui: http://[HOST]:[PORT:8080]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"github_beta": "false",
|
||||
"github_exclude": "",
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "immich",
|
||||
"source": "github",
|
||||
"upstream_repo": "immich-app/immich",
|
||||
"upstream_version": "3.0.3"
|
||||
"upstream_version": "3.1.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 3.1.0 (2026-08-01)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
|
||||
## 3.0.3 (2026-07-22)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
## 3.0.1-2 (04-07-2026)
|
||||
|
||||
@@ -139,6 +139,6 @@ slug: immich_cuda
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "3.0.3"
|
||||
version: "3.1.0"
|
||||
video: true
|
||||
webui: http://[HOST]:[PORT:8080]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"github_beta": "false",
|
||||
"github_exclude": "",
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "immich",
|
||||
"source": "github",
|
||||
"upstream_repo": "immich-app/immich",
|
||||
"upstream_version": "3.0.3"
|
||||
"upstream_version": "3.1.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 3.1.0 (2026-08-01)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
|
||||
## 3.0.3 (2026-07-22)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
## 3.0.1-2 (04-07-2026)
|
||||
|
||||
@@ -140,6 +140,6 @@ slug: immich_noml
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "3.0.3"
|
||||
version: "3.1.0"
|
||||
video: true
|
||||
webui: http://[HOST]:[PORT:8080]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"github_beta": "false",
|
||||
"github_exclude": "",
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "immich",
|
||||
"source": "github",
|
||||
"upstream_repo": "immich-app/immich",
|
||||
"upstream_version": "3.0.3"
|
||||
"upstream_version": "3.1.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 3.1.0 (2026-08-01)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
|
||||
## 3.0.3 (2026-07-22)
|
||||
- Update to latest version from immich-app/immich (changelog : https://github.com/immich-app/immich/releases)
|
||||
## 3.0.1-2 (04-07-2026)
|
||||
|
||||
@@ -140,6 +140,6 @@ slug: immich_openvino
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "3.0.3"
|
||||
version: "3.1.0"
|
||||
video: true
|
||||
webui: http://[HOST]:[PORT:8080]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"github_beta": "false",
|
||||
"github_exclude": "",
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "immich",
|
||||
"source": "github",
|
||||
"upstream_repo": "immich-app/immich",
|
||||
"upstream_version": "3.0.3"
|
||||
"upstream_version": "3.1.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
|
||||
## 0.24.2315 (2026-08-02)
|
||||
- Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases)
|
||||
|
||||
## 0.24.2307 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases)
|
||||
|
||||
## 0.24.2304 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases)
|
||||
|
||||
## 0.24.2265 (2026-07-25)
|
||||
- Update to latest version from linuxserver/docker-jackett (changelog : https://github.com/linuxserver/docker-jackett/releases)
|
||||
|
||||
|
||||
@@ -106,5 +106,5 @@ schema:
|
||||
slug: jackett_nas
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/jackett
|
||||
version: "0.24.2265"
|
||||
version: "0.24.2315"
|
||||
webui: http://[HOST]:[PORT:9117]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-02",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "jackett",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-jackett",
|
||||
"upstream_version": "0.24.2265"
|
||||
"upstream_version": "0.24.2315"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.4.6 (2026-08-01)
|
||||
- Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases)
|
||||
|
||||
## 2.4.5 (2026-07-25)
|
||||
- Update to latest version from linuxserver/docker-kometa (changelog : https://github.com/linuxserver/docker-kometa/releases)
|
||||
|
||||
|
||||
@@ -96,4 +96,4 @@ schema:
|
||||
slug: kometa
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/kometa
|
||||
version: "2.4.5"
|
||||
version: "2.4.6"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-25",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "kometa",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-kometa",
|
||||
"upstream_version": "2.4.5"
|
||||
"upstream_version": "2.4.6"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 3.1.0.4875.1 (2026-08-01)
|
||||
|
||||
- Version renamed from `3.1.0.4875-1`, which Home Assistant could not order and therefore could not reliably offer as an update: every number of the previous version is kept, as a section of its own. The addon itself and the upstream version it tracks are unchanged
|
||||
|
||||
|
||||
## 3.1.0.4875-1 (2026-01-08)
|
||||
- ⚠ MAJOR CHANGE : switch to the new config logic from homeassistant. Your configuration files will have migrated from /config/addons_config/lidarr to a folder only accessible from my Filebrowser addon called /addon_configs/xxx-lidarr_nas. This avoids the addon to mess with your homeassistant configuration folder, and allows to backup the options. Migration of data should be automatic. Please be sure to update all your links however ! For more information, see here : https://developers.home-assistant.io/blog/2023/11/06/public-addon-config/
|
||||
|
||||
@@ -102,5 +102,5 @@ schema:
|
||||
slug: lidarr_nas
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/blob/master/lidarr/README.md
|
||||
version: "3.1.0.4875-1"
|
||||
version: "3.1.0.4875.1"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:8686]"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
## 2.16.0 (2026-08-01)
|
||||
- Update to latest version from linkwarden/linkwarden (changelog : https://github.com/linkwarden/linkwarden/releases)
|
||||
## 2.15.1.2 (2026-07-22)
|
||||
- **Potential breaking change:** Create a full Home Assistant backup before upgrading. This release changes the Linkwarden application startup and database migration invocation.
|
||||
- Fix startup after Linkwarden 2.15 changed its production container layout.
|
||||
|
||||
@@ -45,5 +45,5 @@ schema:
|
||||
STORAGE_FOLDER: str?
|
||||
slug: linkwarden
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/linkwarden
|
||||
version: "2.15.1.2"
|
||||
version: "2.16.0"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:3000]"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-22",
|
||||
"last_update": "2026-08-01",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "linkwarden",
|
||||
"source": "github",
|
||||
"upstream_repo": "linkwarden/linkwarden",
|
||||
"upstream_version": "2.15.1"
|
||||
"upstream_version": "2.16.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
|
||||
## 3.21.1 (2026-08-02)
|
||||
- Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases)
|
||||
|
||||
## 3.21.0 (2026-08-01)
|
||||
- Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases)
|
||||
|
||||
## 3.20.1 (2026-08-01)
|
||||
- Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases)
|
||||
|
||||
## 3.18.0 (2026-07-22)
|
||||
- Update to latest version from maintainerr/maintainerr (changelog : https://github.com/maintainerr/maintainerr/releases)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user