ha-deploy-kit 0.1.2__tar.gz → 0.1.4__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.
Files changed (58) hide show
  1. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/CHANGELOG.md +29 -0
  2. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/PKG-INFO +1 -1
  3. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/pyproject.toml +1 -1
  4. ha_deploy_kit-0.1.4/src/ha_deploy_kit/ha/repairs_flow.py +56 -0
  5. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_repairs_flow.py +18 -3
  6. ha_deploy_kit-0.1.2/src/ha_deploy_kit/ha/repairs_flow.py +0 -50
  7. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/.coveragerc-ha +0 -0
  8. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/.github/workflows/release.yml +0 -0
  9. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/.github/workflows/validate.yml +0 -0
  10. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/.gitignore +0 -0
  11. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/LICENSE +0 -0
  12. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/README.md +0 -0
  13. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/docs/adopting.md +0 -0
  14. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/docs/configuration.md +0 -0
  15. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/docs/install.md +0 -0
  16. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/docs/releasing.md +0 -0
  17. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/backend_only/.env.example +0 -0
  18. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/backend_only/deploy.py +0 -0
  19. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/backend_only/deploy.sh +0 -0
  20. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/frontend_card/.env.example +0 -0
  21. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/frontend_card/deploy.py +0 -0
  22. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/frontend_card/deploy.sh +0 -0
  23. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/frontend_card/ha_adapter.py +0 -0
  24. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/synthetic_widget/deploy.py +0 -0
  25. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/examples/synthetic_widget/ha_adapter.py +0 -0
  26. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/__init__.py +0 -0
  27. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/cli.py +0 -0
  28. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/comparison.py +0 -0
  29. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/config.py +0 -0
  30. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/frontend.py +0 -0
  31. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/ha/__init__.py +0 -0
  32. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/ha/repairs_issue.py +0 -0
  33. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/ha/service.py +0 -0
  34. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/ha/serving.py +0 -0
  35. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/inventory.py +0 -0
  36. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/metadata/services.yaml.tmpl +0 -0
  37. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/metadata/strings.snippet.fixflow.json +0 -0
  38. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/metadata/strings.snippet.json +0 -0
  39. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/notify.py +0 -0
  40. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/settings.py +0 -0
  41. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/src/ha_deploy_kit/transport.py +0 -0
  42. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/conftest.py +0 -0
  43. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/conftest.py +0 -0
  44. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_example_adapters.py +0 -0
  45. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_real_http_to_service.py +0 -0
  46. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_repairs_issue.py +0 -0
  47. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_service.py +0 -0
  48. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/ha/test_serving.py +0 -0
  49. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_cli.py +0 -0
  50. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_comparison.py +0 -0
  51. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_config.py +0 -0
  52. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_frontend.py +0 -0
  53. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_installed_smoke.py +0 -0
  54. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_inventory.py +0 -0
  55. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_multi_consumer.py +0 -0
  56. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_notify.py +0 -0
  57. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_packaging.py +0 -0
  58. {ha_deploy_kit-0.1.2 → ha_deploy_kit-0.1.4}/tests/test_transport.py +0 -0
@@ -1,6 +1,35 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.1.4 (2026-10-29)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **ha**: Do not forward injected user_input into the restart confirm step
9
+ ([`31fdb48`](https://github.com/latrekc-meta/ha-deploy-kit/commit/31fdb48b7c344ef0fb49b8459a58df54e3d7613c))
10
+
11
+ HA seeds the first fix-flow step's user_input with {"issue_id": ...} (RepairsFlowManager.async_init
12
+ -> data_entry_flow feeds it to async_step_init). async_step_init forwarded that into
13
+ async_step_confirm_restart, whose user_input-is-not-None branch then restarted Home Assistant the
14
+ instant the issue was opened -- no confirmation. Mirror HA's own ConfirmRepairFlow:
15
+ async_step_init delegates WITHOUT forwarding user_input, so the restart fires only on a real
16
+ submit. Add a regression test that opens the flow with {"issue_id": ...} and asserts no restart.
17
+
18
+
19
+ ## v0.1.3 (2026-10-29)
20
+
21
+ ### Bug Fixes
22
+
23
+ - **ha**: Show restart confirmation without an empty schema
24
+ ([`c6de7bb`](https://github.com/latrekc-meta/ha-deploy-kit/commit/c6de7bba87c0116d137a5cac918a5a050763ef59))
25
+
26
+ HA's Repairs frontend auto-submits a form that has an empty data schema, so
27
+ async_show_form(step_id=..., data_schema=vol.Schema({})) restarted Home Assistant the instant the
28
+ user opened the issue -- no confirmation. Show the form with no data schema (as in the proven
29
+ hand-rolled flow) so HA renders a confirmation that waits for an explicit submit. Add a regression
30
+ guard asserting data_schema is None.
31
+
32
+
4
33
  ## v0.1.2 (2026-10-29)
5
34
 
6
35
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ha-deploy-kit
3
- Version: 0.1.2
3
+ Version: 0.1.4
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ha-deploy-kit"
7
- version = "0.1.2"
7
+ version = "0.1.4"
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" }
@@ -0,0 +1,56 @@
1
+ """Optional opt-in confirm-and-restart Repairs flow.
2
+
3
+ Opening the Repairs issue shows a confirmation dialog; the restart happens ONLY
4
+ when the user submits it. This mirrors Home Assistant's own ``ConfirmRepairFlow``:
5
+ ``async_step_init`` delegates to the confirm step WITHOUT forwarding ``user_input``.
6
+ That matters because HA seeds the first step's ``user_input`` with
7
+ ``{"issue_id": ...}`` (see repairs ``RepairsFlowManager.async_init``); forwarding it
8
+ would look like a submit and restart Home Assistant the instant the issue is
9
+ opened. No executable restart lives in the core engine; this flow is the single
10
+ opt-in place a consumer may wire it.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING, Any
16
+
17
+ import voluptuous as vol
18
+ from homeassistant.components.repairs import RepairsFlow
19
+
20
+ if TYPE_CHECKING:
21
+ from homeassistant.core import HomeAssistant
22
+ from homeassistant.data_entry_flow import FlowResult
23
+
24
+
25
+ class _RestartRequiredFixFlow(RepairsFlow):
26
+ """Shows a confirmation dialog, then restarts only when the user submits it."""
27
+
28
+ async def async_step_init(
29
+ self, user_input: dict[str, Any] | None = None
30
+ ) -> FlowResult:
31
+ # Do NOT forward user_input: HA seeds the first step with
32
+ # {"issue_id": ...}, which would look like a submit and restart on open.
33
+ return await self.async_step_confirm_restart()
34
+
35
+ async def async_step_confirm_restart(
36
+ self, user_input: dict[str, Any] | None = None
37
+ ) -> FlowResult:
38
+ if user_input is not None:
39
+ await self.hass.services.async_call("homeassistant", "restart")
40
+ return self.async_create_entry(title="", data={})
41
+ return self.async_show_form(
42
+ step_id="confirm_restart", data_schema=vol.Schema({})
43
+ )
44
+
45
+
46
+ def async_create_restart_fix_flow(
47
+ hass: HomeAssistant,
48
+ issue_id: str,
49
+ data: dict[str, Any] | None = None,
50
+ *,
51
+ expected_issue_id: str = "restart_required",
52
+ ) -> RepairsFlow | None:
53
+ """Return the opt-in restart fix flow, or ``None`` for other issue ids."""
54
+ if issue_id != expected_issue_id:
55
+ return None
56
+ return _RestartRequiredFixFlow()
@@ -6,17 +6,32 @@ from ha_deploy_kit.ha import async_create_restart_fix_flow
6
6
  pytestmark = pytest.mark.ha
7
7
 
8
8
 
9
- async def test_showing_the_form_never_restarts(hass):
9
+ async def test_opening_the_issue_shows_the_form_and_never_restarts(hass):
10
+ # HA seeds the first step's user_input with {"issue_id": ...} (see
11
+ # RepairsFlowManager.async_init). async_step_init must still only SHOW the
12
+ # confirmation dialog -- never restart on open.
10
13
  calls = async_mock_service(hass, "homeassistant", "restart")
11
14
 
12
15
  flow = async_create_restart_fix_flow(hass, "restart_required")
13
16
  assert flow is not None
14
17
  flow.hass = hass
15
18
 
16
- shown = await flow.async_step_init()
19
+ shown = await flow.async_step_init({"issue_id": "restart_required"})
17
20
  assert shown["type"] == "form"
18
21
  assert shown["step_id"] == "confirm_restart"
19
- assert len(calls) == 0 # merely showing the confirmation dialog never restarts
22
+ assert len(calls) == 0 # opening the issue must not restart
23
+
24
+
25
+ async def test_init_without_input_shows_the_form(hass):
26
+ calls = async_mock_service(hass, "homeassistant", "restart")
27
+
28
+ flow = async_create_restart_fix_flow(hass, "restart_required")
29
+ assert flow is not None
30
+ flow.hass = hass
31
+
32
+ shown = await flow.async_step_init()
33
+ assert shown["type"] == "form"
34
+ assert len(calls) == 0
20
35
 
21
36
 
22
37
  async def test_restart_only_after_submitting_the_dialog(hass):
@@ -1,50 +0,0 @@
1
- """Optional opt-in confirm-and-restart Repairs flow.
2
-
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.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- from typing import TYPE_CHECKING, Any
12
-
13
- import voluptuous as vol
14
- from homeassistant.components.repairs import RepairsFlow
15
-
16
- if TYPE_CHECKING:
17
- from homeassistant.core import HomeAssistant
18
- from homeassistant.data_entry_flow import FlowResult
19
-
20
-
21
- class _RestartRequiredFixFlow(RepairsFlow):
22
- """Shows a confirmation dialog, then restarts only when the user submits it."""
23
-
24
- async def async_step_init(
25
- self, user_input: dict[str, Any] | None = None
26
- ) -> FlowResult:
27
- return await self.async_step_confirm_restart(user_input)
28
-
29
- async def async_step_confirm_restart(
30
- self, user_input: dict[str, Any] | None = None
31
- ) -> FlowResult:
32
- if user_input is None:
33
- return self.async_show_form(
34
- step_id="confirm_restart", data_schema=vol.Schema({})
35
- )
36
- await self.hass.services.async_call("homeassistant", "restart", blocking=False)
37
- return self.async_create_entry(title="", data={})
38
-
39
-
40
- def async_create_restart_fix_flow(
41
- hass: HomeAssistant,
42
- issue_id: str,
43
- data: dict[str, Any] | None = None,
44
- *,
45
- expected_issue_id: str = "restart_required",
46
- ) -> RepairsFlow | None:
47
- """Return the opt-in restart fix flow, or ``None`` for other issue ids."""
48
- if issue_id != expected_issue_id:
49
- return None
50
- return _RestartRequiredFixFlow()
File without changes
File without changes
File without changes