Compare commits

...

3 Commits

Author SHA1 Message Date
Stefan Agner
3557f97cd1 Bump devcontainer to 6
With devcontainer 6 dbus-daemon is installed in the container, which
is required for tests. The latest version also has support to disable
AppArmor using the `SUPERVISOR_UNCONFINED` environment variable.
2026-04-17 17:30:05 +02:00
Stefan Agner
38ddb3df54 Fix Core update rollback: delay image cleanup and fix missing rollback path (#6726)
* Delay old image cleanup until after health checks on Core update

Move the old Docker image cleanup from inside _update() to after the
post-update health checks (frontend loaded and accessible). This keeps
the previous version's image available locally when a rollback is
needed, avoiding a potentially slow re-download.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add test assertions for old image cleanup timing on Core update

Verify that the old Docker image is cleaned up only after health checks
pass, and not when a rollback is triggered.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix missing rollback when get_config fails after Core update

The early return after setting error_state skipped the rollback block,
leaving the system on a broken new version when the API stopped
responding after update. The other health check failure paths correctly
fall through to the rollback logic; this was the only one that didn't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 10:57:13 +02:00
dependabot[bot]
0db56b09ce Bump ruff from 0.15.10 to 0.15.11 (#6746)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.10 to 0.15.11.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.15.10...0.15.11)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.11
  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>
2026-04-17 10:56:49 +02:00
4 changed files with 11 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "Supervisor dev",
"image": "ghcr.io/home-assistant/devcontainer:5-supervisor",
"image": "ghcr.io/home-assistant/devcontainer:6-supervisor",
"overrideCommand": false,
"remoteUser": "vscode",
"containerEnv": {

View File

@@ -8,7 +8,7 @@ pytest-asyncio==1.3.0
pytest-cov==7.1.0
pytest-timeout==2.4.0
pytest==9.0.3
ruff==0.15.10
ruff==0.15.11
time-machine==3.2.0
types-pyyaml==6.0.12.20260408
urllib3==2.6.3

View File

@@ -321,8 +321,6 @@ class HomeAssistantCore(JobGroup):
# Successfull - last step
await self.sys_homeassistant.save_data()
with suppress(DockerError):
await self.instance.cleanup(old_image=old_image)
# Update Home Assistant
with suppress(HomeAssistantError):
@@ -332,9 +330,8 @@ class HomeAssistantCore(JobGroup):
try:
data = await self.sys_homeassistant.api.get_config()
except HomeAssistantError:
# The API stoped responding between the up checks an now
# The API stopped responding between the update and now
self._error_state = True
return
# Verify that the frontend is loaded
if "frontend" not in data.get("components", []):
@@ -347,6 +344,9 @@ class HomeAssistantCore(JobGroup):
)
self._error_state = True
else:
# Health checks passed, clean up old image
with suppress(DockerError):
await self.instance.cleanup(old_image=old_image)
return
# Update going wrong, revert it

View File

@@ -458,10 +458,12 @@ async def test_update_frontend_check_success(api_client: TestClient, coresys: Co
HomeAssistantAPI, "get_config", return_value={"components": ["frontend"]}
),
patch.object(HomeAssistantAPI, "check_frontend_available", return_value=True),
patch.object(DockerInterface, "cleanup") as mock_cleanup,
):
resp = await api_client.post("/core/update", json={"version": "2025.8.3"})
assert resp.status == 200
mock_cleanup.assert_called_once()
async def test_update_frontend_check_fails_triggers_rollback(
@@ -498,6 +500,7 @@ async def test_update_frontend_check_fails_triggers_rollback(
HomeAssistantAPI, "get_config", return_value={"components": ["frontend"]}
),
patch.object(HomeAssistantAPI, "check_frontend_available", return_value=False),
patch.object(DockerInterface, "cleanup") as mock_cleanup,
):
resp = await api_client.post("/core/update", json={"version": "2025.8.3"})
@@ -511,3 +514,5 @@ async def test_update_frontend_check_fails_triggers_rollback(
assert (
Issue(IssueType.UPDATE_ROLLBACK, ContextType.CORE) in coresys.resolution.issues
)
# Old image should not be cleaned up so rollback doesn't need to re-download
mock_cleanup.assert_not_called()