vm-tool 1.0.41__tar.gz → 1.0.42__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.
- {vm_tool-1.0.41 → vm_tool-1.0.42}/PKG-INFO +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.42}/pyproject.toml +2 -2
- {vm_tool-1.0.41 → vm_tool-1.0.42}/setup.py +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/cli.py +43 -1
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/runner.py +146 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/push_code_tasks.yml +25 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/PKG-INFO +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.devcontainer/devcontainer.json +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.github/dependabot.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.github/workflows/ci.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.github/workflows/deploy.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.github/workflows/publish.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.gitignore +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/.pre-commit-config.yaml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/CONTRIBUTING +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/CONTRIBUTING.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/LICENSE +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/MANIFEST.in +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/Makefile +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/codePushToGithub.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/MODULE_GUIDE.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/deployment-approaches.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/ec2-github-actions-guide.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/features.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/generator.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/index.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/pipeline-generator.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/reference/runner.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/reference/ssh.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/ssh-key-setup.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/docs/usage.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/cloud/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/cloud/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/cloud/ssh_identity_file.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/cloud/ssh_password.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/cloud/template_cloud_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/deploy_full_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/docker-compose.example.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/ec2-setup.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/github-actions-ec2.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/github-actions-full-setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/local/.keep +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/local/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/local/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/local/template_local_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/production-deploy.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/rollback.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/setup.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/ssh_key_management.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/examples/version_check.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/mkdocs.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/molecule/default/converge.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/molecule/default/molecule.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/molecule/default/verify.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/requirements-docs.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/requirements.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/runtime.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/setup.cfg +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/conftest.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/integration/test_deployment.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_config.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_generator.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_health.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_history.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_logging.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_runner.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_ssh.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/tests/test_state.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/alerting.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/audit.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/backup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/benchmarking.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/cloud.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/completion.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/compliance.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/config.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/drift.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/generator.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/health.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/history.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/kubernetes.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/metrics.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/notifications.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/plugins.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/policy.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/rbac.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/recovery.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/reporting.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/secrets.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/ssh.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/state.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/strategies/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/strategies/ab_testing.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/strategies/blue_green.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/strategies/canary.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/validation.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/cleanup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/docker/create_docker_service.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/docker/docker_setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/docker/install_docker_and_compose.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/docker/login_to_docker_hub.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/github/git_configuration.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/inventory.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/k8s.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/main.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/monitoring.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/project_service.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/push_code.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/vm_setup/setup_project_env.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool/webhooks.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/SOURCES.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/dependency_links.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/entry_points.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/requires.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.42}/vm_tool.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vm_tool"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.42"
|
|
8
8
|
description = "A Comprehensive Tool for Setting Up Virtual Machines."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -76,7 +76,7 @@ include = '\.pyi?$'
|
|
|
76
76
|
profile = "black"
|
|
77
77
|
multi_line_output = 3
|
|
78
78
|
[tool.bumpversion]
|
|
79
|
-
current_version = "1.0.
|
|
79
|
+
current_version = "1.0.42"
|
|
80
80
|
commit = true
|
|
81
81
|
tag = true
|
|
82
82
|
|
|
@@ -12,7 +12,7 @@ else:
|
|
|
12
12
|
|
|
13
13
|
setup(
|
|
14
14
|
name="vm_tool",
|
|
15
|
-
version="1.0.
|
|
15
|
+
version="1.0.42", # This will be updated by bump2version
|
|
16
16
|
packages=find_packages(),
|
|
17
17
|
description="A Comprehensive Tool for Setting Up Virtual Machines.",
|
|
18
18
|
long_description=long_description,
|
|
@@ -6,7 +6,7 @@ def main():
|
|
|
6
6
|
parser = argparse.ArgumentParser(
|
|
7
7
|
description="VM Tool: Setup, Provision, and Manage VMs"
|
|
8
8
|
)
|
|
9
|
-
parser.add_argument("--version", action="version", version="1.0.
|
|
9
|
+
parser.add_argument("--version", action="version", version="1.0.42")
|
|
10
10
|
parser.add_argument(
|
|
11
11
|
"--verbose", "-v", action="store_true", help="Enable verbose output"
|
|
12
12
|
)
|
|
@@ -168,6 +168,29 @@ def main():
|
|
|
168
168
|
"--inventory", type=str, default="inventory.yml", help="Inventory file to use"
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
+
# Hydrate Env command
|
|
172
|
+
hydrate_parser = subparsers.add_parser(
|
|
173
|
+
"hydrate-env", help="Hydrate missing env files from secrets"
|
|
174
|
+
)
|
|
175
|
+
hydrate_parser.add_argument(
|
|
176
|
+
"--compose-file",
|
|
177
|
+
type=str,
|
|
178
|
+
default="docker-compose.yml",
|
|
179
|
+
help="Path to docker-compose.yml",
|
|
180
|
+
)
|
|
181
|
+
hydrate_parser.add_argument(
|
|
182
|
+
"--secrets",
|
|
183
|
+
type=str,
|
|
184
|
+
required=True,
|
|
185
|
+
help="JSON string of GitHub secrets",
|
|
186
|
+
)
|
|
187
|
+
hydrate_parser.add_argument(
|
|
188
|
+
"--project-root",
|
|
189
|
+
type=str,
|
|
190
|
+
default=".",
|
|
191
|
+
help="Project root directory",
|
|
192
|
+
)
|
|
193
|
+
|
|
171
194
|
# Docker Deploy command
|
|
172
195
|
docker_parser = subparsers.add_parser(
|
|
173
196
|
"deploy-docker", help="Deploy using Docker Compose"
|
|
@@ -637,6 +660,25 @@ def main():
|
|
|
637
660
|
print(f"Error: {e}")
|
|
638
661
|
sys.exit(1)
|
|
639
662
|
|
|
663
|
+
elif args.command == "hydrate-env":
|
|
664
|
+
try:
|
|
665
|
+
import json
|
|
666
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig
|
|
667
|
+
|
|
668
|
+
secrets_map = json.loads(args.secrets)
|
|
669
|
+
config = SetupRunnerConfig(github_project_url="dummy")
|
|
670
|
+
runner = SetupRunner(config)
|
|
671
|
+
|
|
672
|
+
print("💧 Hydrating environment files from secrets...")
|
|
673
|
+
runner.hydrate_env_from_secrets(
|
|
674
|
+
compose_file=args.compose_file,
|
|
675
|
+
secrets_map=secrets_map,
|
|
676
|
+
project_root=args.project_root,
|
|
677
|
+
)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
print(f"❌ Failed to hydrate env: {e}")
|
|
680
|
+
sys.exit(1)
|
|
681
|
+
|
|
640
682
|
elif args.command == "completion":
|
|
641
683
|
from vm_tool.completion import print_completion, install_completion
|
|
642
684
|
|
|
@@ -171,6 +171,75 @@ class SetupRunner:
|
|
|
171
171
|
self.env_path = config.env_path
|
|
172
172
|
self.env_data = config.env_data
|
|
173
173
|
|
|
174
|
+
def _get_compose_dependencies(self, compose_file: str) -> List[dict]:
|
|
175
|
+
"""
|
|
176
|
+
Parses docker-compose file to find local file dependencies (env_files, volumes).
|
|
177
|
+
Returns a list of dicts: {'src': 'local/path', 'dest': 'remote/path'}
|
|
178
|
+
"""
|
|
179
|
+
dependencies = []
|
|
180
|
+
if not os.path.exists(compose_file):
|
|
181
|
+
return dependencies
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
with open(compose_file, "r") as f:
|
|
185
|
+
data = yaml.safe_load(f)
|
|
186
|
+
|
|
187
|
+
if not data or "services" not in data:
|
|
188
|
+
return dependencies
|
|
189
|
+
|
|
190
|
+
found_paths = set()
|
|
191
|
+
|
|
192
|
+
for service in data.get("services", {}).values():
|
|
193
|
+
# Check env_file
|
|
194
|
+
env_files = service.get("env_file", [])
|
|
195
|
+
if isinstance(env_files, str):
|
|
196
|
+
env_files = [env_files]
|
|
197
|
+
|
|
198
|
+
for env_path in env_files:
|
|
199
|
+
# Normalize local path
|
|
200
|
+
if env_path.startswith("./"):
|
|
201
|
+
clean_path = env_path[2:]
|
|
202
|
+
else:
|
|
203
|
+
clean_path = env_path
|
|
204
|
+
|
|
205
|
+
# Only include relative paths that are files
|
|
206
|
+
if not clean_path.startswith("/") and os.path.exists(clean_path):
|
|
207
|
+
if clean_path not in found_paths:
|
|
208
|
+
found_paths.add(clean_path)
|
|
209
|
+
dependencies.append({"src": clean_path, "dest": clean_path})
|
|
210
|
+
elif not os.path.exists(clean_path):
|
|
211
|
+
logger.warning(
|
|
212
|
+
f"⚠️ Referenced env_file not found locally: {clean_path}"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Check volumes (bind mounts)
|
|
216
|
+
volumes = service.get("volumes", [])
|
|
217
|
+
for vol in volumes:
|
|
218
|
+
if isinstance(vol, str):
|
|
219
|
+
parts = vol.split(":")
|
|
220
|
+
if len(parts) >= 2:
|
|
221
|
+
host_path = parts[0]
|
|
222
|
+
# Check if it's a relative path bind mount
|
|
223
|
+
if (
|
|
224
|
+
host_path.startswith("./")
|
|
225
|
+
or host_path.startswith("../")
|
|
226
|
+
) and os.path.exists(host_path):
|
|
227
|
+
if host_path.startswith("./"):
|
|
228
|
+
clean_path = host_path[2:]
|
|
229
|
+
else:
|
|
230
|
+
clean_path = host_path
|
|
231
|
+
|
|
232
|
+
if clean_path not in found_paths:
|
|
233
|
+
found_paths.add(clean_path)
|
|
234
|
+
dependencies.append(
|
|
235
|
+
{"src": clean_path, "dest": clean_path}
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
except Exception as e:
|
|
239
|
+
logger.warning(f"Failed to parse compose file for dependencies: {e}")
|
|
240
|
+
|
|
241
|
+
return dependencies
|
|
242
|
+
|
|
174
243
|
def _get_git_commit(self) -> Optional[str]:
|
|
175
244
|
"""Get current git commit hash if in a git repository."""
|
|
176
245
|
import subprocess
|
|
@@ -225,6 +294,65 @@ class SetupRunner:
|
|
|
225
294
|
f"An error occurred while running the Ansible playbook: {str(e)}"
|
|
226
295
|
)
|
|
227
296
|
|
|
297
|
+
def hydrate_env_from_secrets(
|
|
298
|
+
self, compose_file: str, secrets_map: dict, project_root: str = "."
|
|
299
|
+
):
|
|
300
|
+
"""
|
|
301
|
+
Scans compose file for env_files. If a file is missing locally,
|
|
302
|
+
checks secrets_map for a matching key (FILENAME_EXT -> FILENAME_EXT output as SNAKE_CASE)
|
|
303
|
+
and creates the file.
|
|
304
|
+
Example: env/backend.env -> checks secrets_map['BACKEND_ENV']
|
|
305
|
+
"""
|
|
306
|
+
dependencies = self._get_compose_dependencies(compose_file)
|
|
307
|
+
|
|
308
|
+
# We need to find *potential* dependencies that might not exist yet.
|
|
309
|
+
# _get_compose_dependencies only returns existing ones.
|
|
310
|
+
# So we need to parse again broadly or just rely on what we find.
|
|
311
|
+
# Actually, _get_compose_dependencies skips missing files.
|
|
312
|
+
# So we should parse manually here to find *missing* ones.
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
with open(compose_file, "r") as f:
|
|
316
|
+
data = yaml.safe_load(f)
|
|
317
|
+
|
|
318
|
+
if not data or "services" not in data:
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
for service in data.get("services", {}).values():
|
|
322
|
+
env_files = service.get("env_file", [])
|
|
323
|
+
if isinstance(env_files, str):
|
|
324
|
+
env_files = [env_files]
|
|
325
|
+
|
|
326
|
+
for env_path in env_files:
|
|
327
|
+
# Resolve path relative to project root
|
|
328
|
+
full_path = os.path.join(project_root, env_path)
|
|
329
|
+
|
|
330
|
+
if not os.path.exists(full_path):
|
|
331
|
+
# Attempt to hydrate
|
|
332
|
+
filename = os.path.basename(env_path)
|
|
333
|
+
# Normalize keys: backend.env -> BACKEND_ENV
|
|
334
|
+
secret_key = filename.replace(".", "_").upper()
|
|
335
|
+
|
|
336
|
+
secret_value = secrets_map.get(secret_key)
|
|
337
|
+
if secret_value:
|
|
338
|
+
logger.info(
|
|
339
|
+
f"💧 Hydrating {env_path} from secret {secret_key}"
|
|
340
|
+
)
|
|
341
|
+
# Ensure directory exists
|
|
342
|
+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
343
|
+
with open(full_path, "w") as out:
|
|
344
|
+
out.write(secret_value)
|
|
345
|
+
else:
|
|
346
|
+
logger.warning(
|
|
347
|
+
f"⚠️ Missing env file {env_path} and no secret found for {secret_key}"
|
|
348
|
+
)
|
|
349
|
+
else:
|
|
350
|
+
logger.debug(f"✅ Env file exists: {env_path}")
|
|
351
|
+
|
|
352
|
+
except Exception as e:
|
|
353
|
+
logger.error(f"Failed to hydrate env files: {e}")
|
|
354
|
+
raise
|
|
355
|
+
|
|
228
356
|
def run_setup(self):
|
|
229
357
|
"""Runs the setup process using Ansible."""
|
|
230
358
|
extravars = {
|
|
@@ -402,6 +530,24 @@ class SetupRunner:
|
|
|
402
530
|
if deploy_command:
|
|
403
531
|
extravars["DEPLOY_COMMAND"] = deploy_command
|
|
404
532
|
|
|
533
|
+
# Dynamic Dependency Detection
|
|
534
|
+
dependencies = self._get_compose_dependencies(compose_file)
|
|
535
|
+
|
|
536
|
+
# Ensure CLI-provided env_file is included in detection logic
|
|
537
|
+
if env_file:
|
|
538
|
+
# Normalize and check existence
|
|
539
|
+
clean_env_path = env_file[2:] if env_file.startswith("./") else env_file
|
|
540
|
+
if os.path.exists(clean_env_path):
|
|
541
|
+
# Check if already in dependencies
|
|
542
|
+
if not any(d["src"] == clean_env_path for d in dependencies):
|
|
543
|
+
dependencies.append({"src": clean_env_path, "dest": clean_env_path})
|
|
544
|
+
|
|
545
|
+
if dependencies:
|
|
546
|
+
logger.info(
|
|
547
|
+
f"📦 Detected dependencies to copy: {[d['src'] for d in dependencies]}"
|
|
548
|
+
)
|
|
549
|
+
extravars["FILES_TO_COPY"] = dependencies
|
|
550
|
+
|
|
405
551
|
playbook_path = os.path.join(
|
|
406
552
|
os.path.dirname(__file__), "vm_setup", "push_code.yml"
|
|
407
553
|
)
|
|
@@ -31,10 +31,35 @@
|
|
|
31
31
|
src: "{{ SOURCE_PATH }}/{{ DOCKER_COMPOSE_FILE_PATH }}"
|
|
32
32
|
dest: "{{ project_dest_dir }}/{{ DOCKER_COMPOSE_FILE_PATH }}"
|
|
33
33
|
|
|
34
|
+
# Handle dynamic dependencies (new method)
|
|
35
|
+
- name: Ensure parent directories for dependencies exist
|
|
36
|
+
file:
|
|
37
|
+
path: "{{ project_dest_dir }}/{{ item.dest | dirname }}"
|
|
38
|
+
state: directory
|
|
39
|
+
mode: '0755'
|
|
40
|
+
loop: "{{ FILES_TO_COPY | default([]) }}"
|
|
41
|
+
when: FILES_TO_COPY is defined and (item.dest | dirname) != ''
|
|
42
|
+
|
|
43
|
+
- name: Copy dependency files
|
|
44
|
+
copy:
|
|
45
|
+
src: "{{ SOURCE_PATH }}/{{ item.src }}"
|
|
46
|
+
dest: "{{ project_dest_dir }}/{{ item.dest }}"
|
|
47
|
+
loop: "{{ FILES_TO_COPY | default([]) }}"
|
|
48
|
+
when: FILES_TO_COPY is defined
|
|
49
|
+
|
|
50
|
+
# Backward compatibility for single env file
|
|
51
|
+
- name: Ensure Env file parent directory exists
|
|
52
|
+
file:
|
|
53
|
+
path: "{{ project_dest_dir }}/{{ ENV_FILE_PATH | default('.env') | dirname }}"
|
|
54
|
+
state: directory
|
|
55
|
+
mode: '0755'
|
|
56
|
+
when: FILES_TO_COPY is not defined and ENV_FILE_PATH is defined and (ENV_FILE_PATH | dirname) != ''
|
|
57
|
+
|
|
34
58
|
- name: Copy Env file
|
|
35
59
|
copy:
|
|
36
60
|
src: "{{ SOURCE_PATH }}/{{ ENV_FILE_PATH | default('.env') }}"
|
|
37
61
|
dest: "{{ project_dest_dir }}/{{ ENV_FILE_PATH | default('.env') }}"
|
|
62
|
+
when: FILES_TO_COPY is not defined
|
|
38
63
|
failed_when: false
|
|
39
64
|
|
|
40
65
|
- name: Deploy application
|
|
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
|
|
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
|