vm-tool 1.0.41__tar.gz → 1.0.43__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.43/.agent/workflows/push.md +11 -0
- vm_tool-1.0.43/.agent/workflows/test_and_lint.md +18 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/PKG-INFO +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.43}/codePushToGithub.py +32 -6
- vm_tool-1.0.43/jingo.code-workspace +11 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/pyproject.toml +2 -2
- {vm_tool-1.0.41 → vm_tool-1.0.43}/setup.py +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/cli.py +117 -1
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/generator.py +121 -37
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/runner.py +146 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/secrets.py +75 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/push_code_tasks.yml +25 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/PKG-INFO +1 -1
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/SOURCES.txt +3 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.devcontainer/devcontainer.json +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.github/dependabot.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.github/workflows/ci.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.github/workflows/deploy.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.github/workflows/publish.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.gitignore +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/.pre-commit-config.yaml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/CONTRIBUTING +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/CONTRIBUTING.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/LICENSE +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/MANIFEST.in +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/Makefile +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/MODULE_GUIDE.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/deployment-approaches.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/ec2-github-actions-guide.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/features.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/generator.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/index.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/pipeline-generator.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/reference/runner.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/reference/ssh.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/ssh-key-setup.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/docs/usage.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/cloud/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/cloud/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/cloud/ssh_identity_file.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/cloud/ssh_password.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/cloud/template_cloud_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/deploy_full_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/docker-compose.example.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/ec2-setup.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/github-actions-ec2.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/github-actions-full-setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/local/.keep +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/local/README.md +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/local/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/local/template_local_setup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/production-deploy.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/rollback.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/setup.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/ssh_key_management.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/examples/version_check.sh +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/mkdocs.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/molecule/default/converge.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/molecule/default/molecule.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/molecule/default/verify.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/requirements-docs.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/requirements.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/runtime.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/setup.cfg +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/conftest.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/integration/test_deployment.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_config.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_generator.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_health.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_history.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_logging.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_runner.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_ssh.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/tests/test_state.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/alerting.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/audit.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/backup.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/benchmarking.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/cloud.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/completion.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/compliance.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/config.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/drift.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/health.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/history.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/kubernetes.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/metrics.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/notifications.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/plugins.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/policy.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/rbac.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/recovery.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/reporting.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/ssh.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/state.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/strategies/__init__.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/strategies/ab_testing.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/strategies/blue_green.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/strategies/canary.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/validation.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/cleanup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/docker/create_docker_service.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/docker/docker_setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/docker/install_docker_and_compose.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/docker/login_to_docker_hub.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/github/git_configuration.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/inventory.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/k8s.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/main.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/monitoring.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/project_service.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/push_code.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/setup.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/vm_setup/setup_project_env.yml +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool/webhooks.py +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/dependency_links.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/entry_points.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/requires.txt +0 -0
- {vm_tool-1.0.41 → vm_tool-1.0.43}/vm_tool.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Push code using the custom automation script
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
This workflow uses the project's custom script to commit, bump version, and push changes. It ensures consistent versioning and environment management.
|
|
6
|
+
|
|
7
|
+
1. Run the custom push script
|
|
8
|
+
```bash
|
|
9
|
+
# Replace arguments as needed
|
|
10
|
+
python3 codePushToGithub.py --branch main --message "Update via Agent"
|
|
11
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run tests and linting for vm_tool
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
This workflow runs the project's tests and linting checks using the configured Makefile and Python tools.
|
|
6
|
+
|
|
7
|
+
1. Install dependencies (if needed) and run tests
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
pytest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2. Run linting checks
|
|
15
|
+
```bash
|
|
16
|
+
flake8 vm_tool
|
|
17
|
+
black --check vm_tool
|
|
18
|
+
```
|
|
@@ -128,20 +128,38 @@ def get_current_version():
|
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
def main():
|
|
131
|
+
import argparse
|
|
132
|
+
|
|
133
|
+
parser = argparse.ArgumentParser(
|
|
134
|
+
description="Automate git commits and pushes with version bumping."
|
|
135
|
+
)
|
|
136
|
+
parser.add_argument("--branch", "-b", help="Branch name to push to", default=None)
|
|
137
|
+
parser.add_argument("--message", "-m", help="Commit message", default=None)
|
|
138
|
+
args = parser.parse_args()
|
|
139
|
+
|
|
131
140
|
# Setup virtual environment
|
|
132
141
|
print("🔧 Setting up environment...")
|
|
133
142
|
setup_environment()
|
|
134
143
|
print()
|
|
135
144
|
|
|
136
|
-
# Ask for the branch name
|
|
137
|
-
|
|
145
|
+
# Ask for the branch name if not provided
|
|
146
|
+
if args.branch:
|
|
147
|
+
branch_name = args.branch
|
|
148
|
+
else:
|
|
149
|
+
branch_name = input("Enter the branch name: ").strip()
|
|
138
150
|
|
|
139
|
-
if branch_name
|
|
151
|
+
if branch_name.lower() in ["main", "master"]:
|
|
140
152
|
# 1. Commit User Changes FIRST
|
|
141
153
|
print("Adding changes...")
|
|
142
154
|
run_command("git add .")
|
|
143
155
|
|
|
144
|
-
|
|
156
|
+
if args.message:
|
|
157
|
+
commit_message = args.message
|
|
158
|
+
else:
|
|
159
|
+
commit_message = input(
|
|
160
|
+
"Enter the commit message for your changes: "
|
|
161
|
+
).strip()
|
|
162
|
+
|
|
145
163
|
print("Committing changes...")
|
|
146
164
|
# Try to commit changes
|
|
147
165
|
result = run_command(f'git commit -m "{commit_message}"', check=False)
|
|
@@ -181,14 +199,22 @@ def main():
|
|
|
181
199
|
else:
|
|
182
200
|
# Create and switch to the new branch
|
|
183
201
|
print(f"Creating and switching to branch: {branch_name}")
|
|
184
|
-
|
|
202
|
+
# Check if branch exists
|
|
203
|
+
result = run_command(f"git rev-parse --verify {branch_name}", check=False)
|
|
204
|
+
if result.returncode == 0:
|
|
205
|
+
run_command(f"git checkout {branch_name}")
|
|
206
|
+
else:
|
|
207
|
+
run_command(f"git checkout -b {branch_name}")
|
|
185
208
|
|
|
186
209
|
# Add all changes
|
|
187
210
|
print("Adding changes...")
|
|
188
211
|
run_command("git add .")
|
|
189
212
|
|
|
190
213
|
# Ask for commit message
|
|
191
|
-
|
|
214
|
+
if args.message:
|
|
215
|
+
commit_message = args.message
|
|
216
|
+
else:
|
|
217
|
+
commit_message = input("Enter the commit message: ").strip()
|
|
192
218
|
|
|
193
219
|
# Commit with the entered message
|
|
194
220
|
print("Committing changes...")
|
|
@@ -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.43"
|
|
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.43"
|
|
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.43", # 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.43")
|
|
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"
|
|
@@ -271,6 +294,23 @@ def main():
|
|
|
271
294
|
help="CI/CD Platform",
|
|
272
295
|
)
|
|
273
296
|
|
|
297
|
+
# Secrets command
|
|
298
|
+
secrets_parser = subparsers.add_parser("secrets", help="Manage secrets")
|
|
299
|
+
secrets_subparsers = secrets_parser.add_subparsers(
|
|
300
|
+
dest="secrets_command", help="Secrets operations"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# secrets sync
|
|
304
|
+
sync_parser = secrets_subparsers.add_parser(
|
|
305
|
+
"sync", help="Sync local .env to GitHub Secrets"
|
|
306
|
+
)
|
|
307
|
+
sync_parser.add_argument(
|
|
308
|
+
"--env-file", type=str, default=".env", help="Path to .env file"
|
|
309
|
+
)
|
|
310
|
+
sync_parser.add_argument(
|
|
311
|
+
"--repo", type=str, help="Target GitHub repository (owner/repo)"
|
|
312
|
+
)
|
|
313
|
+
|
|
274
314
|
args = parser.parse_args()
|
|
275
315
|
|
|
276
316
|
# Configure logging based on flags
|
|
@@ -637,6 +677,25 @@ def main():
|
|
|
637
677
|
print(f"Error: {e}")
|
|
638
678
|
sys.exit(1)
|
|
639
679
|
|
|
680
|
+
elif args.command == "hydrate-env":
|
|
681
|
+
try:
|
|
682
|
+
import json
|
|
683
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig
|
|
684
|
+
|
|
685
|
+
secrets_map = json.loads(args.secrets)
|
|
686
|
+
config = SetupRunnerConfig(github_project_url="dummy")
|
|
687
|
+
runner = SetupRunner(config)
|
|
688
|
+
|
|
689
|
+
print("💧 Hydrating environment files from secrets...")
|
|
690
|
+
runner.hydrate_env_from_secrets(
|
|
691
|
+
compose_file=args.compose_file,
|
|
692
|
+
secrets_map=secrets_map,
|
|
693
|
+
project_root=args.project_root,
|
|
694
|
+
)
|
|
695
|
+
except Exception as e:
|
|
696
|
+
print(f"❌ Failed to hydrate env: {e}")
|
|
697
|
+
sys.exit(1)
|
|
698
|
+
|
|
640
699
|
elif args.command == "completion":
|
|
641
700
|
from vm_tool.completion import print_completion, install_completion
|
|
642
701
|
|
|
@@ -760,6 +819,63 @@ def main():
|
|
|
760
819
|
except Exception as e:
|
|
761
820
|
print(f"Error: {e}")
|
|
762
821
|
sys.exit(1)
|
|
822
|
+
elif args.command == "secrets":
|
|
823
|
+
if args.secrets_command == "sync":
|
|
824
|
+
from vm_tool.secrets import SecretsManager
|
|
825
|
+
import os
|
|
826
|
+
|
|
827
|
+
if not os.path.exists(args.env_file):
|
|
828
|
+
print(f"❌ Env file not found: {args.env_file}")
|
|
829
|
+
sys.exit(1)
|
|
830
|
+
|
|
831
|
+
try:
|
|
832
|
+
manager = SecretsManager.from_github(repo=args.repo)
|
|
833
|
+
|
|
834
|
+
print(f"📖 Reading secrets from {args.env_file}...")
|
|
835
|
+
secrets_to_sync = {}
|
|
836
|
+
with open(args.env_file, "r") as f:
|
|
837
|
+
for line in f:
|
|
838
|
+
line = line.strip()
|
|
839
|
+
if not line or line.startswith("#"):
|
|
840
|
+
continue
|
|
841
|
+
# Basic env parsing
|
|
842
|
+
if "=" in line:
|
|
843
|
+
key, value = line.split("=", 1)
|
|
844
|
+
key = key.strip()
|
|
845
|
+
value = value.strip().strip("'").strip('"')
|
|
846
|
+
secrets_to_sync[key] = value
|
|
847
|
+
|
|
848
|
+
if not secrets_to_sync:
|
|
849
|
+
print("⚠️ No secrets found to sync.")
|
|
850
|
+
sys.exit(0)
|
|
851
|
+
|
|
852
|
+
print(f"🚀 Syncing {len(secrets_to_sync)} secrets to GitHub...")
|
|
853
|
+
# Confirm with user
|
|
854
|
+
confirm = (
|
|
855
|
+
input(
|
|
856
|
+
f"Proceed to upload {len(secrets_to_sync)} secrets? (yes/no): "
|
|
857
|
+
)
|
|
858
|
+
.strip()
|
|
859
|
+
.lower()
|
|
860
|
+
)
|
|
861
|
+
if confirm != "yes":
|
|
862
|
+
print("❌ Operation cancelled.")
|
|
863
|
+
sys.exit(0)
|
|
864
|
+
|
|
865
|
+
success_count = 0
|
|
866
|
+
for key, value in secrets_to_sync.items():
|
|
867
|
+
print(f" Uploading {key}...")
|
|
868
|
+
if manager.set(key, value):
|
|
869
|
+
success_count += 1
|
|
870
|
+
|
|
871
|
+
print(
|
|
872
|
+
f"\n✨ Successfully synced {success_count}/{len(secrets_to_sync)} secrets!"
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
except Exception as e:
|
|
876
|
+
print(f"❌ Error syncing secrets: {e}")
|
|
877
|
+
sys.exit(1)
|
|
878
|
+
|
|
763
879
|
else:
|
|
764
880
|
parser.print_help()
|
|
765
881
|
|
|
@@ -22,6 +22,8 @@ class PipelineGenerator:
|
|
|
22
22
|
health_url: Optional[str] = None,
|
|
23
23
|
backup_paths: Optional[list] = None,
|
|
24
24
|
app_port: int = 8000,
|
|
25
|
+
hydrate_env: bool = True,
|
|
26
|
+
combine_compose: bool = True,
|
|
25
27
|
):
|
|
26
28
|
self.platform = platform
|
|
27
29
|
self.strategy = strategy
|
|
@@ -37,6 +39,8 @@ class PipelineGenerator:
|
|
|
37
39
|
)
|
|
38
40
|
self.backup_paths = backup_paths or ["/app", "/etc/nginx"]
|
|
39
41
|
self.app_port = app_port
|
|
42
|
+
self.hydrate_env = hydrate_env
|
|
43
|
+
self.combine_compose = combine_compose
|
|
40
44
|
|
|
41
45
|
# New options
|
|
42
46
|
self.run_linting = False
|
|
@@ -50,8 +54,7 @@ class PipelineGenerator:
|
|
|
50
54
|
run_tests: bool = False,
|
|
51
55
|
python_version: str = "3.11",
|
|
52
56
|
branch: str = "main",
|
|
53
|
-
):
|
|
54
|
-
"""Set additional options for the pipeline."""
|
|
57
|
+
): """Set additional options for the pipeline."""
|
|
55
58
|
self.run_linting = run_linting
|
|
56
59
|
self.run_tests = run_tests
|
|
57
60
|
self.python_version = python_version
|
|
@@ -120,6 +123,12 @@ class PipelineGenerator:
|
|
|
120
123
|
if self.enable_dry_run:
|
|
121
124
|
steps.append(self._step_dry_run())
|
|
122
125
|
|
|
126
|
+
if self.hydrate_env:
|
|
127
|
+
steps.append(self._step_hydrate_env())
|
|
128
|
+
|
|
129
|
+
if self.combine_compose:
|
|
130
|
+
steps.append(self._step_combine_compose())
|
|
131
|
+
|
|
123
132
|
# Main deployment
|
|
124
133
|
steps.append(self._step_deploy())
|
|
125
134
|
|
|
@@ -143,7 +152,7 @@ class PipelineGenerator:
|
|
|
143
152
|
# Combine all steps
|
|
144
153
|
steps_yaml = "\n".join(steps)
|
|
145
154
|
|
|
146
|
-
return f"""name: Deploy
|
|
155
|
+
return f"""name: Deploy with vm_tool
|
|
147
156
|
|
|
148
157
|
on:
|
|
149
158
|
push:
|
|
@@ -153,8 +162,8 @@ on:
|
|
|
153
162
|
workflow_dispatch:
|
|
154
163
|
|
|
155
164
|
env:
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
SSH_HOSTNAME: ${{{{ secrets.SSH_HOSTNAME }}}}
|
|
166
|
+
SSH_USERNAME: ${{{{ secrets.SSH_USERNAME }}}}
|
|
158
167
|
APP_PORT: {self.app_port}
|
|
159
168
|
|
|
160
169
|
jobs:
|
|
@@ -176,16 +185,16 @@ jobs:
|
|
|
176
185
|
echo "🔐 Validating GitHub Secrets..."
|
|
177
186
|
MISSING_SECRETS=()
|
|
178
187
|
|
|
179
|
-
if [ -z "${{ secrets.
|
|
180
|
-
MISSING_SECRETS+=("
|
|
188
|
+
if [ -z "${{ secrets.SSH_HOSTNAME }}" ]; then
|
|
189
|
+
MISSING_SECRETS+=("SSH_HOSTNAME")
|
|
181
190
|
fi
|
|
182
191
|
|
|
183
|
-
if [ -z "${{ secrets.
|
|
184
|
-
MISSING_SECRETS+=("
|
|
192
|
+
if [ -z "${{ secrets.SSH_USERNAME }}" ]; then
|
|
193
|
+
MISSING_SECRETS+=("SSH_USERNAME")
|
|
185
194
|
fi
|
|
186
195
|
|
|
187
|
-
if [ -z "${{ secrets.
|
|
188
|
-
MISSING_SECRETS+=("
|
|
196
|
+
if [ -z "${{ secrets.SSH_ID_RSA }}" ]; then
|
|
197
|
+
MISSING_SECRETS+=("SSH_ID_RSA")
|
|
189
198
|
fi
|
|
190
199
|
|
|
191
200
|
if [ ${#MISSING_SECRETS[@]} -ne 0 ]; then
|
|
@@ -199,16 +208,16 @@ jobs:
|
|
|
199
208
|
echo "2. Add each secret:"
|
|
200
209
|
echo ""
|
|
201
210
|
|
|
202
|
-
if [[ " ${MISSING_SECRETS[*]} " =~ "
|
|
203
|
-
echo "
|
|
211
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " SSH_HOSTNAME " ]]; then
|
|
212
|
+
echo " SSH_HOSTNAME: Your Server IP (e.g., 54.123.45.67)"
|
|
204
213
|
fi
|
|
205
214
|
|
|
206
|
-
if [[ " ${MISSING_SECRETS[*]} " =~ "
|
|
207
|
-
echo "
|
|
215
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " SSH_USERNAME " ]]; then
|
|
216
|
+
echo " SSH_USERNAME: SSH username (e.g., ubuntu)"
|
|
208
217
|
fi
|
|
209
218
|
|
|
210
|
-
if [[ " ${MISSING_SECRETS[*]} " =~ "
|
|
211
|
-
echo "
|
|
219
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " SSH_ID_RSA " ]]; then
|
|
220
|
+
echo " SSH_ID_RSA: Run 'cat ~/.ssh/id_rsa' and copy output"
|
|
212
221
|
fi
|
|
213
222
|
|
|
214
223
|
echo ""
|
|
@@ -293,9 +302,54 @@ jobs:
|
|
|
293
302
|
- name: Set up SSH
|
|
294
303
|
run: |
|
|
295
304
|
mkdir -p ~/.ssh
|
|
296
|
-
echo "${{ secrets.
|
|
305
|
+
echo "${{ secrets.SSH_ID_RSA }}" > ~/.ssh/deploy_key
|
|
297
306
|
chmod 600 ~/.ssh/deploy_key
|
|
298
|
-
ssh-keyscan -H ${{ secrets.
|
|
307
|
+
ssh-keyscan -H ${{ secrets.SSH_HOSTNAME }} >> ~/.ssh/known_hosts
|
|
308
|
+
|
|
309
|
+
# Create SSH config to use the key for the specific host
|
|
310
|
+
cat > ~/.ssh/config <<EOF
|
|
311
|
+
Host ${{ secrets.SSH_HOSTNAME }}
|
|
312
|
+
User ${{ secrets.SSH_USERNAME }}
|
|
313
|
+
IdentityFile ~/.ssh/deploy_key
|
|
314
|
+
StrictHostKeyChecking no
|
|
315
|
+
EOF"""
|
|
316
|
+
|
|
317
|
+
def _step_hydrate_env(self) -> str:
|
|
318
|
+
return """
|
|
319
|
+
- name: Hydrate Environment Files from Secrets
|
|
320
|
+
env:
|
|
321
|
+
SECRETS_CONTEXT: ${{ toJSON(secrets) }}
|
|
322
|
+
run: |
|
|
323
|
+
# Use vm_tool to create local env files from secrets based on compose file
|
|
324
|
+
vm_tool hydrate-env \\
|
|
325
|
+
--compose-file docker-compose.yml \\
|
|
326
|
+
--secrets "$SECRETS_CONTEXT"
|
|
327
|
+
echo "✅ Hydrated environment files from secrets"
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
def _step_combine_compose(self) -> str:
|
|
331
|
+
return """
|
|
332
|
+
- name: Combine Docker Compose files
|
|
333
|
+
run: |
|
|
334
|
+
# Combine base and prod compose files if prod exists
|
|
335
|
+
if [ -f docker/docker-compose.prod.yml ]; then
|
|
336
|
+
echo "Merging docker-compose.yml and docker/docker-compose.prod.yml..."
|
|
337
|
+
docker compose -f docker-compose.yml -f docker/docker-compose.prod.yml config > docker-compose.released.yml
|
|
338
|
+
elif [ -f docker-compose.prod.yml ]; then
|
|
339
|
+
echo "Merging docker-compose.yml and docker-compose.prod.yml..."
|
|
340
|
+
docker compose -f docker-compose.yml -f docker-compose.prod.yml config > docker-compose.released.yml
|
|
341
|
+
else
|
|
342
|
+
echo "No prod overriding file found, using base file..."
|
|
343
|
+
docker compose -f docker-compose.yml config > docker-compose.released.yml
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# Replace absolute paths (from CI runner) with relative paths
|
|
347
|
+
# This assumes the structure: /home/runner/work/repo/repo/ -> ./
|
|
348
|
+
sed -i 's|/home/runner/work/[^/]*/[^/]*||g' docker-compose.released.yml
|
|
349
|
+
|
|
350
|
+
echo "✅ Generated combined Docker Compose file"
|
|
351
|
+
cat docker-compose.released.yml
|
|
352
|
+
"""
|
|
299
353
|
|
|
300
354
|
def _step_validate_ssh(self) -> str:
|
|
301
355
|
return """
|
|
@@ -356,25 +410,52 @@ jobs:
|
|
|
356
410
|
def _step_deploy(self) -> str:
|
|
357
411
|
return """
|
|
358
412
|
- name: Deploy with vm_tool (Ansible-based)
|
|
413
|
+
env:
|
|
414
|
+
# Define deployment command for the released file
|
|
415
|
+
DEPLOY_COMMAND: "docker compose -f docker-compose.released.yml up -d --remove-orphans"
|
|
359
416
|
run: |
|
|
360
417
|
# Create inventory file for Ansible
|
|
361
418
|
cat > inventory.yml << EOF
|
|
362
419
|
all:
|
|
363
420
|
hosts:
|
|
364
421
|
production:
|
|
365
|
-
ansible_host: ${{ secrets.
|
|
366
|
-
ansible_user: ${{ secrets.
|
|
422
|
+
ansible_host: ${{ secrets.SSH_HOSTNAME }}
|
|
423
|
+
ansible_user: ${{ secrets.SSH_USERNAME }}
|
|
367
424
|
ansible_ssh_private_key_file: ~/.ssh/deploy_key
|
|
368
425
|
EOF
|
|
369
426
|
|
|
370
427
|
# Deploy using vm_tool (uses Ansible under the hood)
|
|
371
428
|
export GITHUB_REPOSITORY_OWNER=${{ github.repository_owner }}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
429
|
+
|
|
430
|
+
# Determine Project Directory based on user (handle root case)
|
|
431
|
+
if [ "${{ secrets.SSH_USERNAME }}" = "root" ]; then
|
|
432
|
+
HOME_DIR="/root"
|
|
433
|
+
else
|
|
434
|
+
HOME_DIR="/home/${{ secrets.SSH_USERNAME }}"
|
|
435
|
+
fi
|
|
436
|
+
PROJECT_DIR="${HOME_DIR}/apps/${{ github.event.repository.name }}"
|
|
437
|
+
|
|
438
|
+
# Prepare arguments
|
|
439
|
+
ARGS=(
|
|
440
|
+
"--inventory" "inventory.yml"
|
|
441
|
+
"--compose-file" "docker-compose.released.yml"
|
|
442
|
+
"--project-dir" "$PROJECT_DIR"
|
|
443
|
+
"--deploy-command" "${{ env.DEPLOY_COMMAND }}"
|
|
444
|
+
"--force"
|
|
445
|
+
"--health-url" "http://${{ secrets.SSH_HOSTNAME }}:${{ env.APP_PORT }}/health"
|
|
446
|
+
"--host" "${{ secrets.SSH_HOSTNAME }}"
|
|
447
|
+
"--user" "${{ secrets.SSH_USERNAME }}"
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# Move .env if it exists and add to args
|
|
451
|
+
if [ -f .env ]; then
|
|
452
|
+
mkdir -p env
|
|
453
|
+
mv .env env/.env
|
|
454
|
+
ARGS+=("--env-file" "env/.env")
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
vm_tool deploy-docker "${ARGS[@]}"
|
|
458
|
+
"""
|
|
378
459
|
|
|
379
460
|
def _step_health_check(self) -> str:
|
|
380
461
|
return f"""
|
|
@@ -394,10 +475,13 @@ jobs:
|
|
|
394
475
|
return """
|
|
395
476
|
- name: Verify
|
|
396
477
|
run: |
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
478
|
+
# Use absolute path for verify step too
|
|
479
|
+
PROJECT_DIR="/home/${{ secrets.SSH_USERNAME }}/apps/${{ github.event.repository.name }}"
|
|
480
|
+
|
|
481
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOSTNAME }} << EOF
|
|
482
|
+
cd $PROJECT_DIR
|
|
483
|
+
docker compose ps
|
|
484
|
+
docker compose logs --tail=20
|
|
401
485
|
EOF"""
|
|
402
486
|
|
|
403
487
|
def _step_rollback(self) -> str:
|
|
@@ -406,12 +490,12 @@ jobs:
|
|
|
406
490
|
if: failure()
|
|
407
491
|
run: |
|
|
408
492
|
echo "⚠️ Rolling back..."
|
|
409
|
-
|
|
493
|
+
# Note: Rollback logic might need adjustment for absolute paths, keeping simple for now
|
|
494
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOSTNAME }} << 'EOF'
|
|
410
495
|
BACKUP=$(ls -t ~/backups/*.tar.gz 2>/dev/null | head -1)
|
|
411
496
|
if [ -n "$BACKUP" ]; then
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
echo "✅ Rolled back"
|
|
497
|
+
# TODO: Improve rollback to handle dynamic dirs
|
|
498
|
+
echo "Rollback not fully implemented for dynamic paths yet"
|
|
415
499
|
fi
|
|
416
500
|
EOF"""
|
|
417
501
|
|
|
@@ -420,7 +504,7 @@ jobs:
|
|
|
420
504
|
- name: Cleanup
|
|
421
505
|
if: success()
|
|
422
506
|
run: |
|
|
423
|
-
ssh -i ~/.ssh/deploy_key ${{ secrets.
|
|
507
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOSTNAME }} << 'EOF'
|
|
424
508
|
cd ~/backups 2>/dev/null || exit 0
|
|
425
509
|
ls -t *.tar.gz 2>/dev/null | tail -n +6 | xargs rm -f || true
|
|
426
510
|
EOF"""
|
|
@@ -431,7 +515,7 @@ jobs:
|
|
|
431
515
|
if: always()
|
|
432
516
|
run: |
|
|
433
517
|
if [ "${{ job.status }}" == "success" ]; then
|
|
434
|
-
echo "✅ Deployed to ${{ secrets.
|
|
518
|
+
echo "✅ Deployed to ${{ secrets.SSH_HOSTNAME }}:${{ env.APP_PORT }}"
|
|
435
519
|
else
|
|
436
520
|
echo "❌ Deployment failed"
|
|
437
521
|
fi"""
|