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.
- {redeploy-0.2.65 → redeploy-0.2.67}/PKG-INFO +6 -6
- {redeploy-0.2.65 → redeploy-0.2.67}/README.md +5 -5
- {redeploy-0.2.65 → redeploy-0.2.67}/pyproject.toml +1 -1
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/__init__.py +1 -1
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/spec_analyzer.py +119 -11
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/handlers.py +29 -7
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/plan_apply.py +425 -1
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/schema.py +3 -1
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_analyze.py +53 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_executor.py +1 -1
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/PKG-INFO +6 -6
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/checkers/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/analyze/preflight_schema.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/exceptions.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/executor.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/progress.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/rollback.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/state.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/state_apply.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/utils/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/apply/utils/run_container_build.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/audit.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/extractor.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/docker_compose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/generators/migration.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/compose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/hardware.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/infra.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/blueprint/sources/migration.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/audit.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/blueprint.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/bump_fix.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/detect.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/device_map.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/device_map_renderers.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/devices.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/diagnose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/diff.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/exec_.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/export.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/hardware.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/import_.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/init.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/inspect.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/lint.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/mcp_cmd.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/patterns.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/plugin.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/probe.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/prompt_cmd.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/push.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/state.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/status.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/target.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/commands.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/helpers.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/monorepo.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/release.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/scanner.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/changelog_config.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/version/utils/git_config.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/commands/workflow.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/core.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/display.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli/query.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/cli.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/applier.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/config_apply/loader.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/data_sync.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/builtin/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/detector.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/hardware.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/hardware_rules.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/probes.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/remote.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/templates.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/detect/workflow.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/discovery.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/loader.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl/parser.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/context.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/decorators.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/docker_steps.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/exceptions.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/runner.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/dsl_python/steps.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/fleet.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/config_txt.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/hyperpixel.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/official.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/data/waveshare.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/fixes.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/autostart.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/browsers.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/compositors.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/kiosk/output_profiles.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/panels.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/hardware/raspi_config.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/decider.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/hint_provider.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/log_writer.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/loop_detector.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal/runner.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/heal.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/base.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/config_hints.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/docker_compose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/parsers/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/parsers/compose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/iac/registry.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/integrations/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/integrations/op3_bridge.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/compiler.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/models.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/markpact/parser.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/mcp_server.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/blueprint.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/devices.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/enums.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/hardware.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/infra.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/manifest.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/persisted.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/pipeline.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/plan.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/models/spec.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/observe.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/parse.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/patterns.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plan/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plan/planner.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/browser_reload.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/hardware_diagnostic.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/notify.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/process_control.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/plugins/builtin/systemd_reload.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/spec_loader.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/ssh.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/builtins.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/steps/kiosk.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_audit.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_cli.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_data_sync.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_discovery.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_dsl.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_examples.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_fleet.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_git_integration.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_heal.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_heal_decider.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_hooks.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac_compose_contract.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_iac_docker_compose_contract.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_manifest.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_markpact_compiler.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_markpact_parser.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_models.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_observe.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_parse.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_patterns.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_planner_edge.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_plugins.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_probes.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_public_api.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_resume.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_spec_loader.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_ssh.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_templates.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_verify.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_changelog.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_cli.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_manifest.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_version_sources.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/tests/test_workflow.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/verify.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/bump.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/changelog.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/commits.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/diff.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/git_integration.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/git_transaction.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/manifest.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/__init__.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/base.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/json_.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/plain.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/regex.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/toml_.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/sources/yaml_.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version/transaction.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy/version.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/SOURCES.txt +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/dependency_links.txt +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/entry_points.txt +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/requires.txt +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/redeploy.egg-info/top_level.txt +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/setup.cfg +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_blueprint_sources.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_blueprint_sources_compose.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_cli_query.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_argocd_flux_plugin.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_gitops_ci_plugin.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_example_helm_kustomize_plugin.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_config.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_kiosk.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_hardware_rules.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_heal_decider.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_heal_loop_detector.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac_config_hints.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_iac_plugin_loading.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_import_plugin_template.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_parse.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_prompt_llm.py +0 -0
- {redeploy-0.2.65 → redeploy-0.2.67}/tests/test_redeploy.py +0 -0
- {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.
|
|
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
|
-
     
|
|
36
|
+
  
|
|
37
37
|
|
|
38
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
39
|
-
- 👤 **Human dev:** ~$
|
|
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
|
-
    
|
|
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
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
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
|
-
    
|
|
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.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
|
|
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
|
|
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
|
|
68
|
+
raise StepError(step, f"scp failed: {_format_step_output(result.stdout, result.stderr, 400)}")
|
|
55
69
|
step.status = StepStatus.DONE
|
|
56
|
-
step.result =
|
|
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 = [
|
|
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
|
|
97
|
+
raise StepError(step, f"rsync failed: {_format_step_output(result.stdout, result.stderr, 400)}")
|
|
76
98
|
step.status = StepStatus.DONE
|
|
77
|
-
step.result =
|
|
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:
|