redeploy 0.2.63__tar.gz → 0.2.65__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.
- {redeploy-0.2.63 → redeploy-0.2.65}/PKG-INFO +5 -5
- {redeploy-0.2.63 → redeploy-0.2.65}/README.md +4 -4
- {redeploy-0.2.63 → redeploy-0.2.65}/pyproject.toml +1 -1
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/__init__.py +1 -1
- redeploy-0.2.65/redeploy/analyze/__init__.py +14 -0
- redeploy-0.2.65/redeploy/analyze/preflight_schema.py +285 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/analyze/spec_analyzer.py +271 -17
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/handlers.py +23 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/plan_apply.py +51 -1
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/hint_provider.py +134 -10
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/runner.py +10 -2
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plan/planner.py +8 -2
- redeploy-0.2.65/redeploy/tests/test_analyze.py +140 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_executor.py +1 -1
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/PKG-INFO +5 -5
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/SOURCES.txt +2 -0
- redeploy-0.2.63/redeploy/analyze/__init__.py +0 -6
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/analyze/checkers/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/exceptions.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/executor.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/progress.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/rollback.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/state.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/state_apply.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/utils/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/apply/utils/run_container_build.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/audit.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/extractor.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/generators/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/generators/docker_compose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/generators/migration.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/sources/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/sources/compose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/sources/hardware.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/sources/infra.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/blueprint/sources/migration.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/audit.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/blueprint.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/bump_fix.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/detect.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/device_map.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/device_map_renderers.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/devices.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/diagnose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/diff.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/exec_.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/export.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/hardware.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/import_.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/init.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/inspect.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/lint.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/mcp_cmd.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/patterns.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/plugin.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/probe.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/prompt_cmd.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/push.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/state.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/status.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/target.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/commands.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/helpers.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/monorepo.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/release.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/scanner.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/utils/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/utils/changelog_config.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/version/utils/git_config.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/commands/workflow.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/core.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/display.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli/query.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/cli.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/config_apply/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/config_apply/applier.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/config_apply/loader.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/data_sync.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/builtin/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/detector.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/hardware.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/hardware_rules.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/probes.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/remote.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/templates.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/detect/workflow.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/discovery.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl/loader.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl/parser.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/context.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/decorators.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/docker_steps.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/exceptions.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/runner.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/dsl_python/steps.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/fleet.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/config_txt.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/data/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/data/hyperpixel.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/data/official.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/data/waveshare.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/fixes.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/kiosk/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/kiosk/autostart.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/kiosk/browsers.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/kiosk/compositors.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/kiosk/output_profiles.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/panels.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/hardware/raspi_config.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/decider.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/log_writer.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal/loop_detector.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/heal.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/base.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/config_hints.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/docker_compose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/parsers/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/parsers/compose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/iac/registry.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/integrations/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/integrations/op3_bridge.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/markpact/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/markpact/compiler.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/markpact/models.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/markpact/parser.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/mcp_server.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/blueprint.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/devices.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/enums.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/hardware.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/infra.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/manifest.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/persisted.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/pipeline.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/plan.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/models/spec.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/observe.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/parse.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/patterns.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plan/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/browser_reload.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/hardware_diagnostic.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/notify.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/process_control.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/plugins/builtin/systemd_reload.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/schema.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/spec_loader.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/ssh.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/steps/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/steps/builtins.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/steps/kiosk.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_audit.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_cli.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_data_sync.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_discovery.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_dsl.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_examples.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_fleet.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_git_integration.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_heal.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_heal_decider.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_hooks.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_iac.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_iac_compose_contract.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_iac_docker_compose_contract.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_manifest.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_markpact_compiler.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_markpact_parser.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_models.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_observe.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_parse.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_patterns.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_planner_edge.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_plugins.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_probes.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_public_api.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_resume.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_spec_loader.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_ssh.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_templates.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_verify.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_version.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_version_changelog.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_version_cli.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_version_manifest.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_version_sources.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/tests/test_workflow.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/verify.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/bump.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/changelog.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/commits.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/diff.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/git_integration.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/git_transaction.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/manifest.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/__init__.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/base.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/json_.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/plain.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/regex.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/toml_.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/sources/yaml_.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version/transaction.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy/version.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/dependency_links.txt +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/entry_points.txt +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/requires.txt +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/redeploy.egg-info/top_level.txt +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/setup.cfg +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_blueprint_sources.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_blueprint_sources_compose.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_cli_query.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_example_argocd_flux_plugin.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_example_gitops_ci_plugin.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_example_helm_kustomize_plugin.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_hardware_config.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_hardware_kiosk.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_hardware_rules.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_heal_decider.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_heal_loop_detector.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_iac.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_iac_config_hints.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_iac_plugin_loading.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_import_plugin_template.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_parse.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_prompt_llm.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/tests/test_redeploy.py +0 -0
- {redeploy-0.2.63 → redeploy-0.2.65}/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.
|
|
3
|
+
Version: 0.2.65
|
|
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
|
-
    
|
|
36
36
|
  
|
|
37
37
|
|
|
38
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
39
|
-
- 👤 **Human dev:** ~$
|
|
38
|
+
- 🤖 **LLM usage:** $7.5000 (85 commits)
|
|
39
|
+
- 👤 **Human dev:** ~$3184 (31.8h @ $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
|
-
    
|
|
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
|
-
    
|
|
7
7
|
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $7.5000 (85 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$3184 (31.8h @ $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
|
-
    
|
|
17
17
|
|
|
18
18
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
19
19
|
|
|
@@ -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.
|
|
10
|
+
__version__ = "0.2.65"
|
|
11
11
|
|
|
12
12
|
# ── Core models ───────────────────────────────────────────────────────────────
|
|
13
13
|
from .models import ( # noqa: F401
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Static analysis for migration specs — pre-flight checks before plan/apply."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .spec_analyzer import SpecAnalyzer, AnalysisResult, IssueSeverity
|
|
5
|
+
from .preflight_schema import PreflightResult, generate_preflight_schema, save_preflight_schema
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"SpecAnalyzer",
|
|
9
|
+
"AnalysisResult",
|
|
10
|
+
"IssueSeverity",
|
|
11
|
+
"PreflightResult",
|
|
12
|
+
"generate_preflight_schema",
|
|
13
|
+
"save_preflight_schema",
|
|
14
|
+
]
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Operational preflight schema generation for redeploy runs.
|
|
2
|
+
|
|
3
|
+
Produces a machine-readable snapshot before apply:
|
|
4
|
+
- resolved step graph (ordering refs, command refs)
|
|
5
|
+
- local artifact existence checks
|
|
6
|
+
- optional remote path probes (best-effort)
|
|
7
|
+
- blockers suitable for fail-fast gating
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import re
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
import yaml
|
|
18
|
+
|
|
19
|
+
from ..ssh import SshClient
|
|
20
|
+
from .spec_analyzer import AnalysisResult, IssueSeverity
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class PreflightResult:
|
|
25
|
+
schema: dict[str, Any]
|
|
26
|
+
blockers: list[dict[str, Any]]
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def has_blockers(self) -> bool:
|
|
30
|
+
return bool(self.blockers)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def generate_preflight_schema(
|
|
34
|
+
*,
|
|
35
|
+
spec_path: Path,
|
|
36
|
+
spec,
|
|
37
|
+
migration,
|
|
38
|
+
lint_result: AnalysisResult | None,
|
|
39
|
+
base_dir: Path,
|
|
40
|
+
remote_check: bool = True,
|
|
41
|
+
) -> PreflightResult:
|
|
42
|
+
blockers: list[dict[str, Any]] = []
|
|
43
|
+
|
|
44
|
+
lint_issues = []
|
|
45
|
+
if lint_result is not None:
|
|
46
|
+
for issue in lint_result.issues:
|
|
47
|
+
lint_issues.append({
|
|
48
|
+
"severity": issue.severity.value,
|
|
49
|
+
"category": issue.category,
|
|
50
|
+
"step_id": issue.step_id,
|
|
51
|
+
"message": issue.message,
|
|
52
|
+
"suggestion": issue.suggestion,
|
|
53
|
+
})
|
|
54
|
+
if issue.severity == IssueSeverity.ERROR:
|
|
55
|
+
blockers.append({
|
|
56
|
+
"type": "lint_error",
|
|
57
|
+
"step_id": issue.step_id,
|
|
58
|
+
"message": issue.message,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
command_refs = _resolve_command_refs(spec, spec_path, blockers)
|
|
62
|
+
local_artifacts = _collect_local_artifacts(spec, base_dir, blockers)
|
|
63
|
+
|
|
64
|
+
remote = {
|
|
65
|
+
"enabled": bool(remote_check),
|
|
66
|
+
"host": migration.host,
|
|
67
|
+
"reachable": None,
|
|
68
|
+
"checks": [],
|
|
69
|
+
}
|
|
70
|
+
if remote_check:
|
|
71
|
+
remote = _collect_remote_checks(spec, migration.host)
|
|
72
|
+
|
|
73
|
+
schema = {
|
|
74
|
+
"schema_version": "1.0",
|
|
75
|
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
76
|
+
"spec": {
|
|
77
|
+
"path": str(spec_path),
|
|
78
|
+
"name": getattr(spec, "name", ""),
|
|
79
|
+
"source_strategy": spec.source.strategy.value,
|
|
80
|
+
"target_strategy": spec.target.strategy.value,
|
|
81
|
+
"host": spec.source.host,
|
|
82
|
+
},
|
|
83
|
+
"plan": {
|
|
84
|
+
"steps_total": len(migration.steps),
|
|
85
|
+
"risk": migration.risk.value,
|
|
86
|
+
"estimated_downtime": migration.estimated_downtime,
|
|
87
|
+
"steps": [
|
|
88
|
+
{
|
|
89
|
+
"id": s.id,
|
|
90
|
+
"action": s.action.value,
|
|
91
|
+
"description": s.description,
|
|
92
|
+
"has_command_ref": bool(getattr(s, "command_ref", None)),
|
|
93
|
+
"insert_before": getattr(s, "insert_before", None),
|
|
94
|
+
}
|
|
95
|
+
for s in migration.steps
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
"checks": {
|
|
99
|
+
"lint": {
|
|
100
|
+
"enabled": lint_result is not None,
|
|
101
|
+
"errors": len(lint_result.errors()) if lint_result else 0,
|
|
102
|
+
"warnings": len(lint_result.warnings()) if lint_result else 0,
|
|
103
|
+
"issues": lint_issues,
|
|
104
|
+
},
|
|
105
|
+
"command_refs": command_refs,
|
|
106
|
+
"local_artifacts": local_artifacts,
|
|
107
|
+
"remote": remote,
|
|
108
|
+
},
|
|
109
|
+
"blockers": blockers,
|
|
110
|
+
"summary": {
|
|
111
|
+
"blockers": len(blockers),
|
|
112
|
+
"local_missing": sum(1 for x in local_artifacts if not x["exists"]),
|
|
113
|
+
"refs_missing": sum(1 for x in command_refs if not x["resolved"]),
|
|
114
|
+
"remote_failed_checks": sum(1 for x in remote.get("checks", []) if x.get("ok") is False),
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return PreflightResult(schema=schema, blockers=blockers)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def save_preflight_schema(schema: dict[str, Any], output_path: Path) -> None:
|
|
122
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
123
|
+
output_path.write_text(yaml.safe_dump(schema, sort_keys=False, allow_unicode=True), encoding="utf-8")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _resolve_command_refs(spec, spec_path: Path, blockers: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
127
|
+
from ..markpact.parser import extract_script_by_ref, extract_script_from_markdown
|
|
128
|
+
|
|
129
|
+
out: list[dict[str, Any]] = []
|
|
130
|
+
for step in spec.extra_steps:
|
|
131
|
+
sid = step.get("id")
|
|
132
|
+
cref = step.get("command_ref")
|
|
133
|
+
if not cref:
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
entry = {
|
|
137
|
+
"step_id": sid,
|
|
138
|
+
"command_ref": cref,
|
|
139
|
+
"file": None,
|
|
140
|
+
"ref": None,
|
|
141
|
+
"resolved": False,
|
|
142
|
+
"mode": None,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if "#" in cref:
|
|
146
|
+
file_part, ref_id = cref.split("#", 1)
|
|
147
|
+
ref_file = (spec_path.parent / file_part).resolve() if file_part else spec_path.resolve()
|
|
148
|
+
entry["file"] = str(ref_file)
|
|
149
|
+
entry["ref"] = ref_id
|
|
150
|
+
if not ref_file.exists():
|
|
151
|
+
out.append(entry)
|
|
152
|
+
blockers.append({
|
|
153
|
+
"type": "missing_command_ref_file",
|
|
154
|
+
"step_id": sid,
|
|
155
|
+
"message": f"command_ref file missing: {file_part}",
|
|
156
|
+
})
|
|
157
|
+
continue
|
|
158
|
+
text = ref_file.read_text(encoding="utf-8", errors="replace")
|
|
159
|
+
by_ref = extract_script_by_ref(text, ref_id, language="bash")
|
|
160
|
+
by_heading = extract_script_from_markdown(text, ref_id, language="bash")
|
|
161
|
+
if by_ref is not None:
|
|
162
|
+
entry["resolved"] = True
|
|
163
|
+
entry["mode"] = "markpact_ref"
|
|
164
|
+
elif by_heading is not None:
|
|
165
|
+
entry["resolved"] = True
|
|
166
|
+
entry["mode"] = "heading"
|
|
167
|
+
else:
|
|
168
|
+
blockers.append({
|
|
169
|
+
"type": "missing_command_ref",
|
|
170
|
+
"step_id": sid,
|
|
171
|
+
"message": f"command_ref '#{ref_id}' not found in {ref_file}",
|
|
172
|
+
})
|
|
173
|
+
out.append(entry)
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
# Simple ref id in current spec markdown.
|
|
177
|
+
ref_file = spec_path.resolve()
|
|
178
|
+
entry["file"] = str(ref_file)
|
|
179
|
+
entry["ref"] = cref
|
|
180
|
+
text = ref_file.read_text(encoding="utf-8", errors="replace") if ref_file.exists() else ""
|
|
181
|
+
by_ref = extract_script_by_ref(text, cref, language="bash")
|
|
182
|
+
by_heading = extract_script_from_markdown(text, cref, language="bash")
|
|
183
|
+
if by_ref is not None:
|
|
184
|
+
entry["resolved"] = True
|
|
185
|
+
entry["mode"] = "markpact_ref"
|
|
186
|
+
elif by_heading is not None:
|
|
187
|
+
entry["resolved"] = True
|
|
188
|
+
entry["mode"] = "heading"
|
|
189
|
+
else:
|
|
190
|
+
blockers.append({
|
|
191
|
+
"type": "missing_command_ref",
|
|
192
|
+
"step_id": sid,
|
|
193
|
+
"message": f"command_ref '{cref}' not found in {ref_file}",
|
|
194
|
+
})
|
|
195
|
+
out.append(entry)
|
|
196
|
+
|
|
197
|
+
return out
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _collect_local_artifacts(spec, base_dir: Path, blockers: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
201
|
+
items: dict[tuple[str, str], dict[str, Any]] = {}
|
|
202
|
+
|
|
203
|
+
def add(path_raw: str, kind: str, step_id: str | None):
|
|
204
|
+
key = (kind, path_raw)
|
|
205
|
+
if key not in items:
|
|
206
|
+
resolved = _resolve_local_path(path_raw, base_dir)
|
|
207
|
+
exists = resolved.exists() if resolved is not None else False
|
|
208
|
+
items[key] = {
|
|
209
|
+
"kind": kind,
|
|
210
|
+
"path": path_raw,
|
|
211
|
+
"resolved": str(resolved) if resolved else None,
|
|
212
|
+
"exists": bool(exists),
|
|
213
|
+
"steps": [],
|
|
214
|
+
}
|
|
215
|
+
if not exists and kind in {"src", "config_file", "env_file"}:
|
|
216
|
+
blockers.append({
|
|
217
|
+
"type": "missing_local_artifact",
|
|
218
|
+
"step_id": step_id,
|
|
219
|
+
"message": f"Missing local {kind}: {path_raw}",
|
|
220
|
+
})
|
|
221
|
+
if step_id and step_id not in items[key]["steps"]:
|
|
222
|
+
items[key]["steps"].append(step_id)
|
|
223
|
+
|
|
224
|
+
# target-level env file
|
|
225
|
+
target_env = getattr(spec.target, "env_file", None) or getattr(spec.source, "env_file", None)
|
|
226
|
+
if target_env:
|
|
227
|
+
add(str(target_env), "env_file", None)
|
|
228
|
+
|
|
229
|
+
for step in spec.extra_steps:
|
|
230
|
+
sid = step.get("id")
|
|
231
|
+
for field in ("src", "config_file"):
|
|
232
|
+
val = step.get(field)
|
|
233
|
+
if val:
|
|
234
|
+
add(str(val), field, sid)
|
|
235
|
+
|
|
236
|
+
return sorted(items.values(), key=lambda x: (x["kind"], x["path"]))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _collect_remote_checks(spec, host: str) -> dict[str, Any]:
|
|
240
|
+
probe = SshClient(host)
|
|
241
|
+
reachable = probe.is_reachable(timeout=10)
|
|
242
|
+
out = {
|
|
243
|
+
"enabled": True,
|
|
244
|
+
"host": host,
|
|
245
|
+
"reachable": reachable,
|
|
246
|
+
"checks": [],
|
|
247
|
+
}
|
|
248
|
+
if not reachable:
|
|
249
|
+
out["checks"].append({
|
|
250
|
+
"type": "ssh_reachability",
|
|
251
|
+
"ok": False,
|
|
252
|
+
"message": f"Cannot reach host via SSH: {host}",
|
|
253
|
+
})
|
|
254
|
+
return out
|
|
255
|
+
|
|
256
|
+
# Best-effort path checks for common remote dependencies in commands.
|
|
257
|
+
remote_paths: set[str] = set()
|
|
258
|
+
path_re = re.compile(r"(~\/[^\s'\";&|)]+|\/home\/[^\s'\";&|)]+)")
|
|
259
|
+
for step in spec.extra_steps:
|
|
260
|
+
cmd = step.get("command") or ""
|
|
261
|
+
for m in path_re.finditer(cmd):
|
|
262
|
+
remote_paths.add(m.group(1).rstrip("/"))
|
|
263
|
+
|
|
264
|
+
for p in sorted(remote_paths):
|
|
265
|
+
cmd = f"test -e {p}"
|
|
266
|
+
r = probe.run(cmd, timeout=20)
|
|
267
|
+
out["checks"].append({
|
|
268
|
+
"type": "remote_path_exists",
|
|
269
|
+
"path": p,
|
|
270
|
+
"ok": bool(r.ok),
|
|
271
|
+
"message": "exists" if r.ok else (r.stderr or "missing"),
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
return out
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _resolve_local_path(val: str, base_dir: Path) -> Path | None:
|
|
278
|
+
val = val.strip()
|
|
279
|
+
if re.search(r"^[\w.-]+@", val):
|
|
280
|
+
return None
|
|
281
|
+
if val.startswith("~/"):
|
|
282
|
+
return Path.home() / val[2:]
|
|
283
|
+
if val.startswith("/"):
|
|
284
|
+
return Path(val)
|
|
285
|
+
return (base_dir / val).resolve()
|