redeploy 0.2.26__tar.gz → 0.2.27__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 (138) hide show
  1. {redeploy-0.2.26 → redeploy-0.2.27}/PKG-INFO +6 -6
  2. {redeploy-0.2.26 → redeploy-0.2.27}/README.md +5 -5
  3. {redeploy-0.2.26 → redeploy-0.2.27}/pyproject.toml +1 -1
  4. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/__init__.py +1 -1
  5. redeploy-0.2.27/redeploy/apply/exceptions.py +12 -0
  6. redeploy-0.2.27/redeploy/apply/progress.py +106 -0
  7. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/PKG-INFO +6 -6
  8. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/SOURCES.txt +2 -0
  9. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/apply/__init__.py +0 -0
  10. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/apply/executor.py +0 -0
  11. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/apply/state.py +0 -0
  12. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/audit.py +0 -0
  13. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/__init__.py +0 -0
  14. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/__init__.py +0 -0
  15. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/audit.py +0 -0
  16. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/detect.py +0 -0
  17. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/devices.py +0 -0
  18. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/diagnose.py +0 -0
  19. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/diff.py +0 -0
  20. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/exec_.py +0 -0
  21. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/export.py +0 -0
  22. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/import_.py +0 -0
  23. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/init.py +0 -0
  24. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/inspect.py +0 -0
  25. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/patterns.py +0 -0
  26. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/plan_apply.py +0 -0
  27. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/plugin.py +0 -0
  28. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/probe.py +0 -0
  29. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/state.py +0 -0
  30. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/status.py +0 -0
  31. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/target.py +0 -0
  32. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/version.py +0 -0
  33. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/commands/workflow.py +0 -0
  34. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/core.py +0 -0
  35. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli/display.py +0 -0
  36. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/cli.py +0 -0
  37. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/data_sync.py +0 -0
  38. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/__init__.py +0 -0
  39. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/detector.py +0 -0
  40. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/probes.py +0 -0
  41. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/remote.py +0 -0
  42. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/templates.py +0 -0
  43. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/detect/workflow.py +0 -0
  44. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/discovery.py +0 -0
  45. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl/__init__.py +0 -0
  46. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl/loader.py +0 -0
  47. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl/parser.py +0 -0
  48. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/__init__.py +0 -0
  49. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/context.py +0 -0
  50. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/decorators.py +0 -0
  51. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/docker_steps.py +0 -0
  52. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/exceptions.py +0 -0
  53. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/runner.py +0 -0
  54. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/dsl_python/steps.py +0 -0
  55. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/fleet.py +0 -0
  56. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/__init__.py +0 -0
  57. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/base.py +0 -0
  58. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/docker_compose.py +0 -0
  59. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/parsers/__init__.py +0 -0
  60. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/parsers/compose.py +0 -0
  61. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/iac/registry.py +0 -0
  62. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/markpact/__init__.py +0 -0
  63. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/markpact/compiler.py +0 -0
  64. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/markpact/models.py +0 -0
  65. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/markpact/parser.py +0 -0
  66. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/models.py +0 -0
  67. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/observe.py +0 -0
  68. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/parse.py +0 -0
  69. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/patterns.py +0 -0
  70. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plan/__init__.py +0 -0
  71. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plan/planner.py +0 -0
  72. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plugins/__init__.py +0 -0
  73. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plugins/builtin/__init__.py +0 -0
  74. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plugins/builtin/browser_reload.py +0 -0
  75. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plugins/builtin/notify.py +0 -0
  76. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/plugins/builtin/systemd_reload.py +0 -0
  77. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/spec_loader.py +0 -0
  78. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/ssh.py +0 -0
  79. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/steps.py +0 -0
  80. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/__init__.py +0 -0
  81. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_audit.py +0 -0
  82. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_cli.py +0 -0
  83. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_data_sync.py +0 -0
  84. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_discovery.py +0 -0
  85. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_dsl.py +0 -0
  86. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_examples.py +0 -0
  87. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_executor.py +0 -0
  88. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_fleet.py +0 -0
  89. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_git_integration.py +0 -0
  90. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_iac.py +0 -0
  91. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_manifest.py +0 -0
  92. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_markpact_compiler.py +0 -0
  93. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_markpact_parser.py +0 -0
  94. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_models.py +0 -0
  95. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_observe.py +0 -0
  96. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_parse.py +0 -0
  97. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_patterns.py +0 -0
  98. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_planner_edge.py +0 -0
  99. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_plugins.py +0 -0
  100. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_probes.py +0 -0
  101. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_public_api.py +0 -0
  102. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_resume.py +0 -0
  103. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_spec_loader.py +0 -0
  104. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_ssh.py +0 -0
  105. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_templates.py +0 -0
  106. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_verify.py +0 -0
  107. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_version.py +0 -0
  108. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_version_changelog.py +0 -0
  109. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_version_cli.py +0 -0
  110. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_version_manifest.py +0 -0
  111. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_version_sources.py +0 -0
  112. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/tests/test_workflow.py +0 -0
  113. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/verify.py +0 -0
  114. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/__init__.py +0 -0
  115. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/bump.py +0 -0
  116. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/changelog.py +0 -0
  117. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/commits.py +0 -0
  118. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/diff.py +0 -0
  119. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/git_integration.py +0 -0
  120. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/git_transaction.py +0 -0
  121. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/manifest.py +0 -0
  122. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/__init__.py +0 -0
  123. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/base.py +0 -0
  124. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/json_.py +0 -0
  125. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/plain.py +0 -0
  126. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/regex.py +0 -0
  127. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/toml_.py +0 -0
  128. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/sources/yaml_.py +0 -0
  129. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version/transaction.py +0 -0
  130. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy/version.py +0 -0
  131. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/dependency_links.txt +0 -0
  132. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/entry_points.txt +0 -0
  133. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/requires.txt +0 -0
  134. {redeploy-0.2.26 → redeploy-0.2.27}/redeploy.egg-info/top_level.txt +0 -0
  135. {redeploy-0.2.26 → redeploy-0.2.27}/setup.cfg +0 -0
  136. {redeploy-0.2.26 → redeploy-0.2.27}/tests/test_iac.py +0 -0
  137. {redeploy-0.2.26 → redeploy-0.2.27}/tests/test_parse.py +0 -0
  138. {redeploy-0.2.26 → redeploy-0.2.27}/tests/test_redeploy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.2.26
3
+ Version: 0.2.27
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -27,17 +27,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
27
27
 
28
28
  ## AI Cost Tracking
29
29
 
30
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$6.90-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
30
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.05-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
32
 
33
- - 🤖 **LLM usage:** $6.9000 (46 commits)
34
- - 👤 **Human dev:** ~$1432 (14.3h @ $100/h, 30min dedup)
33
+ - 🤖 **LLM usage:** $7.0500 (47 commits)
34
+ - 👤 **Human dev:** ~$1435 (14.3h @ $100/h, 30min dedup)
35
35
 
36
36
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
37
37
 
38
38
  ---
39
39
 
40
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
41
 
42
42
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
43
43
 
@@ -3,17 +3,17 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$6.90-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.05-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $6.9000 (46 commits)
10
- - 👤 **Human dev:** ~$1432 (14.3h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.0500 (47 commits)
10
+ - 👤 **Human dev:** ~$1435 (14.3h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
16
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
17
17
 
18
18
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
19
19
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "redeploy"
7
- version = "0.2.26"
7
+ version = "0.2.27"
8
8
  description = "Infrastructure migration toolkit: detect → plan → apply"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -7,7 +7,7 @@ Public API (stable from 0.2.0, semver guaranteed)::
7
7
 
8
8
  Everything not listed in ``__all__`` is internal and may change without notice.
9
9
  """
10
- __version__ = "0.2.26"
10
+ __version__ = "0.2.27"
11
11
 
12
12
  # ── Core models ───────────────────────────────────────────────────────────────
13
13
  from .models import ( # noqa: F401
@@ -0,0 +1,12 @@
1
+ """Exceptions for the apply package."""
2
+ from __future__ import annotations
3
+
4
+ from ..models import MigrationStep
5
+
6
+
7
+ class StepError(Exception):
8
+ """Exception raised when a migration step fails."""
9
+
10
+ def __init__(self, step: MigrationStep, msg: str):
11
+ self.step = step
12
+ super().__init__(f"[{step.id}] {msg}")
@@ -0,0 +1,106 @@
1
+ """Progress emission for migration execution."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+ from datetime import datetime, timezone
6
+ from typing import IO, Optional
7
+
8
+ import yaml
9
+ from loguru import logger
10
+
11
+ from ..models import MigrationPlan, MigrationStep
12
+
13
+
14
+ class ProgressEmitter:
15
+ """Emits YAML-formatted progress events to a stream (default: stdout).
16
+
17
+ Each event is a YAML document (separated by ---) so consumers can
18
+ parse the stream incrementally with yaml.safe_load_all().
19
+
20
+ Event types:
21
+ - start : deployment begins
22
+ - step_start: a single step begins
23
+ - step_done : step completed successfully
24
+ - step_fail : step failed with error
25
+ - progress : mid-step progress update (build cache, container status…)
26
+ - done : all steps completed
27
+ - failed : deployment failed
28
+ """
29
+
30
+ def __init__(self, stream: IO[str] = None):
31
+ self._out = stream or sys.stdout
32
+ self._t0 = time.monotonic()
33
+
34
+ def _ts(self) -> str:
35
+ return datetime.now(timezone.utc).strftime("%H:%M:%S")
36
+
37
+ def _elapsed(self) -> float:
38
+ return round(time.monotonic() - self._t0, 1)
39
+
40
+ def _emit(self, event: dict) -> None:
41
+ event.setdefault("ts", self._ts())
42
+ event.setdefault("elapsed_s", self._elapsed())
43
+ self._out.write("---\n")
44
+ self._out.write(yaml.dump(event, default_flow_style=False, allow_unicode=True))
45
+ self._out.flush()
46
+
47
+ def start(self, plan: MigrationPlan) -> None:
48
+ from ..models import StepStatus
49
+
50
+ self._emit({
51
+ "event": "start",
52
+ "host": plan.host,
53
+ "strategy": f"{plan.from_strategy.value} → {plan.to_strategy.value}",
54
+ "total_steps": len(plan.steps),
55
+ "steps": [
56
+ {"n": i + 1, "id": s.id, "action": s.action.value,
57
+ "description": s.description, "status": s.status.value}
58
+ for i, s in enumerate(plan.steps)
59
+ ],
60
+ })
61
+
62
+ def step_start(self, n: int, step: MigrationStep) -> None:
63
+ self._emit({
64
+ "event": "step_start",
65
+ "n": n,
66
+ "id": step.id,
67
+ "action": step.action.value,
68
+ "description": step.description,
69
+ "status": "running",
70
+ })
71
+
72
+ def step_done(self, n: int, step: MigrationStep) -> None:
73
+ self._emit({
74
+ "event": "step_done",
75
+ "n": n,
76
+ "id": step.id,
77
+ "status": "done",
78
+ "result": step.result,
79
+ })
80
+
81
+ def step_fail(self, n: int, step: MigrationStep, error: str) -> None:
82
+ self._emit({
83
+ "event": "step_fail",
84
+ "n": n,
85
+ "id": step.id,
86
+ "status": "failed",
87
+ "error": error,
88
+ })
89
+
90
+ def progress(self, step_id: str, message: str) -> None:
91
+ self._emit({
92
+ "event": "progress",
93
+ "id": step_id,
94
+ "message": message,
95
+ })
96
+
97
+ def done(self, total: int) -> None:
98
+ self._emit({"event": "done", "steps_completed": total, "result": "ok"})
99
+
100
+ def failed(self, completed: int, total: int, error: str) -> None:
101
+ self._emit({
102
+ "event": "failed",
103
+ "steps_completed": completed,
104
+ "steps_total": total,
105
+ "error": error,
106
+ })
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.2.26
3
+ Version: 0.2.27
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -27,17 +27,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
27
27
 
28
28
  ## AI Cost Tracking
29
29
 
30
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$6.90-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
30
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
31
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.05-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-14.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
32
 
33
- - 🤖 **LLM usage:** $6.9000 (46 commits)
34
- - 👤 **Human dev:** ~$1432 (14.3h @ $100/h, 30min dedup)
33
+ - 🤖 **LLM usage:** $7.0500 (47 commits)
34
+ - 👤 **Human dev:** ~$1435 (14.3h @ $100/h, 30min dedup)
35
35
 
36
36
  Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
37
37
 
38
38
  ---
39
39
 
40
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.26-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
40
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.27-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
41
41
 
42
42
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
43
43
 
@@ -22,7 +22,9 @@ redeploy.egg-info/entry_points.txt
22
22
  redeploy.egg-info/requires.txt
23
23
  redeploy.egg-info/top_level.txt
24
24
  redeploy/apply/__init__.py
25
+ redeploy/apply/exceptions.py
25
26
  redeploy/apply/executor.py
27
+ redeploy/apply/progress.py
26
28
  redeploy/apply/state.py
27
29
  redeploy/cli/__init__.py
28
30
  redeploy/cli/core.py
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