ha-deploy-kit 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/CHANGELOG.md +13 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/PKG-INFO +2 -2
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/README.md +1 -1
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/pyproject.toml +1 -1
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/ha/repairs_flow.py +7 -14
- ha_deploy_kit-0.1.2/src/ha_deploy_kit/metadata/strings.snippet.fixflow.json +15 -0
- ha_deploy_kit-0.1.2/tests/ha/test_repairs_flow.py +35 -0
- ha_deploy_kit-0.1.1/src/ha_deploy_kit/metadata/strings.snippet.fixflow.json +0 -21
- ha_deploy_kit-0.1.1/tests/ha/test_repairs_flow.py +0 -54
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/.coveragerc-ha +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/.github/workflows/release.yml +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/.github/workflows/validate.yml +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/.gitignore +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/LICENSE +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/docs/adopting.md +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/docs/configuration.md +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/docs/install.md +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/docs/releasing.md +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/backend_only/.env.example +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/backend_only/deploy.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/backend_only/deploy.sh +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/frontend_card/.env.example +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/frontend_card/deploy.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/frontend_card/deploy.sh +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/frontend_card/ha_adapter.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/synthetic_widget/deploy.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/examples/synthetic_widget/ha_adapter.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/__init__.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/cli.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/comparison.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/config.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/frontend.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/ha/__init__.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/ha/repairs_issue.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/ha/service.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/ha/serving.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/inventory.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/metadata/services.yaml.tmpl +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/metadata/strings.snippet.json +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/notify.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/settings.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/src/ha_deploy_kit/transport.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/conftest.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/conftest.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/test_example_adapters.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/test_real_http_to_service.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/test_repairs_issue.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/test_service.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/ha/test_serving.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_cli.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_comparison.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_config.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_frontend.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_installed_smoke.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_inventory.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_multi_consumer.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_notify.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_packaging.py +0 -0
- {ha_deploy_kit-0.1.1 → ha_deploy_kit-0.1.2}/tests/test_transport.py +0 -0
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.1.2 (2026-10-29)
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- **ha**: Confirm restart via a dialog instead of a checkbox
|
|
9
|
+
([`aeaf825`](https://github.com/latrekc-meta/ha-deploy-kit/commit/aeaf82541dced2e3e77828b97f28c34dab5e361a))
|
|
10
|
+
|
|
11
|
+
The checkbox step added friction without benefit: opening the Repairs issue already shows a
|
|
12
|
+
confirmation dialog, and the restart should fire when the user submits it. Drop the required
|
|
13
|
+
confirm checkbox and the confirm_required error; restart only on submit of the dialog. Trim the
|
|
14
|
+
fix-flow strings snippet back to title + description.
|
|
15
|
+
|
|
16
|
+
|
|
4
17
|
## v0.1.1 (2026-10-29)
|
|
5
18
|
|
|
6
19
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ha-deploy-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: HA-free SSH deploy / compare / notify / optional-frontend toolkit for Home Assistant custom components
|
|
5
5
|
Project-URL: Homepage, https://github.com/latrekc-meta/ha-deploy-kit
|
|
6
6
|
Project-URL: Repository, https://github.com/latrekc-meta/ha-deploy-kit
|
|
@@ -110,7 +110,7 @@ how releases are published.
|
|
|
110
110
|
| `ha.async_register_restart_service(hass, domain, ...)` | `ha_deploy_kit.ha` | Idempotently register the restart service (empty schema). Pass `fixable=True` for a one-click Repairs card (pair with the fix flow). |
|
|
111
111
|
| `ha.async_raise_restart_issue(hass, domain, ...)` | `ha_deploy_kit.ha` | Raise the deduplicated, non-persistent restart Repairs issue. |
|
|
112
112
|
| `ha.async_clear_restart_issue(hass, domain, ...)` | `ha_deploy_kit.ha` | Clear the restart issue. |
|
|
113
|
-
| `ha.async_create_restart_fix_flow(hass, issue_id, ...)` | `ha_deploy_kit.ha` | Optional opt-in confirm-and-restart flow (restart only
|
|
113
|
+
| `ha.async_create_restart_fix_flow(hass, issue_id, ...)` | `ha_deploy_kit.ha` | Optional opt-in confirm-and-restart flow (shows a confirmation dialog; restart only when the user submits it). |
|
|
114
114
|
| `ha.register_card_serving(hass, domain, lovelace_dir, card_url)` | `ha_deploy_kit.ha` | Serve the card and register it as a frontend module. |
|
|
115
115
|
|
|
116
116
|
## CLI
|
|
@@ -62,7 +62,7 @@ how releases are published.
|
|
|
62
62
|
| `ha.async_register_restart_service(hass, domain, ...)` | `ha_deploy_kit.ha` | Idempotently register the restart service (empty schema). Pass `fixable=True` for a one-click Repairs card (pair with the fix flow). |
|
|
63
63
|
| `ha.async_raise_restart_issue(hass, domain, ...)` | `ha_deploy_kit.ha` | Raise the deduplicated, non-persistent restart Repairs issue. |
|
|
64
64
|
| `ha.async_clear_restart_issue(hass, domain, ...)` | `ha_deploy_kit.ha` | Clear the restart issue. |
|
|
65
|
-
| `ha.async_create_restart_fix_flow(hass, issue_id, ...)` | `ha_deploy_kit.ha` | Optional opt-in confirm-and-restart flow (restart only
|
|
65
|
+
| `ha.async_create_restart_fix_flow(hass, issue_id, ...)` | `ha_deploy_kit.ha` | Optional opt-in confirm-and-restart flow (shows a confirmation dialog; restart only when the user submits it). |
|
|
66
66
|
| `ha.register_card_serving(hass, domain, lovelace_dir, card_url)` | `ha_deploy_kit.ha` | Serve the card and register it as a frontend module. |
|
|
67
67
|
|
|
68
68
|
## CLI
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-deploy-kit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "HA-free SSH deploy / compare / notify / optional-frontend toolkit for Home Assistant custom components"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""Optional opt-in confirm-and-restart Repairs flow.
|
|
2
2
|
|
|
3
|
-
The restart happens ONLY
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
may wire it.
|
|
3
|
+
The restart happens ONLY after the user submits the confirmation dialog. Opening
|
|
4
|
+
the Repairs issue shows a dialog describing the restart; clicking submit is the
|
|
5
|
+
explicit confirmation. No executable restart lives in the core engine; this flow
|
|
6
|
+
is the single opt-in place a consumer may wire it.
|
|
8
7
|
"""
|
|
9
8
|
|
|
10
9
|
from __future__ import annotations
|
|
@@ -18,11 +17,9 @@ if TYPE_CHECKING:
|
|
|
18
17
|
from homeassistant.core import HomeAssistant
|
|
19
18
|
from homeassistant.data_entry_flow import FlowResult
|
|
20
19
|
|
|
21
|
-
_CONFIRM_SCHEMA = vol.Schema({vol.Required("confirm", default=False): bool})
|
|
22
|
-
|
|
23
20
|
|
|
24
21
|
class _RestartRequiredFixFlow(RepairsFlow):
|
|
25
|
-
"""
|
|
22
|
+
"""Shows a confirmation dialog, then restarts only when the user submits it."""
|
|
26
23
|
|
|
27
24
|
async def async_step_init(
|
|
28
25
|
self, user_input: dict[str, Any] | None = None
|
|
@@ -32,13 +29,9 @@ class _RestartRequiredFixFlow(RepairsFlow):
|
|
|
32
29
|
async def async_step_confirm_restart(
|
|
33
30
|
self, user_input: dict[str, Any] | None = None
|
|
34
31
|
) -> FlowResult:
|
|
35
|
-
if
|
|
36
|
-
# First render, or submitted without ticking the box: re-show the
|
|
37
|
-
# form and never restart.
|
|
32
|
+
if user_input is None:
|
|
38
33
|
return self.async_show_form(
|
|
39
|
-
step_id="confirm_restart",
|
|
40
|
-
data_schema=_CONFIRM_SCHEMA,
|
|
41
|
-
errors={"base": "confirm_required"} if user_input else None,
|
|
34
|
+
step_id="confirm_restart", data_schema=vol.Schema({})
|
|
42
35
|
)
|
|
43
36
|
await self.hass.services.async_call("homeassistant", "restart", blocking=False)
|
|
44
37
|
return self.async_create_entry(title="", data={})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"issues": {
|
|
3
|
+
"restart_required": {
|
|
4
|
+
"title": "Restart required to finish updating",
|
|
5
|
+
"fix_flow": {
|
|
6
|
+
"step": {
|
|
7
|
+
"confirm_restart": {
|
|
8
|
+
"title": "Restart Home Assistant",
|
|
9
|
+
"description": "Newly deployed code is on disk. Click submit to restart Home Assistant now and load it."
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pytest_homeassistant_custom_component.common import async_mock_service
|
|
3
|
+
|
|
4
|
+
from ha_deploy_kit.ha import async_create_restart_fix_flow
|
|
5
|
+
|
|
6
|
+
pytestmark = pytest.mark.ha
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def test_showing_the_form_never_restarts(hass):
|
|
10
|
+
calls = async_mock_service(hass, "homeassistant", "restart")
|
|
11
|
+
|
|
12
|
+
flow = async_create_restart_fix_flow(hass, "restart_required")
|
|
13
|
+
assert flow is not None
|
|
14
|
+
flow.hass = hass
|
|
15
|
+
|
|
16
|
+
shown = await flow.async_step_init()
|
|
17
|
+
assert shown["type"] == "form"
|
|
18
|
+
assert shown["step_id"] == "confirm_restart"
|
|
19
|
+
assert len(calls) == 0 # merely showing the confirmation dialog never restarts
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def test_restart_only_after_submitting_the_dialog(hass):
|
|
23
|
+
calls = async_mock_service(hass, "homeassistant", "restart")
|
|
24
|
+
|
|
25
|
+
flow = async_create_restart_fix_flow(hass, "restart_required")
|
|
26
|
+
assert flow is not None
|
|
27
|
+
flow.hass = hass
|
|
28
|
+
|
|
29
|
+
result = await flow.async_step_confirm_restart({})
|
|
30
|
+
assert result["type"] == "create_entry"
|
|
31
|
+
assert len(calls) == 1 # restart only when the user submits the dialog
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def test_fix_flow_other_issue_returns_none(hass):
|
|
35
|
+
assert async_create_restart_fix_flow(hass, "something_else") is None
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"issues": {
|
|
3
|
-
"restart_required": {
|
|
4
|
-
"title": "Restart required to finish updating",
|
|
5
|
-
"fix_flow": {
|
|
6
|
-
"step": {
|
|
7
|
-
"confirm_restart": {
|
|
8
|
-
"title": "Restart Home Assistant",
|
|
9
|
-
"description": "Restart Home Assistant now to load the newly deployed code? Tick the box below to confirm.",
|
|
10
|
-
"data": {
|
|
11
|
-
"confirm": "Yes, restart Home Assistant now"
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"error": {
|
|
16
|
-
"confirm_required": "Tick the box to confirm the restart, or close this dialog to cancel."
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from pytest_homeassistant_custom_component.common import async_mock_service
|
|
3
|
-
|
|
4
|
-
from ha_deploy_kit.ha import async_create_restart_fix_flow
|
|
5
|
-
|
|
6
|
-
pytestmark = pytest.mark.ha
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async def test_showing_the_form_never_restarts(hass):
|
|
10
|
-
calls = async_mock_service(hass, "homeassistant", "restart")
|
|
11
|
-
|
|
12
|
-
flow = async_create_restart_fix_flow(hass, "restart_required")
|
|
13
|
-
assert flow is not None
|
|
14
|
-
flow.hass = hass
|
|
15
|
-
|
|
16
|
-
shown = await flow.async_step_init()
|
|
17
|
-
assert shown["type"] == "form"
|
|
18
|
-
assert shown["step_id"] == "confirm_restart"
|
|
19
|
-
assert len(calls) == 0 # merely showing the form never restarts
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
async def test_submitting_without_confirming_does_not_restart(hass):
|
|
23
|
-
calls = async_mock_service(hass, "homeassistant", "restart")
|
|
24
|
-
|
|
25
|
-
flow = async_create_restart_fix_flow(hass, "restart_required")
|
|
26
|
-
assert flow is not None
|
|
27
|
-
flow.hass = hass
|
|
28
|
-
|
|
29
|
-
# A bare submit (checkbox left unticked) must NOT restart; re-show the form.
|
|
30
|
-
unticked = await flow.async_step_confirm_restart({"confirm": False})
|
|
31
|
-
assert unticked["type"] == "form"
|
|
32
|
-
assert unticked["step_id"] == "confirm_restart"
|
|
33
|
-
assert len(calls) == 0
|
|
34
|
-
|
|
35
|
-
# An empty submit is treated the same as an unticked checkbox.
|
|
36
|
-
empty = await flow.async_step_confirm_restart({})
|
|
37
|
-
assert empty["type"] == "form"
|
|
38
|
-
assert len(calls) == 0
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
async def test_restart_only_after_explicit_confirmation(hass):
|
|
42
|
-
calls = async_mock_service(hass, "homeassistant", "restart")
|
|
43
|
-
|
|
44
|
-
flow = async_create_restart_fix_flow(hass, "restart_required")
|
|
45
|
-
assert flow is not None
|
|
46
|
-
flow.hass = hass
|
|
47
|
-
|
|
48
|
-
result = await flow.async_step_confirm_restart({"confirm": True})
|
|
49
|
-
assert result["type"] == "create_entry"
|
|
50
|
-
assert len(calls) == 1 # restart only on explicit, ticked confirmation
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async def test_fix_flow_other_issue_returns_none(hass):
|
|
54
|
-
assert async_create_restart_fix_flow(hass, "something_else") is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|