Compare commits

..

18 Commits

Author SHA1 Message Date
Stefan Agner
2c3e06231a Avoid adding Content-Type to non-body responses
The current code sets the content-type header for all responses
to the result's content_type property if upstream does not set a
content_type. The default value for content_type is
"application/octet-stream".

For responses that do not have a body (like 204 No Content or
304 Not Modified), setting a content-type header is unnecessary and
potentially misleading. Follow HTTP standards by only adding the
content-type header to responses that actually contain a body.
2025-10-23 18:58:53 +02:00
dependabot[bot]
b903e1196f Bump sigstore/cosign-installer from 3.10.0 to 4.0.0 (#6256)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.10.0 to 4.0.0.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](d7543c93d8...faadad0cce)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:37:59 +02:00
dependabot[bot]
9f8e8ab15a Bump home-assistant/wheels from 2025.09.1 to 2025.10.0 (#6260)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-22 23:17:38 +02:00
dependabot[bot]
56bffc839b Bump aiohttp from 3.13.0 to 3.13.1 (#6261)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-22 23:14:01 +02:00
dependabot[bot]
952a553c3b Bump pylint from 4.0.1 to 4.0.2 (#6263) 2025-10-22 10:20:35 +02:00
dependabot[bot]
717f1c85f5 Bump sentry-sdk from 2.42.0 to 2.42.1 (#6264)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.42.0 to 2.42.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.42.0...2.42.1)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.42.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-22 09:25:14 +02:00
dependabot[bot]
ffd498a515 Bump sentry-sdk from 2.41.0 to 2.42.0 (#6253)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-18 23:30:45 +02:00
dependabot[bot]
35f0645cb9 Bump cryptography from 46.0.2 to 46.0.3 (#6255)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-18 22:04:32 +02:00
dependabot[bot]
15c6547382 Bump coverage from 7.10.7 to 7.11.0 (#6254)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 23:42:02 +02:00
dependabot[bot]
adefa242e5 Bump colorlog from 6.9.0 to 6.10.1 (#6258)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 09:14:38 +02:00
dependabot[bot]
583a8a82fb Bump ruff from 0.14.0 to 0.14.1 (#6257)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 09:12:47 +02:00
dependabot[bot]
322df15e73 Bump pylint from 4.0.0 to 4.0.1 (#6251)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-15 19:58:36 +02:00
dependabot[bot]
51490c8e41 Bump pylint from 3.3.9 to 4.0.0 and astroid from 3.3.11 to 4.0.1 (#6248)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
2025-10-14 10:45:17 +02:00
dependabot[bot]
3c21a8b8ef Bump sentry-sdk from 2.40.0 to 2.41.0 (#6246)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.40.0 to 2.41.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.40.0...2.41.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-10 08:38:17 +02:00
Jan Čermák
ddb8588d77 Treat containerd snapshotter/overlayfs driver as supported (#6242)
* Treat containerd snapshotter/overlayfs driver as supported

With home-assistant/operating-system#4252 the storage driver would
change to "overlayfs". We don't want the system to be marked as
unsupported. It should be safe to treat it as supported even now, so add
it to the list of allowed values.

* Flip the logic

(note for self: don't forget to check for unstaged changes before push)

* Set valid storage for invalid logging test case
2025-10-09 18:47:06 +02:00
dependabot[bot]
81e46b20b8 Bump pyudev from 0.24.3 to 0.24.4 (#6244)
Bumps [pyudev](https://github.com/pyudev/pyudev) from 0.24.3 to 0.24.4.
- [Changelog](https://github.com/pyudev/pyudev/blob/master/CHANGES.rst)
- [Commits](https://github.com/pyudev/pyudev/compare/v0.24.3...v0.24.4)

---
updated-dependencies:
- dependency-name: pyudev
  dependency-version: 0.24.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-09 09:48:34 +02:00
dependabot[bot]
5041a1ed5c Bump types-docker from 7.1.0.20250916 to 7.1.0.20251009 (#6243)
Bumps [types-docker](https://github.com/typeshed-internal/stub_uploader) from 7.1.0.20250916 to 7.1.0.20251009.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-docker
  dependency-version: 7.1.0.20251009
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-09 09:38:12 +02:00
Stefan Agner
337731a55a Let only bugs go stale (#6239)
Let's apply the stale label only to issues which are bugs. Tasks are
more long lived and should not be marked as stale automatically.
2025-10-08 15:56:35 +02:00
10 changed files with 132 additions and 25 deletions

View File

@@ -107,7 +107,7 @@ jobs:
# home-assistant/wheels doesn't support sha pinning
- name: Build wheels
if: needs.init.outputs.requirements == 'true'
uses: home-assistant/wheels@2025.09.1
uses: home-assistant/wheels@2025.10.0
with:
abi: cp313
tag: musllinux_1_2
@@ -132,7 +132,7 @@ jobs:
- name: Install Cosign
if: needs.init.outputs.publish == 'true'
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.5.3"

View File

@@ -346,7 +346,7 @@ jobs:
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.5.3"
- name: Restore Python virtual environment

View File

@@ -16,6 +16,7 @@ jobs:
days-before-close: 7
stale-issue-label: "stale"
exempt-issue-labels: "no-stale,Help%20wanted,help-wanted,pinned,rfc,security"
only-issue-types: "bug"
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
high number of incoming GitHub notifications, we have to clean some

View File

@@ -1,14 +1,14 @@
aiodns==3.5.0
aiohttp==3.13.0
aiohttp==3.13.1
atomicwrites-homeassistant==1.4.1
attrs==25.4.0
awesomeversion==25.8.0
blockbuster==1.5.25
brotli==1.1.0
ciso8601==2.3.3
colorlog==6.9.0
colorlog==6.10.1
cpe==1.3.1
cryptography==46.0.2
cryptography==46.0.3
debugpy==1.8.17
deepmerge==2.0
dirhash==0.5.0
@@ -19,11 +19,11 @@ jinja2==3.1.6
log-rate-limit==1.4.2
orjson==3.11.3
pulsectl==24.12.0
pyudev==0.24.3
pyudev==0.24.4
PyYAML==6.0.3
requests==2.32.5
securetar==2025.2.1
sentry-sdk==2.40.0
sentry-sdk==2.42.1
setuptools==80.9.0
voluptuous==0.15.2
dbus-fast==2.44.5

View File

@@ -1,16 +1,16 @@
astroid==3.3.11
coverage==7.10.7
astroid==4.0.1
coverage==7.11.0
mypy==1.18.2
pre-commit==4.3.0
pylint==3.3.9
pylint==4.0.2
pytest-aiohttp==1.1.0
pytest-asyncio==0.25.2
pytest-cov==7.0.0
pytest-timeout==2.4.0
pytest==8.4.2
ruff==0.14.0
ruff==0.14.1
time-machine==2.19.0
types-docker==7.1.0.20250916
types-docker==7.1.0.20251009
types-pyyaml==6.0.12.20250915
types-requests==2.32.4.20250913
urllib3==2.5.0

View File

@@ -253,6 +253,16 @@ class APIIngress(CoreSysAttributes):
skip_auto_headers={hdrs.CONTENT_TYPE},
) as result:
headers = _response_header(result)
# Empty body responses (304, 204, HEAD, etc.) should not be streamed,
# otherwise aiohttp < 3.9.0 may generate an invalid "0\r\n\r\n" chunk
# This also avoids setting content_type for empty responses.
if must_be_empty_body(request.method, result.status):
return web.Response(
headers=headers,
status=result.status,
)
# Avoid parsing content_type in simple cases for better performance
if maybe_content_type := result.headers.get(hdrs.CONTENT_TYPE):
content_type = (maybe_content_type.partition(";"))[0].strip()
@@ -260,11 +270,7 @@ class APIIngress(CoreSysAttributes):
content_type = result.content_type
# Simple request
if (
# empty body responses should not be streamed,
# otherwise aiohttp < 3.9.0 may generate
# an invalid "0\r\n\r\n" chunk instead of an empty response.
must_be_empty_body(request.method, result.status)
or hdrs.CONTENT_LENGTH in result.headers
hdrs.CONTENT_LENGTH in result.headers
and int(result.headers.get(hdrs.CONTENT_LENGTH, 0)) < 4_194_000
):
# Return Response

View File

@@ -108,7 +108,8 @@ class APISupervisor(CoreSysAttributes):
ATTR_AUTO_UPDATE: self.sys_updater.auto_update,
ATTR_DETECT_BLOCKING_IO: BlockBusterManager.is_enabled(),
ATTR_COUNTRY: self.sys_config.country,
# Deprecated
# Depricated
ATTR_WAIT_BOOT: self.sys_config.wait_boot,
ATTR_ADDONS: [
{
ATTR_NAME: addon.name,
@@ -122,6 +123,10 @@ class APISupervisor(CoreSysAttributes):
}
for addon in self.sys_addons.local.values()
],
ATTR_ADDONS_REPOSITORIES: [
{ATTR_NAME: store.name, ATTR_SLUG: store.slug}
for store in self.sys_store.all
],
}
@api_process
@@ -177,10 +182,20 @@ class APISupervisor(CoreSysAttributes):
self.sys_config.detect_blocking_io = False
BlockBusterManager.deactivate()
# Deprecated
if ATTR_WAIT_BOOT in body:
self.sys_config.wait_boot = body[ATTR_WAIT_BOOT]
# Save changes before processing addons in case of errors
await self.sys_updater.save_data()
await self.sys_config.save_data()
# Remove: 2022.9
if ATTR_ADDONS_REPOSITORIES in body:
await asyncio.shield(
self.sys_store.update_repositories(set(body[ATTR_ADDONS_REPOSITORIES]))
)
await self.sys_resolution.evaluate.evaluate_system()
@api_process

View File

@@ -8,7 +8,7 @@ from ..const import UnsupportedReason
from .base import EvaluateBase
EXPECTED_LOGGING = "journald"
EXPECTED_STORAGE = "overlay2"
EXPECTED_STORAGE = ("overlay2", "overlayfs")
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -41,14 +41,18 @@ class EvaluateDockerConfiguration(EvaluateBase):
storage_driver = self.sys_docker.info.storage
logging_driver = self.sys_docker.info.logging
if storage_driver != EXPECTED_STORAGE:
is_unsupported = False
if storage_driver not in EXPECTED_STORAGE:
is_unsupported = True
_LOGGER.warning(
"Docker storage driver %s is not supported!", storage_driver
)
if logging_driver != EXPECTED_LOGGING:
is_unsupported = True
_LOGGER.warning(
"Docker logging driver %s is not supported!", logging_driver
)
return storage_driver != EXPECTED_STORAGE or logging_driver != EXPECTED_LOGGING
return is_unsupported

View File

@@ -12,8 +12,9 @@ import pytest
from supervisor.const import CoreState
from supervisor.core import Core
from supervisor.coresys import CoreSys
from supervisor.exceptions import HassioError, HostNotSupportedError
from supervisor.exceptions import HassioError, HostNotSupportedError, StoreGitError
from supervisor.homeassistant.const import WSEvent
from supervisor.store.repository import Repository
from supervisor.supervisor import Supervisor
from supervisor.updater import Updater
@@ -34,6 +35,81 @@ async def test_api_supervisor_options_debug(api_client: TestClient, coresys: Cor
assert coresys.config.debug
async def test_api_supervisor_options_add_repository(
api_client: TestClient, coresys: CoreSys, supervisor_internet: AsyncMock
):
"""Test add a repository via POST /supervisor/options REST API."""
assert REPO_URL not in coresys.store.repository_urls
with (
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
patch("supervisor.store.repository.RepositoryGit.validate", return_value=True),
):
response = await api_client.post(
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
)
assert response.status == 200
assert REPO_URL in coresys.store.repository_urls
async def test_api_supervisor_options_remove_repository(
api_client: TestClient, coresys: CoreSys, test_repository: Repository
):
"""Test remove a repository via POST /supervisor/options REST API."""
assert test_repository.source in coresys.store.repository_urls
assert test_repository.slug in coresys.store.repositories
response = await api_client.post(
"/supervisor/options", json={"addons_repositories": []}
)
assert response.status == 200
assert test_repository.source not in coresys.store.repository_urls
assert test_repository.slug not in coresys.store.repositories
@pytest.mark.parametrize("git_error", [None, StoreGitError()])
async def test_api_supervisor_options_repositories_skipped_on_error(
api_client: TestClient, coresys: CoreSys, git_error: StoreGitError
):
"""Test repositories skipped on error via POST /supervisor/options REST API."""
with (
patch("supervisor.store.repository.RepositoryGit.load", side_effect=git_error),
patch("supervisor.store.repository.RepositoryGit.validate", return_value=False),
patch("supervisor.store.repository.RepositoryCustom.remove"),
):
response = await api_client.post(
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
)
assert response.status == 400
assert len(coresys.resolution.suggestions) == 0
assert REPO_URL not in coresys.store.repository_urls
async def test_api_supervisor_options_repo_error_with_config_change(
api_client: TestClient, coresys: CoreSys
):
"""Test config change with add repository error via POST /supervisor/options REST API."""
assert not coresys.config.debug
with patch(
"supervisor.store.repository.RepositoryGit.load", side_effect=StoreGitError()
):
response = await api_client.post(
"/supervisor/options",
json={"debug": True, "addons_repositories": [REPO_URL]},
)
assert response.status == 400
assert REPO_URL not in coresys.store.repository_urls
assert coresys.config.debug
coresys.updater.save_data.assert_called_once()
coresys.config.save_data.assert_called_once()
async def test_api_supervisor_options_auto_update(
api_client: TestClient, coresys: CoreSys
):

View File

@@ -25,13 +25,18 @@ async def test_evaluation(coresys: CoreSys):
assert docker_configuration.reason in coresys.resolution.unsupported
coresys.resolution.unsupported.clear()
coresys.docker.info.storage = EXPECTED_STORAGE
coresys.docker.info.storage = EXPECTED_STORAGE[0]
coresys.docker.info.logging = "unsupported"
await docker_configuration()
assert docker_configuration.reason in coresys.resolution.unsupported
coresys.resolution.unsupported.clear()
coresys.docker.info.storage = EXPECTED_STORAGE
coresys.docker.info.storage = "overlay2"
coresys.docker.info.logging = EXPECTED_LOGGING
await docker_configuration()
assert docker_configuration.reason not in coresys.resolution.unsupported
coresys.docker.info.storage = "overlayfs"
coresys.docker.info.logging = EXPECTED_LOGGING
await docker_configuration()
assert docker_configuration.reason not in coresys.resolution.unsupported