redeploy 0.2.65__tar.gz → 0.2.67__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 (243) hide show
  1. {redeploy-0.2.65 → redeploy-0.2.67}/PKG-INFO +6 -6
  2. {redeploy-0.2.65 → redeploy-0.2.67}/README.md +5 -5
  3. {redeploy-0.2.65 → redeploy-0.2.67}/pyproject.toml +1 -1
  4. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/__init__.py +1 -1
  5. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/spec_analyzer.py +119 -11
  6. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/handlers.py +29 -7
  7. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/plan_apply.py +425 -1
  8. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/schema.py +3 -1
  9. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_analyze.py +53 -0
  10. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_executor.py +1 -1
  11. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/PKG-INFO +6 -6
  12. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/__init__.py +0 -0
  13. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/checkers/__init__.py +0 -0
  14. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/preflight_schema.py +0 -0
  15. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/__init__.py +0 -0
  16. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/exceptions.py +0 -0
  17. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/executor.py +0 -0
  18. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/progress.py +0 -0
  19. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/rollback.py +0 -0
  20. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/state.py +0 -0
  21. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/state_apply.py +0 -0
  22. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/utils/__init__.py +0 -0
  23. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/utils/run_container_build.py +0 -0
  24. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/audit.py +0 -0
  25. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/__init__.py +0 -0
  26. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/extractor.py +0 -0
  27. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/__init__.py +0 -0
  28. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/docker_compose.py +0 -0
  29. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/migration.py +0 -0
  30. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/__init__.py +0 -0
  31. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/compose.py +0 -0
  32. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/hardware.py +0 -0
  33. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/infra.py +0 -0
  34. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/migration.py +0 -0
  35. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/__init__.py +0 -0
  36. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/__init__.py +0 -0
  37. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/audit.py +0 -0
  38. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/blueprint.py +0 -0
  39. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/bump_fix.py +0 -0
  40. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/detect.py +0 -0
  41. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/device_map.py +0 -0
  42. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/device_map_renderers.py +0 -0
  43. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/devices.py +0 -0
  44. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/diagnose.py +0 -0
  45. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/diff.py +0 -0
  46. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/exec_.py +0 -0
  47. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/export.py +0 -0
  48. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/hardware.py +0 -0
  49. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/import_.py +0 -0
  50. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/init.py +0 -0
  51. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/inspect.py +0 -0
  52. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/lint.py +0 -0
  53. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/mcp_cmd.py +0 -0
  54. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/patterns.py +0 -0
  55. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/plugin.py +0 -0
  56. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/probe.py +0 -0
  57. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/prompt_cmd.py +0 -0
  58. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/push.py +0 -0
  59. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/state.py +0 -0
  60. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/status.py +0 -0
  61. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/target.py +0 -0
  62. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/__init__.py +0 -0
  63. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/commands.py +0 -0
  64. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/helpers.py +0 -0
  65. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/monorepo.py +0 -0
  66. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/release.py +0 -0
  67. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/scanner.py +0 -0
  68. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/__init__.py +0 -0
  69. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/changelog_config.py +0 -0
  70. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/git_config.py +0 -0
  71. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/workflow.py +0 -0
  72. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/core.py +0 -0
  73. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/display.py +0 -0
  74. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/query.py +0 -0
  75. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli.py +0 -0
  76. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/__init__.py +0 -0
  77. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/applier.py +0 -0
  78. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/loader.py +0 -0
  79. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/data_sync.py +0 -0
  80. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/__init__.py +0 -0
  81. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/builtin/__init__.py +0 -0
  82. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/detector.py +0 -0
  83. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/hardware.py +0 -0
  84. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/hardware_rules.py +0 -0
  85. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/probes.py +0 -0
  86. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/remote.py +0 -0
  87. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/templates.py +0 -0
  88. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/workflow.py +0 -0
  89. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/discovery.py +0 -0
  90. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/__init__.py +0 -0
  91. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/loader.py +0 -0
  92. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/parser.py +0 -0
  93. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/__init__.py +0 -0
  94. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/context.py +0 -0
  95. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/decorators.py +0 -0
  96. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/docker_steps.py +0 -0
  97. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/exceptions.py +0 -0
  98. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/runner.py +0 -0
  99. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/steps.py +0 -0
  100. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/fleet.py +0 -0
  101. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/__init__.py +0 -0
  102. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/config_txt.py +0 -0
  103. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/__init__.py +0 -0
  104. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/hyperpixel.py +0 -0
  105. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/official.py +0 -0
  106. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/waveshare.py +0 -0
  107. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/fixes.py +0 -0
  108. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/__init__.py +0 -0
  109. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/autostart.py +0 -0
  110. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/browsers.py +0 -0
  111. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/compositors.py +0 -0
  112. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/output_profiles.py +0 -0
  113. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/panels.py +0 -0
  114. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/raspi_config.py +0 -0
  115. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/__init__.py +0 -0
  116. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/decider.py +0 -0
  117. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/hint_provider.py +0 -0
  118. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/log_writer.py +0 -0
  119. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/loop_detector.py +0 -0
  120. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/runner.py +0 -0
  121. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal.py +0 -0
  122. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/__init__.py +0 -0
  123. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/base.py +0 -0
  124. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/config_hints.py +0 -0
  125. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/docker_compose.py +0 -0
  126. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/parsers/__init__.py +0 -0
  127. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/parsers/compose.py +0 -0
  128. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/registry.py +0 -0
  129. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/integrations/__init__.py +0 -0
  130. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/integrations/op3_bridge.py +0 -0
  131. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/__init__.py +0 -0
  132. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/compiler.py +0 -0
  133. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/models.py +0 -0
  134. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/parser.py +0 -0
  135. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/mcp_server.py +0 -0
  136. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/__init__.py +0 -0
  137. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/blueprint.py +0 -0
  138. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/devices.py +0 -0
  139. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/enums.py +0 -0
  140. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/hardware.py +0 -0
  141. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/infra.py +0 -0
  142. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/manifest.py +0 -0
  143. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/persisted.py +0 -0
  144. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/pipeline.py +0 -0
  145. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/plan.py +0 -0
  146. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/spec.py +0 -0
  147. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/observe.py +0 -0
  148. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/parse.py +0 -0
  149. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/patterns.py +0 -0
  150. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plan/__init__.py +0 -0
  151. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plan/planner.py +0 -0
  152. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/__init__.py +0 -0
  153. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/__init__.py +0 -0
  154. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/browser_reload.py +0 -0
  155. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/hardware_diagnostic.py +0 -0
  156. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/notify.py +0 -0
  157. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/process_control.py +0 -0
  158. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/systemd_reload.py +0 -0
  159. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/spec_loader.py +0 -0
  160. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/ssh.py +0 -0
  161. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/__init__.py +0 -0
  162. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/builtins.py +0 -0
  163. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/kiosk.py +0 -0
  164. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/__init__.py +0 -0
  165. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_audit.py +0 -0
  166. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_cli.py +0 -0
  167. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_data_sync.py +0 -0
  168. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_discovery.py +0 -0
  169. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_dsl.py +0 -0
  170. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_examples.py +0 -0
  171. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_fleet.py +0 -0
  172. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_git_integration.py +0 -0
  173. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_heal.py +0 -0
  174. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_heal_decider.py +0 -0
  175. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_hooks.py +0 -0
  176. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac.py +0 -0
  177. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac_compose_contract.py +0 -0
  178. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac_docker_compose_contract.py +0 -0
  179. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_manifest.py +0 -0
  180. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_markpact_compiler.py +0 -0
  181. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_markpact_parser.py +0 -0
  182. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_models.py +0 -0
  183. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_observe.py +0 -0
  184. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_parse.py +0 -0
  185. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_patterns.py +0 -0
  186. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_planner_edge.py +0 -0
  187. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_plugins.py +0 -0
  188. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_probes.py +0 -0
  189. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_public_api.py +0 -0
  190. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_resume.py +0 -0
  191. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_spec_loader.py +0 -0
  192. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_ssh.py +0 -0
  193. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_templates.py +0 -0
  194. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_verify.py +0 -0
  195. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version.py +0 -0
  196. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_changelog.py +0 -0
  197. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_cli.py +0 -0
  198. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_manifest.py +0 -0
  199. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_sources.py +0 -0
  200. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_workflow.py +0 -0
  201. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/verify.py +0 -0
  202. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/__init__.py +0 -0
  203. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/bump.py +0 -0
  204. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/changelog.py +0 -0
  205. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/commits.py +0 -0
  206. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/diff.py +0 -0
  207. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/git_integration.py +0 -0
  208. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/git_transaction.py +0 -0
  209. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/manifest.py +0 -0
  210. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/__init__.py +0 -0
  211. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/base.py +0 -0
  212. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/json_.py +0 -0
  213. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/plain.py +0 -0
  214. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/regex.py +0 -0
  215. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/toml_.py +0 -0
  216. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/yaml_.py +0 -0
  217. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/transaction.py +0 -0
  218. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version.py +0 -0
  219. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/SOURCES.txt +0 -0
  220. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/dependency_links.txt +0 -0
  221. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/entry_points.txt +0 -0
  222. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/requires.txt +0 -0
  223. {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/top_level.txt +0 -0
  224. {redeploy-0.2.65 → redeploy-0.2.67}/setup.cfg +0 -0
  225. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_blueprint_sources.py +0 -0
  226. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_blueprint_sources_compose.py +0 -0
  227. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_cli_query.py +0 -0
  228. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_argocd_flux_plugin.py +0 -0
  229. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_gitops_ci_plugin.py +0 -0
  230. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_helm_kustomize_plugin.py +0 -0
  231. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_config.py +0 -0
  232. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_kiosk.py +0 -0
  233. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_rules.py +0 -0
  234. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_heal_decider.py +0 -0
  235. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_heal_loop_detector.py +0 -0
  236. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac.py +0 -0
  237. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac_config_hints.py +0 -0
  238. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac_plugin_loading.py +0 -0
  239. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_import_plugin_template.py +0 -0
  240. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_parse.py +0 -0
  241. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_prompt_llm.py +0 -0
  242. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_redeploy.py +0 -0
  243. {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_steps_kiosk.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redeploy
3
- Version: 0.2.65
3
+ Version: 0.2.67
4
4
  Summary: Infrastructure migration toolkit: detect → plan → apply
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -32,17 +32,17 @@ Requires-Dist: mcp>=1.0; extra == "mcp"
32
32
 
33
33
  ## AI Cost Tracking
34
34
 
35
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.65-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
36
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-31.8h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
35
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.67-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
36
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-34.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
37
37
 
38
- - 🤖 **LLM usage:** $7.5000 (85 commits)
39
- - 👤 **Human dev:** ~$3184 (31.8h @ $100/h, 30min dedup)
38
+ - 🤖 **LLM usage:** $7.5000 (88 commits)
39
+ - 👤 **Human dev:** ~$3416 (34.2h @ $100/h, 30min dedup)
40
40
 
41
41
  Generated on 2026-04-23 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
42
42
 
43
43
  ---
44
44
 
45
- ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.65-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
45
+ ![PyPI](https://img.shields.io/badge/pypi-redeploy-blue) ![Version](https://img.shields.io/badge/version-0.2.67-blue) ![Python](https://img.shields.io/badge/python-3.10+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
46
46
 
47
47
  Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
48
48
 
@@ -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.65-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.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-31.8h-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.67-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.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-34.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (85 commits)
10
- - 👤 **Human dev:** ~$3184 (31.8h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.5000 (88 commits)
10
+ - 👤 **Human dev:** ~$3416 (34.2h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-23 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.65-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.67-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.65"
7
+ version = "0.2.67"
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.65"
10
+ __version__ = "0.2.67"
11
11
 
12
12
  # ── Core models ───────────────────────────────────────────────────────────────
13
13
  from .models import ( # noqa: F401
@@ -9,6 +9,7 @@ Scans compiled MigrationSpec + raw markpact blocks for:
9
9
  """
10
10
  from __future__ import annotations
11
11
 
12
+ import fnmatch
12
13
  import re
13
14
  import shlex
14
15
  import shutil
@@ -59,6 +60,91 @@ class AnalysisResult:
59
60
  return [i for i in self.issues if i.severity == IssueSeverity.WARNING]
60
61
 
61
62
 
63
+ DEFAULT_REDEPLOYIGNORE = """# .redeployignore — files excluded from deployment sync
64
+ # Auto-generated by redeploy. Edit as needed.
65
+
66
+ # Python
67
+ __pycache__/
68
+ *.pyc
69
+ *.pyo
70
+ .venv/
71
+ env/
72
+ venv/
73
+
74
+ # IDE
75
+ .idea/
76
+ .vscode/
77
+ *.swp
78
+ *.swo
79
+
80
+ # OS
81
+ .DS_Store
82
+ Thumbs.db
83
+
84
+ # Build artifacts
85
+ dist/
86
+ build/
87
+ *.egg-info/
88
+ node_modules/
89
+ target/
90
+
91
+ # Version control (already handled by git, but explicit here)
92
+ .git/
93
+ """
94
+
95
+
96
+ def ensure_redeployignore(base_dir: Path) -> None:
97
+ """Create .redeployignore with sensible defaults if it doesn't exist."""
98
+ path = base_dir / ".redeployignore"
99
+ if not path.exists():
100
+ path.write_text(DEFAULT_REDEPLOYIGNORE)
101
+
102
+
103
+ class _IgnoreList:
104
+ """Read .gitignore and .redeployignore patterns and test if paths are ignored."""
105
+
106
+ def __init__(self, base_dir: Path):
107
+ self.patterns: list[str] = []
108
+ self._load(base_dir / ".gitignore")
109
+ self._load(base_dir / ".redeployignore")
110
+
111
+ def _load(self, path: Path) -> None:
112
+ if not path.exists():
113
+ return
114
+ for line in path.read_text().splitlines():
115
+ line = line.strip()
116
+ if line and not line.startswith("#"):
117
+ self.patterns.append(line)
118
+
119
+ def is_ignored(self, rel_path: Path) -> bool:
120
+ """Check if a path relative to base_dir matches any ignore pattern."""
121
+ parts = rel_path.parts
122
+ name = rel_path.name
123
+
124
+ for pattern in self.patterns:
125
+ pat = pattern.rstrip("/")
126
+ # Direct filename match
127
+ if fnmatch.fnmatch(name, pat):
128
+ return True
129
+ # Any path component match
130
+ for part in parts:
131
+ if fnmatch.fnmatch(part, pat):
132
+ return True
133
+ # Directory-depth patterns with slash
134
+ if "/" in pattern:
135
+ pat_parts = pat.split("/")
136
+ # Try matching at each possible depth
137
+ for start in range(len(parts) - len(pat_parts) + 1):
138
+ match = True
139
+ for i, pp in enumerate(pat_parts):
140
+ if not fnmatch.fnmatch(parts[start + i], pp):
141
+ match = False
142
+ break
143
+ if match:
144
+ return True
145
+ return False
146
+
147
+
62
148
  class _Checker:
63
149
  def check(self, spec: MigrationSpec, document: MarkpactDocument | None,
64
150
  base_dir: Path, result: AnalysisResult) -> None:
@@ -69,13 +155,14 @@ class _PathChecker(_Checker):
69
155
  """Validate local file paths referenced by steps."""
70
156
 
71
157
  def check(self, spec, document, base_dir, result):
158
+ ign = _IgnoreList(base_dir)
72
159
  for step in spec.extra_steps:
73
- self._check_field(step, "src", base_dir, result)
74
- self._check_field(step, "dst", base_dir, result)
75
- self._check_field(step, "config_file", base_dir, result)
160
+ self._check_field(step, "src", base_dir, result, ign)
161
+ self._check_field(step, "dst", base_dir, result, ign)
162
+ self._check_field(step, "config_file", base_dir, result, ign)
76
163
  self._check_env_file(step, base_dir, result)
77
164
 
78
- def _check_field(self, step: dict, field_name: str, base_dir: Path, result: AnalysisResult):
165
+ def _check_field(self, step: dict, field_name: str, base_dir: Path, result: AnalysisResult, ign: _IgnoreList):
79
166
  val = step.get(field_name)
80
167
  if not val:
81
168
  return
@@ -87,6 +174,13 @@ class _PathChecker(_Checker):
87
174
  return
88
175
  path = self._resolve(val, base_dir)
89
176
  if path and not path.exists():
177
+ # Skip if the missing path matches an ignore pattern
178
+ try:
179
+ rel = path.resolve().relative_to(base_dir.resolve())
180
+ if ign.is_ignored(rel):
181
+ return
182
+ except ValueError:
183
+ pass
90
184
  result.add(
91
185
  IssueSeverity.ERROR, "paths",
92
186
  f"Step '{step.get('id')}' references missing {field_name}: {val}",
@@ -199,6 +293,7 @@ class _ComposeChecker(_Checker):
199
293
  """Validate docker-compose files declared in spec or found in project."""
200
294
 
201
295
  def check(self, spec, document, base_dir, result):
296
+ ign = _IgnoreList(base_dir)
202
297
  compose_files = list(spec.target.compose_files or [])
203
298
  # Also try to discover common names if none explicitly declared
204
299
  if not compose_files:
@@ -216,15 +311,22 @@ class _ComposeChecker(_Checker):
216
311
  suggestion=f"Create {path} or remove from target.compose_files."
217
312
  )
218
313
  continue
219
- self._scan_compose(path, base_dir, result)
314
+ self._scan_compose(path, base_dir, result, ign)
220
315
 
221
- def _scan_compose(self, path: Path, base_dir: Path, result: AnalysisResult):
316
+ def _scan_compose(self, path: Path, base_dir: Path, result: AnalysisResult, ign: _IgnoreList):
222
317
  try:
223
318
  data = yaml.safe_load(path.read_text()) or {}
224
319
  except Exception as exc:
225
320
  result.add(IssueSeverity.ERROR, "compose", f"Cannot parse {path}: {exc}")
226
321
  return
227
322
 
323
+ def _skip_ignored(check_path: Path) -> bool:
324
+ try:
325
+ rel = check_path.resolve().relative_to(base_dir.resolve())
326
+ return ign.is_ignored(rel)
327
+ except ValueError:
328
+ return False
329
+
228
330
  services = data.get("services", {})
229
331
  for svc_name, svc in services.items():
230
332
  if not isinstance(svc, dict):
@@ -243,7 +345,7 @@ class _ComposeChecker(_Checker):
243
345
  dockerfile = build.get("dockerfile")
244
346
  if dockerfile:
245
347
  df_path = ctx_path / dockerfile
246
- if not df_path.exists():
348
+ if not df_path.exists() and not _skip_ignored(df_path):
247
349
  result.add(
248
350
  IssueSeverity.ERROR, "compose",
249
351
  f"Service '{svc_name}' Dockerfile missing: {df_path}",
@@ -255,7 +357,7 @@ class _ComposeChecker(_Checker):
255
357
  env_files = [env_files]
256
358
  for ef in env_files:
257
359
  ef_path = base_dir / ef if not str(ef).startswith("/") else Path(ef)
258
- if not ef_path.exists():
360
+ if not ef_path.exists() and not _skip_ignored(ef_path):
259
361
  result.add(
260
362
  IssueSeverity.WARNING, "compose",
261
363
  f"Service '{svc_name}' env_file missing: {ef}",
@@ -268,7 +370,7 @@ class _ComposeChecker(_Checker):
268
370
  host_part = vol.split(":", 1)[0]
269
371
  # Skip named volumes and relative paths that exist
270
372
  if host_part.startswith("/"):
271
- if not Path(host_part).exists():
373
+ if not Path(host_part).exists() and not _skip_ignored(Path(host_part)):
272
374
  result.add(
273
375
  IssueSeverity.WARNING, "compose",
274
376
  f"Service '{svc_name}' volume host path missing: {host_part}",
@@ -276,7 +378,7 @@ class _ComposeChecker(_Checker):
276
378
  )
277
379
  elif host_part != "." and "/" in host_part:
278
380
  hp = base_dir / host_part
279
- if not hp.exists():
381
+ if not hp.exists() and not _skip_ignored(hp):
280
382
  result.add(
281
383
  IssueSeverity.WARNING, "compose",
282
384
  f"Service '{svc_name}' relative volume path missing: {hp}",
@@ -593,9 +695,11 @@ class SpecAnalyzer:
593
695
  ]
594
696
 
595
697
  def __init__(self, base_dir: Path | None = None,
596
- checkers: list[_Checker] | None = None):
698
+ checkers: list[_Checker] | None = None,
699
+ auto_create_redeployignore: bool = True):
597
700
  self.base_dir = base_dir or Path.cwd()
598
701
  self.checkers = checkers or list(self.DEFAULT_CHECKERS)
702
+ self.auto_create_redeployignore = auto_create_redeployignore
599
703
 
600
704
  def analyze(self, spec: MigrationSpec,
601
705
  document: MarkpactDocument | None = None) -> AnalysisResult:
@@ -610,6 +714,10 @@ class SpecAnalyzer:
610
714
  from ..markpact.compiler import compile_markpact_document, MarkpactCompileError
611
715
  from ..models.spec import MigrationSpec as MS
612
716
 
717
+ # Ensure .redeployignore exists with sensible defaults
718
+ if self.auto_create_redeployignore:
719
+ ensure_redeployignore(self.base_dir)
720
+
613
721
  document = None
614
722
  spec = None
615
723
  result = AnalysisResult()
@@ -21,6 +21,20 @@ if TYPE_CHECKING:
21
21
  from .progress import ProgressEmitter
22
22
 
23
23
 
24
+ def _format_step_output(stdout: str, stderr: str, max_chars: int = 8000) -> str:
25
+ out = str(stdout or "").strip()
26
+ err = str(stderr or "").strip()
27
+ chunks: list[str] = []
28
+ if out:
29
+ chunks.append("stdout:\n" + out)
30
+ if err:
31
+ chunks.append("stderr:\n" + err)
32
+ if not chunks:
33
+ return "ok"
34
+ joined = "\n\n".join(chunks)
35
+ return joined[:max_chars]
36
+
37
+
24
38
  def run_ssh(step: MigrationStep, probe: RemoteProbe) -> None:
25
39
  """Execute SSH command on remote host."""
26
40
  cmd = step.command
@@ -28,9 +42,9 @@ def run_ssh(step: MigrationStep, probe: RemoteProbe) -> None:
28
42
  raise StepError(step, "No command specified")
29
43
  timeout = step.timeout or 300
30
44
  r = probe.run(cmd, timeout=timeout)
31
- step.result = r.out[:500]
45
+ step.result = _format_step_output(r.out, r.stderr)
32
46
  if not r.ok:
33
- raise StepError(step, f"exit={r.exit_code}: {r.stderr[:200]}")
47
+ raise StepError(step, f"exit={r.exit_code}: {_format_step_output(r.out, r.stderr, 400)}")
34
48
  step.status = StepStatus.DONE
35
49
 
36
50
 
@@ -51,9 +65,9 @@ def run_scp(step: MigrationStep, probe: RemoteProbe, plan: MigrationPlan) -> Non
51
65
  step.src, f"{plan.host}:{step.dst}"]
52
66
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
53
67
  if result.returncode != 0:
54
- raise StepError(step, f"scp failed: {result.stderr[:200]}")
68
+ raise StepError(step, f"scp failed: {_format_step_output(result.stdout, result.stderr, 400)}")
55
69
  step.status = StepStatus.DONE
56
- step.result = "ok"
70
+ step.result = _format_step_output(result.stdout, result.stderr)
57
71
 
58
72
 
59
73
  def run_rsync(step: MigrationStep, probe: RemoteProbe, plan: MigrationPlan) -> None:
@@ -66,15 +80,23 @@ def run_rsync(step: MigrationStep, probe: RemoteProbe, plan: MigrationPlan) -> N
66
80
  else:
67
81
  _ensure_remote_parent_dir(probe, step.dst)
68
82
  dst = f"{plan.host}:{step.dst}"
69
- cmd = ["rsync", "-az", "--delete"]
83
+ cmd = [
84
+ "rsync",
85
+ "-az",
86
+ "--delete",
87
+ "--filter",
88
+ ":- .gitignore",
89
+ "--filter",
90
+ ":- .redeployignore",
91
+ ]
70
92
  for exc in step.excludes:
71
93
  cmd += ["--exclude", exc]
72
94
  cmd += [step.src, dst]
73
95
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
74
96
  if result.returncode != 0:
75
- raise StepError(step, f"rsync failed: {result.stderr[:200]}")
97
+ raise StepError(step, f"rsync failed: {_format_step_output(result.stdout, result.stderr, 400)}")
76
98
  step.status = StepStatus.DONE
77
- step.result = "ok"
99
+ step.result = _format_step_output(result.stdout, result.stderr)
78
100
 
79
101
 
80
102
  def _ensure_remote_parent_dir(probe: RemoteProbe, remote_dst: str) -> None: