mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-09-15 16:09:34 +00:00
Compare commits
11 Commits
need-updat
...
2024.06.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c37b5effd7 | ||
![]() |
ca7f3e8acb | ||
![]() |
b0cdb91d5e | ||
![]() |
4829eb8ae1 | ||
![]() |
1bb814b793 | ||
![]() |
918fcb7d62 | ||
![]() |
bbfd899564 | ||
![]() |
12c4d9da87 | ||
![]() |
6b4fd9b6b8 | ||
![]() |
07c22f4a60 | ||
![]() |
252e1e2ac0 |
8
.github/workflows/builder.yml
vendored
8
.github/workflows/builder.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
requirements: ${{ steps.requirements.outputs.changed }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
if: needs.init.outputs.publish == 'true'
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Initialize git
|
||||
if: needs.init.outputs.publish == 'true'
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Build the Supervisor
|
||||
if: needs.init.outputs.publish != 'true'
|
||||
|
20
.github/workflows/ci.yaml
vendored
20
.github/workflows/ci.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
name: Prepare Python dependencies
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python
|
||||
id: python
|
||||
uses: actions/setup-python@v5.1.0
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Register hadolint problem matcher
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -256,7 +256,7 @@ jobs:
|
||||
needs: prepare
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -288,7 +288,7 @@ jobs:
|
||||
name: Run tests Python ${{ needs.prepare.outputs.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -346,7 +346,7 @@ jobs:
|
||||
needs: ["pytest", "prepare"]
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
|
||||
uses: actions/setup-python@v5.1.0
|
||||
id: python
|
||||
@@ -373,4 +373,4 @@ jobs:
|
||||
coverage report
|
||||
coverage xml
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4.4.1
|
||||
uses: codecov/codecov-action@v4.5.0
|
||||
|
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Release Drafter
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
2
.github/workflows/sentry.yaml
vendored
2
.github/workflows/sentry.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v4.1.6
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Sentry Release
|
||||
uses: getsentry/action-release@v1.7.0
|
||||
env:
|
||||
|
@@ -22,8 +22,8 @@ pyudev==0.24.3
|
||||
PyYAML==6.0.1
|
||||
requests==2.32.3
|
||||
securetar==2024.2.1
|
||||
sentry-sdk==2.5.1
|
||||
setuptools==70.0.0
|
||||
sentry-sdk==2.6.0
|
||||
setuptools==70.1.0
|
||||
voluptuous==0.14.2
|
||||
dbus-fast==2.21.3
|
||||
typing_extensions==4.12.2
|
||||
|
@@ -6,7 +6,7 @@ pytest-asyncio==0.23.6
|
||||
pytest-cov==5.0.0
|
||||
pytest-timeout==2.3.1
|
||||
pytest==8.2.2
|
||||
ruff==0.4.8
|
||||
ruff==0.4.10
|
||||
time-machine==2.14.1
|
||||
typing_extensions==4.12.2
|
||||
urllib3==2.2.1
|
||||
urllib3==2.2.2
|
||||
|
@@ -174,17 +174,6 @@ class Tasks(CoreSysAttributes):
|
||||
self._cache[HASS_WATCHDOG_API_FAILURES] = 0
|
||||
return
|
||||
|
||||
# Give up after 5 reanimation failures in a row. Supervisor cannot fix this issue.
|
||||
reanimate_fails = self._cache.get(HASS_WATCHDOG_REANIMATE_FAILURES, 0)
|
||||
if reanimate_fails >= HASS_WATCHDOG_MAX_REANIMATE_ATTEMPTS:
|
||||
if reanimate_fails == HASS_WATCHDOG_MAX_REANIMATE_ATTEMPTS:
|
||||
_LOGGER.critical(
|
||||
"Watchdog cannot reanimate Home Assistant Core, failed all %s attempts.",
|
||||
reanimate_fails,
|
||||
)
|
||||
self._cache[HASS_WATCHDOG_REANIMATE_FAILURES] += 1
|
||||
return
|
||||
|
||||
# Init cache data
|
||||
api_fails = self._cache.get(HASS_WATCHDOG_API_FAILURES, 0)
|
||||
|
||||
@@ -195,16 +184,38 @@ class Tasks(CoreSysAttributes):
|
||||
_LOGGER.warning("Watchdog missed an Home Assistant Core API response.")
|
||||
return
|
||||
|
||||
_LOGGER.error(
|
||||
"Watchdog missed %s Home Assistant Core API responses in a row. Restarting Home Assistant Core API!",
|
||||
HASS_WATCHDOG_MAX_API_ATTEMPTS,
|
||||
)
|
||||
# After 5 reanimation attempts switch to safe mode. If that fails, give up
|
||||
reanimate_fails = self._cache.get(HASS_WATCHDOG_REANIMATE_FAILURES, 0)
|
||||
if reanimate_fails > HASS_WATCHDOG_MAX_REANIMATE_ATTEMPTS:
|
||||
return
|
||||
|
||||
if safe_mode := reanimate_fails == HASS_WATCHDOG_MAX_REANIMATE_ATTEMPTS:
|
||||
_LOGGER.critical(
|
||||
"Watchdog cannot reanimate Home Assistant Core, failed all %s attempts. Restarting into safe mode",
|
||||
reanimate_fails,
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Watchdog missed %s Home Assistant Core API responses in a row. Restarting Home Assistant Core!",
|
||||
HASS_WATCHDOG_MAX_API_ATTEMPTS,
|
||||
)
|
||||
|
||||
try:
|
||||
await self.sys_homeassistant.core.restart()
|
||||
if safe_mode:
|
||||
await self.sys_homeassistant.core.rebuild(safe_mode=True)
|
||||
else:
|
||||
await self.sys_homeassistant.core.restart()
|
||||
except HomeAssistantError as err:
|
||||
_LOGGER.error("Home Assistant watchdog reanimation failed!")
|
||||
if reanimate_fails == 0:
|
||||
if reanimate_fails == 0 or safe_mode:
|
||||
capture_exception(err)
|
||||
|
||||
if safe_mode:
|
||||
_LOGGER.critical(
|
||||
"Safe mode restart failed. Watchdog cannot bring Home Assistant online."
|
||||
)
|
||||
else:
|
||||
_LOGGER.error("Home Assistant watchdog reanimation failed!")
|
||||
|
||||
self._cache[HASS_WATCHDOG_REANIMATE_FAILURES] = reanimate_fails + 1
|
||||
else:
|
||||
self._cache[HASS_WATCHDOG_REANIMATE_FAILURES] = 0
|
||||
|
@@ -26,7 +26,7 @@ class FixupAddonExecuteRemove(FixupBase):
|
||||
# Remove addon
|
||||
_LOGGER.info("Remove addon: %s", reference)
|
||||
try:
|
||||
addon.uninstall()
|
||||
await addon.uninstall(remove_config=False)
|
||||
except AddonsError as err:
|
||||
_LOGGER.error("Could not remove %s due to %s", reference, err)
|
||||
raise ResolutionFixupError() from None
|
||||
|
@@ -8,11 +8,13 @@ from .const import StoreType
|
||||
|
||||
URL_COMMUNITY_ADDONS = "https://github.com/hassio-addons/repository"
|
||||
URL_ESPHOME = "https://github.com/esphome/home-assistant-addon"
|
||||
URL_MUSIC_ASSISTANT = "https://github.com/music-assistant/home-assistant-addon"
|
||||
BUILTIN_REPOSITORIES = {
|
||||
StoreType.CORE,
|
||||
StoreType.LOCAL,
|
||||
URL_COMMUNITY_ADDONS,
|
||||
URL_ESPHOME,
|
||||
URL_MUSIC_ASSISTANT,
|
||||
}
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
|
5
tests/fixtures/addons/git/d5369777/repository.json
vendored
Normal file
5
tests/fixtures/addons/git/d5369777/repository.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Music Assistant",
|
||||
"url": "https://github.com/music-assistant/core",
|
||||
"maintainer": "Music Assistant <marcelveldt@users.noreply.github.com>"
|
||||
}
|
@@ -46,7 +46,7 @@ async def test_watchdog_homeassistant_api(
|
||||
restart.assert_called_once()
|
||||
assert "Watchdog missed an Home Assistant Core API response." not in caplog.text
|
||||
assert (
|
||||
"Watchdog missed 2 Home Assistant Core API responses in a row. Restarting Home Assistant Core API!"
|
||||
"Watchdog missed 2 Home Assistant Core API responses in a row. Restarting Home Assistant Core!"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
@@ -109,31 +109,48 @@ async def test_watchdog_homeassistant_api_reanimation_limit(
|
||||
HomeAssistantAPI, "check_api_state", return_value=False
|
||||
), patch.object(
|
||||
HomeAssistantCore, "restart", side_effect=(err := HomeAssistantError())
|
||||
) as restart:
|
||||
) as restart, patch.object(
|
||||
HomeAssistantCore, "rebuild", side_effect=err
|
||||
) as rebuild:
|
||||
for _ in range(5):
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
restart.assert_not_called()
|
||||
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
restart.assert_called_once()
|
||||
restart.assert_called_once_with()
|
||||
assert "Home Assistant watchdog reanimation failed!" in caplog.text
|
||||
|
||||
rebuild.assert_not_called()
|
||||
restart.reset_mock()
|
||||
|
||||
capture_exception.assert_called_once_with(err)
|
||||
|
||||
# Next time it should try safe mode
|
||||
caplog.clear()
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
rebuild.assert_not_called()
|
||||
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
|
||||
rebuild.assert_called_once_with(safe_mode=True)
|
||||
restart.assert_not_called()
|
||||
assert "Watchdog missed an Home Assistant Core API response." not in caplog.text
|
||||
assert "Watchdog found a problem with Home Assistant API!" not in caplog.text
|
||||
assert (
|
||||
"Watchdog cannot reanimate Home Assistant Core, failed all 5 attempts."
|
||||
"Watchdog cannot reanimate Home Assistant Core, failed all 5 attempts. Restarting into safe mode"
|
||||
in caplog.text
|
||||
)
|
||||
assert (
|
||||
"Safe mode restart failed. Watchdog cannot bring Home Assistant online."
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
# After safe mode has failed too, no more restart attempts
|
||||
rebuild.reset_mock()
|
||||
caplog.clear()
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
assert "Watchdog missed an Home Assistant Core API response." in caplog.text
|
||||
|
||||
caplog.clear()
|
||||
await tasks._watchdog_homeassistant_api()
|
||||
restart.assert_not_called()
|
||||
assert not caplog.text
|
||||
restart.assert_not_called()
|
||||
rebuild.assert_not_called()
|
||||
|
@@ -163,6 +163,7 @@ async def test_preinstall_valid_repository(
|
||||
assert store_manager.get("local").validate()
|
||||
assert store_manager.get("a0d7b954").validate()
|
||||
assert store_manager.get("5c53de3b").validate()
|
||||
assert store_manager.get("d5369777").validate()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("use_update", [True, False])
|
||||
|
@@ -39,11 +39,11 @@ async def test_default_load(coresys: CoreSys):
|
||||
):
|
||||
await store_manager.load()
|
||||
|
||||
assert len(store_manager.all) == 4
|
||||
assert len(store_manager.all) == 5
|
||||
assert isinstance(store_manager.get("core"), Repository)
|
||||
assert isinstance(store_manager.get("local"), Repository)
|
||||
|
||||
assert len(store_manager.repository_urls) == 2
|
||||
assert len(store_manager.repository_urls) == 3
|
||||
assert (
|
||||
"https://github.com/hassio-addons/repository" in store_manager.repository_urls
|
||||
)
|
||||
@@ -51,6 +51,11 @@ async def test_default_load(coresys: CoreSys):
|
||||
"https://github.com/esphome/home-assistant-addon"
|
||||
in store_manager.repository_urls
|
||||
)
|
||||
assert (
|
||||
"https://github.com/music-assistant/home-assistant-addon"
|
||||
in store_manager.repository_urls
|
||||
)
|
||||
# NOTE: When adding new stores, make sure to add it to tests/fixtures/addons/git/
|
||||
assert refresh_cache_calls == {"local_ssh", "local_example", "core_samba"}
|
||||
|
||||
|
||||
@@ -77,11 +82,11 @@ async def test_load_with_custom_repository(coresys: CoreSys):
|
||||
):
|
||||
await store_manager.load()
|
||||
|
||||
assert len(store_manager.all) == 5
|
||||
assert len(store_manager.all) == 6
|
||||
assert isinstance(store_manager.get("core"), Repository)
|
||||
assert isinstance(store_manager.get("local"), Repository)
|
||||
|
||||
assert len(store_manager.repository_urls) == 3
|
||||
assert len(store_manager.repository_urls) == 4
|
||||
assert (
|
||||
"https://github.com/hassio-addons/repository" in store_manager.repository_urls
|
||||
)
|
||||
@@ -89,6 +94,10 @@ async def test_load_with_custom_repository(coresys: CoreSys):
|
||||
"https://github.com/esphome/home-assistant-addon"
|
||||
in store_manager.repository_urls
|
||||
)
|
||||
assert (
|
||||
"https://github.com/music-assistant/home-assistant-addon"
|
||||
in store_manager.repository_urls
|
||||
)
|
||||
assert "http://example.com" in store_manager.repository_urls
|
||||
|
||||
|
||||
@@ -105,11 +114,11 @@ async def test_load_from_core_config(coresys: CoreSys):
|
||||
), patch("pathlib.Path.exists", return_value=True):
|
||||
await coresys.store.load()
|
||||
|
||||
assert len(coresys.store.all) == 5
|
||||
assert len(coresys.store.all) == 6
|
||||
assert isinstance(coresys.store.get("core"), Repository)
|
||||
assert isinstance(coresys.store.get("local"), Repository)
|
||||
|
||||
assert len(coresys.store.repository_urls) == 3
|
||||
assert len(coresys.store.repository_urls) == 4
|
||||
assert (
|
||||
"https://github.com/hassio-addons/repository" in coresys.store.repository_urls
|
||||
)
|
||||
@@ -117,6 +126,10 @@ async def test_load_from_core_config(coresys: CoreSys):
|
||||
"https://github.com/esphome/home-assistant-addon"
|
||||
in coresys.store.repository_urls
|
||||
)
|
||||
assert (
|
||||
"https://github.com/music-assistant/home-assistant-addon"
|
||||
in coresys.store.repository_urls
|
||||
)
|
||||
assert "http://example.com" in coresys.store.repository_urls
|
||||
|
||||
assert coresys.config.addons_repositories == []
|
||||
@@ -243,12 +256,12 @@ async def test_install_unavailable_addon(
|
||||
async def test_reload(coresys: CoreSys):
|
||||
"""Test store reload."""
|
||||
await coresys.store.load()
|
||||
assert len(coresys.store.all) == 4
|
||||
assert len(coresys.store.all) == 5
|
||||
|
||||
with patch.object(GitRepo, "pull") as git_pull:
|
||||
await coresys.store.reload()
|
||||
|
||||
assert git_pull.call_count == 3
|
||||
assert git_pull.call_count == 4
|
||||
|
||||
|
||||
async def test_addon_version_timestamp(coresys: CoreSys, install_addon_example: Addon):
|
||||
|
@@ -49,12 +49,13 @@ async def test_repository_validate(repo_list: list[str], valid: bool):
|
||||
"""Test repository list validate."""
|
||||
if valid:
|
||||
processed = repositories(repo_list)
|
||||
assert len(processed) == 4
|
||||
assert len(processed) == 5
|
||||
assert set(repositories(repo_list)) == {
|
||||
"core",
|
||||
"local",
|
||||
"https://github.com/hassio-addons/repository",
|
||||
"https://github.com/esphome/home-assistant-addon",
|
||||
"https://github.com/music-assistant/home-assistant-addon",
|
||||
}
|
||||
else:
|
||||
with pytest.raises(Invalid):
|
||||
|
Reference in New Issue
Block a user