redeploy 0.2.6__tar.gz → 0.2.7__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.6 → redeploy-0.2.7}/PKG-INFO +6 -6
- {redeploy-0.2.6 → redeploy-0.2.7}/README.md +5 -5
- {redeploy-0.2.6 → redeploy-0.2.7}/pyproject.toml +1 -1
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/__init__.py +1 -1
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/cli.py +135 -35
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_version_cli.py +54 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/PKG-INFO +6 -6
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/apply/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/apply/executor.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/data_sync.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/detector.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/probes.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/remote.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/templates.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/detect/workflow.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/discovery.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl/loader.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl/parser.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/context.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/decorators.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/docker_steps.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/exceptions.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/runner.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/dsl_python/steps.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/fleet.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/base.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/docker_compose.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/parsers/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/parsers/compose.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/iac/registry.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/models.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/observe.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/parse.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/patterns.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plan/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plan/planner.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plugins/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plugins/builtin/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plugins/builtin/browser_reload.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plugins/builtin/notify.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/plugins/builtin/systemd_reload.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/ssh.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/steps.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_cli.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_data_sync.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_discovery.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_dsl.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_examples.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_executor.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_fleet.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_git_integration.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_iac.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_manifest.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_models.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_observe.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_parse.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_patterns.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_planner_edge.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_plugins.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_probes.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_public_api.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_ssh.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_templates.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_verify.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_version.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_version_changelog.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_version_manifest.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_version_sources.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/tests/test_workflow.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/verify.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/bump.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/changelog.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/commits.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/diff.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/git_integration.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/git_transaction.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/manifest.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/__init__.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/base.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/json_.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/plain.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/regex.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/toml_.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/sources/yaml_.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version/transaction.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy/version.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/SOURCES.txt +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/dependency_links.txt +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/entry_points.txt +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/requires.txt +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/redeploy.egg-info/top_level.txt +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/setup.cfg +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/tests/test_iac.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/tests/test_parse.py +0 -0
- {redeploy-0.2.6 → redeploy-0.2.7}/tests/test_redeploy.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.7
|
|
4
4
|
Summary: Infrastructure migration toolkit: detect → plan → apply
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -26,17 +26,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
|
26
26
|
|
|
27
27
|
## AI Cost Tracking
|
|
28
28
|
|
|
29
|
-
    
|
|
30
|
+
  
|
|
31
31
|
|
|
32
|
-
- 🤖 **LLM usage:** $3.
|
|
33
|
-
- 👤 **Human dev:** ~$
|
|
32
|
+
- 🤖 **LLM usage:** $3.9000 (26 commits)
|
|
33
|
+
- 👤 **Human dev:** ~$759 (7.6h @ $100/h, 30min dedup)
|
|
34
34
|
|
|
35
35
|
Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
-
    
|
|
40
40
|
|
|
41
41
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
42
42
|
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $3.
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $3.9000 (26 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$759 (7.6h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-04-20 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.7"
|
|
11
11
|
|
|
12
12
|
# ── Core models ───────────────────────────────────────────────────────────────
|
|
13
13
|
from .models import ( # noqa: F401
|
|
@@ -2969,46 +2969,146 @@ def version_init(scan, force):
|
|
|
2969
2969
|
|
|
2970
2970
|
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2971
2971
|
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
# Check common locations
|
|
2976
|
-
if Path("VERSION").exists():
|
|
2977
|
-
sources.append(SourceConfig(path=Path("VERSION"), format="plain"))
|
|
2978
|
-
if Path("pyproject.toml").exists():
|
|
2979
|
-
sources.append(SourceConfig(path=Path("pyproject.toml"), format="toml", key="project.version"))
|
|
2980
|
-
if Path("package.json").exists():
|
|
2981
|
-
sources.append(SourceConfig(path=Path("package.json"), format="json", key="version"))
|
|
2982
|
-
|
|
2983
|
-
if not sources:
|
|
2984
|
-
# Default minimal manifest
|
|
2985
|
-
sources = [SourceConfig(path=Path("VERSION"), format="plain")]
|
|
2986
|
-
|
|
2987
|
-
# Detect current version
|
|
2988
|
-
current = "0.1.0"
|
|
2989
|
-
for s in sources:
|
|
2990
|
-
if s.path.exists():
|
|
2991
|
-
try:
|
|
2992
|
-
from .version.sources import get_adapter
|
|
2993
|
-
current = get_adapter(s.format).read(s.path, s)
|
|
2994
|
-
break
|
|
2995
|
-
except Exception:
|
|
2996
|
-
pass
|
|
2972
|
+
root = Path.cwd()
|
|
2973
|
+
root_sources = []
|
|
2974
|
+
packages = None
|
|
2997
2975
|
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
2976
|
+
if scan:
|
|
2977
|
+
root_sources = _detect_version_sources_in_dir(root, root)
|
|
2978
|
+
packages = _scan_package_version_manifests(root)
|
|
2979
|
+
|
|
2980
|
+
if packages:
|
|
2981
|
+
current = _detect_source_version(root_sources, default="0.0.0") if root_sources else "0.0.0"
|
|
2982
|
+
m = VersionManifest(
|
|
2983
|
+
version=current,
|
|
2984
|
+
scheme="semver",
|
|
2985
|
+
policy="independent",
|
|
2986
|
+
sources=root_sources,
|
|
2987
|
+
git=GitConfig(),
|
|
2988
|
+
packages=packages,
|
|
2989
|
+
)
|
|
2990
|
+
else:
|
|
2991
|
+
sources = root_sources
|
|
2992
|
+
if not sources:
|
|
2993
|
+
sources = [SourceConfig(path=Path("VERSION"), format="plain")]
|
|
2994
|
+
|
|
2995
|
+
current = _detect_source_version(sources, default="0.1.0")
|
|
2996
|
+
m = VersionManifest(
|
|
2997
|
+
version=current,
|
|
2998
|
+
scheme="semver",
|
|
2999
|
+
policy="synced",
|
|
3000
|
+
sources=sources,
|
|
3001
|
+
git=GitConfig(),
|
|
3002
|
+
)
|
|
3005
3003
|
|
|
3006
3004
|
m.save(manifest_path)
|
|
3007
3005
|
console.print(f"[green]✓ Created {manifest_path}[/green]")
|
|
3008
3006
|
console.print(f" Current version: {current}")
|
|
3009
|
-
console.print(f"
|
|
3010
|
-
|
|
3011
|
-
|
|
3007
|
+
console.print(f" Policy: {m.policy}")
|
|
3008
|
+
|
|
3009
|
+
if m.sources:
|
|
3010
|
+
console.print(f" Sources: {len(m.sources)}")
|
|
3011
|
+
for source in m.sources:
|
|
3012
|
+
console.print(f" - {source.path} ({source.format})")
|
|
3013
|
+
|
|
3014
|
+
if m.is_monorepo():
|
|
3015
|
+
console.print(f" Packages: {len(m.packages or {})}")
|
|
3016
|
+
for package_name in m.list_packages():
|
|
3017
|
+
package = m.get_package(package_name)
|
|
3018
|
+
console.print(f" - {package_name}: {package.version} ({len(package.sources)} sources)")
|
|
3019
|
+
|
|
3020
|
+
|
|
3021
|
+
def _version_scan_specs():
|
|
3022
|
+
return [
|
|
3023
|
+
("VERSION", "plain", None),
|
|
3024
|
+
("pyproject.toml", "toml", "project.version"),
|
|
3025
|
+
("package.json", "json", "version"),
|
|
3026
|
+
]
|
|
3027
|
+
|
|
3028
|
+
|
|
3029
|
+
def _is_scannable_version_path(path: Path) -> bool:
|
|
3030
|
+
ignored = {".git", ".venv", ".redeploy", "node_modules", "__pycache__", "dist", "build"}
|
|
3031
|
+
return not any(part in ignored for part in path.parts)
|
|
3032
|
+
|
|
3033
|
+
|
|
3034
|
+
def _detect_version_sources_in_dir(directory: Path, workspace_root: Path):
|
|
3035
|
+
from .version.manifest import SourceConfig
|
|
3036
|
+
|
|
3037
|
+
sources = []
|
|
3038
|
+
for filename, format_name, key in _version_scan_specs():
|
|
3039
|
+
candidate = directory / filename
|
|
3040
|
+
if not candidate.exists() or not candidate.is_file():
|
|
3041
|
+
continue
|
|
3042
|
+
|
|
3043
|
+
relative_path = candidate.relative_to(workspace_root)
|
|
3044
|
+
if not _is_scannable_version_path(relative_path):
|
|
3045
|
+
continue
|
|
3046
|
+
|
|
3047
|
+
source_kwargs = {
|
|
3048
|
+
"path": relative_path,
|
|
3049
|
+
"format": format_name,
|
|
3050
|
+
}
|
|
3051
|
+
if key is not None:
|
|
3052
|
+
source_kwargs["key"] = key
|
|
3053
|
+
sources.append(SourceConfig(**source_kwargs))
|
|
3054
|
+
|
|
3055
|
+
return sources
|
|
3056
|
+
|
|
3057
|
+
|
|
3058
|
+
def _detect_source_version(sources, *, default: str):
|
|
3059
|
+
from .version.sources import get_adapter
|
|
3060
|
+
|
|
3061
|
+
for source in sources:
|
|
3062
|
+
try:
|
|
3063
|
+
return get_adapter(source.format).read(source.path, source)
|
|
3064
|
+
except Exception:
|
|
3065
|
+
continue
|
|
3066
|
+
return default
|
|
3067
|
+
|
|
3068
|
+
|
|
3069
|
+
def _derive_scanned_package_name(package_dir: Path, workspace_root: Path, used_names: set[str]) -> str:
|
|
3070
|
+
relative_dir = package_dir.relative_to(workspace_root)
|
|
3071
|
+
if len(relative_dir.parts) > 1 and relative_dir.parts[0] in {"packages", "apps", "services", "modules"}:
|
|
3072
|
+
candidate = relative_dir.parts[-1]
|
|
3073
|
+
else:
|
|
3074
|
+
candidate = relative_dir.as_posix()
|
|
3075
|
+
|
|
3076
|
+
if candidate in used_names:
|
|
3077
|
+
candidate = relative_dir.as_posix().replace("/", "-")
|
|
3078
|
+
|
|
3079
|
+
return candidate
|
|
3080
|
+
|
|
3081
|
+
|
|
3082
|
+
def _scan_package_version_manifests(workspace_root: Path):
|
|
3083
|
+
from .version.manifest import PackageConfig
|
|
3084
|
+
|
|
3085
|
+
package_dirs = set()
|
|
3086
|
+
for filename, _, _ in _version_scan_specs():
|
|
3087
|
+
for candidate in workspace_root.rglob(filename):
|
|
3088
|
+
if not candidate.is_file():
|
|
3089
|
+
continue
|
|
3090
|
+
relative_path = candidate.relative_to(workspace_root)
|
|
3091
|
+
if not _is_scannable_version_path(relative_path):
|
|
3092
|
+
continue
|
|
3093
|
+
if candidate.parent == workspace_root:
|
|
3094
|
+
continue
|
|
3095
|
+
package_dirs.add(candidate.parent)
|
|
3096
|
+
|
|
3097
|
+
packages = {}
|
|
3098
|
+
used_names = set()
|
|
3099
|
+
for package_dir in sorted(package_dirs, key=lambda item: item.as_posix()):
|
|
3100
|
+
sources = _detect_version_sources_in_dir(package_dir, workspace_root)
|
|
3101
|
+
if not sources:
|
|
3102
|
+
continue
|
|
3103
|
+
|
|
3104
|
+
package_name = _derive_scanned_package_name(package_dir, workspace_root, used_names)
|
|
3105
|
+
used_names.add(package_name)
|
|
3106
|
+
packages[package_name] = PackageConfig(
|
|
3107
|
+
version=_detect_source_version(sources, default="0.1.0"),
|
|
3108
|
+
sources=sources,
|
|
3109
|
+
)
|
|
3110
|
+
|
|
3111
|
+
return packages or None
|
|
3012
3112
|
|
|
3013
3113
|
|
|
3014
3114
|
@version_cmd.command(name="diff")
|
|
@@ -238,6 +238,60 @@ class TestVersionCurrent:
|
|
|
238
238
|
assert "2.0.0" in result.output
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
class TestVersionInit:
|
|
242
|
+
def test_init_scan_creates_synced_manifest_from_root_sources(self, tmp_path):
|
|
243
|
+
runner = _runner()
|
|
244
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
245
|
+
Path("VERSION").write_text("2.3.4\n", encoding="utf-8")
|
|
246
|
+
|
|
247
|
+
result = runner.invoke(
|
|
248
|
+
cli,
|
|
249
|
+
["version", "init", "--scan"],
|
|
250
|
+
catch_exceptions=False,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
assert result.exit_code == 0, result.output
|
|
254
|
+
manifest = yaml.safe_load(Path(".redeploy/version.yaml").read_text(encoding="utf-8"))
|
|
255
|
+
assert manifest["version"]["version"] == "2.3.4"
|
|
256
|
+
assert manifest["version"]["policy"] == "synced"
|
|
257
|
+
assert manifest["version"]["sources"] == [
|
|
258
|
+
{"path": "VERSION", "format": "plain", "optional": False}
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
def test_init_scan_creates_independent_manifest_from_package_dirs(self, tmp_path):
|
|
262
|
+
runner = _runner()
|
|
263
|
+
with runner.isolated_filesystem(temp_dir=tmp_path):
|
|
264
|
+
Path("backend").mkdir(parents=True, exist_ok=True)
|
|
265
|
+
Path("frontend").mkdir(parents=True, exist_ok=True)
|
|
266
|
+
Path("backend/VERSION").write_text("1.0.0\n", encoding="utf-8")
|
|
267
|
+
Path("frontend/package.json").write_text(
|
|
268
|
+
'{"name": "frontend", "version": "2.0.0"}\n',
|
|
269
|
+
encoding="utf-8",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
result = runner.invoke(
|
|
273
|
+
cli,
|
|
274
|
+
["version", "init", "--scan"],
|
|
275
|
+
catch_exceptions=False,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
assert result.exit_code == 0, result.output
|
|
279
|
+
manifest = yaml.safe_load(Path(".redeploy/version.yaml").read_text(encoding="utf-8"))
|
|
280
|
+
assert manifest["version"]["policy"] == "independent"
|
|
281
|
+
assert manifest["version"]["version"] == "0.0.0"
|
|
282
|
+
assert manifest["version"].get("sources", []) == []
|
|
283
|
+
assert manifest["version"]["packages"]["backend"]["version"] == "1.0.0"
|
|
284
|
+
assert manifest["version"]["packages"]["backend"]["sources"] == [
|
|
285
|
+
{"path": "backend/VERSION", "format": "plain", "optional": False}
|
|
286
|
+
]
|
|
287
|
+
assert manifest["version"]["packages"]["frontend"]["version"] == "2.0.0"
|
|
288
|
+
assert manifest["version"]["packages"]["frontend"]["sources"] == [
|
|
289
|
+
{"path": "frontend/package.json", "format": "json", "key": "version", "optional": False}
|
|
290
|
+
]
|
|
291
|
+
assert "Policy: independent" in result.output
|
|
292
|
+
assert "Packages: 2" in result.output
|
|
293
|
+
|
|
294
|
+
|
|
241
295
|
class TestVersionVerify:
|
|
242
296
|
def test_verify_all_packages_succeeds_when_in_sync(self, tmp_path):
|
|
243
297
|
runner = _runner()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redeploy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Infrastructure migration toolkit: detect → plan → apply
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -26,17 +26,17 @@ Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
|
26
26
|
|
|
27
27
|
## AI Cost Tracking
|
|
28
28
|
|
|
29
|
-
    
|
|
30
|
+
  
|
|
31
31
|
|
|
32
|
-
- 🤖 **LLM usage:** $3.
|
|
33
|
-
- 👤 **Human dev:** ~$
|
|
32
|
+
- 🤖 **LLM usage:** $3.9000 (26 commits)
|
|
33
|
+
- 👤 **Human dev:** ~$759 (7.6h @ $100/h, 30min dedup)
|
|
34
34
|
|
|
35
35
|
Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
-
    
|
|
40
40
|
|
|
41
41
|
Infrastructure migration and device deploy toolkit — VPS, Raspberry Pi kiosk, Podman Quadlet, k3s.
|
|
42
42
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|