vm-tool 1.0.32__py3-none-any.whl
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.
- examples/README.md +5 -0
- examples/__init__.py +1 -0
- examples/cloud/README.md +3 -0
- examples/cloud/__init__.py +1 -0
- examples/cloud/ssh_identity_file.py +27 -0
- examples/cloud/ssh_password.py +27 -0
- examples/cloud/template_cloud_setup.py +36 -0
- examples/deploy_full_setup.py +44 -0
- examples/docker-compose.example.yml +47 -0
- examples/ec2-setup.sh +95 -0
- examples/github-actions-ec2.yml +245 -0
- examples/github-actions-full-setup.yml +58 -0
- examples/local/.keep +1 -0
- examples/local/README.md +3 -0
- examples/local/__init__.py +1 -0
- examples/local/template_local_setup.py +27 -0
- examples/production-deploy.sh +70 -0
- examples/rollback.sh +52 -0
- examples/setup.sh +52 -0
- examples/ssh_key_management.py +22 -0
- examples/version_check.sh +3 -0
- vm_tool/__init__.py +0 -0
- vm_tool/alerting.py +274 -0
- vm_tool/audit.py +118 -0
- vm_tool/backup.py +125 -0
- vm_tool/benchmarking.py +200 -0
- vm_tool/cli.py +761 -0
- vm_tool/cloud.py +125 -0
- vm_tool/completion.py +200 -0
- vm_tool/compliance.py +104 -0
- vm_tool/config.py +92 -0
- vm_tool/drift.py +98 -0
- vm_tool/generator.py +462 -0
- vm_tool/health.py +197 -0
- vm_tool/history.py +131 -0
- vm_tool/kubernetes.py +89 -0
- vm_tool/metrics.py +183 -0
- vm_tool/notifications.py +152 -0
- vm_tool/plugins.py +119 -0
- vm_tool/policy.py +197 -0
- vm_tool/rbac.py +140 -0
- vm_tool/recovery.py +169 -0
- vm_tool/reporting.py +218 -0
- vm_tool/runner.py +445 -0
- vm_tool/secrets.py +285 -0
- vm_tool/ssh.py +150 -0
- vm_tool/state.py +122 -0
- vm_tool/strategies/__init__.py +16 -0
- vm_tool/strategies/ab_testing.py +258 -0
- vm_tool/strategies/blue_green.py +227 -0
- vm_tool/strategies/canary.py +277 -0
- vm_tool/validation.py +267 -0
- vm_tool/vm_setup/cleanup.yml +27 -0
- vm_tool/vm_setup/docker/create_docker_service.yml +63 -0
- vm_tool/vm_setup/docker/docker_setup.yml +7 -0
- vm_tool/vm_setup/docker/install_docker_and_compose.yml +92 -0
- vm_tool/vm_setup/docker/login_to_docker_hub.yml +6 -0
- vm_tool/vm_setup/github/git_configuration.yml +68 -0
- vm_tool/vm_setup/inventory.yml +1 -0
- vm_tool/vm_setup/k8s.yml +15 -0
- vm_tool/vm_setup/main.yml +27 -0
- vm_tool/vm_setup/monitoring.yml +42 -0
- vm_tool/vm_setup/project_service.yml +17 -0
- vm_tool/vm_setup/push_code.yml +40 -0
- vm_tool/vm_setup/setup.yml +17 -0
- vm_tool/vm_setup/setup_project_env.yml +7 -0
- vm_tool/webhooks.py +83 -0
- vm_tool-1.0.32.dist-info/METADATA +213 -0
- vm_tool-1.0.32.dist-info/RECORD +73 -0
- vm_tool-1.0.32.dist-info/WHEEL +5 -0
- vm_tool-1.0.32.dist-info/entry_points.txt +2 -0
- vm_tool-1.0.32.dist-info/licenses/LICENSE +21 -0
- vm_tool-1.0.32.dist-info/top_level.txt +2 -0
examples/README.md
ADDED
examples/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Example scripts package
|
examples/cloud/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Cloud VM setup examples package
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Cloud VM setup using SSH key authentication (public GitHub repo).
|
|
3
|
+
- Connects to a remote VM using SSH key and runs setup from a public repo.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig, SSHConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
config = SetupRunnerConfig(
|
|
11
|
+
github_project_url="https://github.com/username/public-repo",
|
|
12
|
+
github_branch="main",
|
|
13
|
+
docker_compose_file_path="docker-compose.yml",
|
|
14
|
+
)
|
|
15
|
+
runner = SetupRunner(config)
|
|
16
|
+
ssh_configs = [
|
|
17
|
+
SSHConfig(
|
|
18
|
+
ssh_username="your_ssh_username",
|
|
19
|
+
ssh_hostname="your_ssh_hostname",
|
|
20
|
+
ssh_identity_file="/path/to/your/ssh_key",
|
|
21
|
+
)
|
|
22
|
+
]
|
|
23
|
+
runner.run_cloud_setup(ssh_configs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example: Cloud VM setup using SSH password authentication (public GitHub repo).
|
|
3
|
+
- Connects to a remote VM using SSH username/password and runs setup from a public repo.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig, SSHConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
config = SetupRunnerConfig(
|
|
11
|
+
github_project_url="https://github.com/username/public-repo",
|
|
12
|
+
github_branch="main",
|
|
13
|
+
docker_compose_file_path="docker-compose.yml",
|
|
14
|
+
)
|
|
15
|
+
runner = SetupRunner(config)
|
|
16
|
+
ssh_configs = [
|
|
17
|
+
SSHConfig(
|
|
18
|
+
ssh_username="your_ssh_username",
|
|
19
|
+
ssh_hostname="your_ssh_hostname",
|
|
20
|
+
ssh_password="your_ssh_password",
|
|
21
|
+
)
|
|
22
|
+
]
|
|
23
|
+
runner.run_cloud_setup(ssh_configs)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template: Cloud VM setup (customize for your use case)
|
|
3
|
+
- Use this template to create your own cloud VM setup script.
|
|
4
|
+
- Supports public/private GitHub, DockerHub, SSH key or password authentication, and multiple VMs.
|
|
5
|
+
- Fill in only the fields you need for your scenario.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig, SSHConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
# Fill in only the fields you need for your use case
|
|
13
|
+
config = SetupRunnerConfig(
|
|
14
|
+
github_username=None, # e.g., 'your_github_username' (for private repos)
|
|
15
|
+
github_token=None, # e.g., 'your_github_token' (for private repos)
|
|
16
|
+
github_project_url="https://github.com/username/your-repo",
|
|
17
|
+
github_branch="main", # Optional, defaults to 'main'
|
|
18
|
+
docker_compose_file_path="docker-compose.yml", # Optional
|
|
19
|
+
dockerhub_username=None, # e.g., 'your_dockerhub_username' (if DockerHub login needed)
|
|
20
|
+
dockerhub_password=None, # e.g., 'your_dockerhub_password' (if DockerHub login needed)
|
|
21
|
+
)
|
|
22
|
+
runner = SetupRunner(config)
|
|
23
|
+
ssh_configs = [
|
|
24
|
+
SSHConfig(
|
|
25
|
+
ssh_username="your_ssh_username",
|
|
26
|
+
ssh_hostname="your_ssh_hostname",
|
|
27
|
+
ssh_identity_file=None, # e.g., '/path/to/your/ssh_key' (for SSH key auth)
|
|
28
|
+
ssh_password=None, # e.g., 'your_ssh_password' (for SSH password auth)
|
|
29
|
+
)
|
|
30
|
+
# Add more SSHConfig instances for multiple VMs if needed
|
|
31
|
+
]
|
|
32
|
+
runner.run_cloud_setup(ssh_configs)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example deployment script using run_cloud_setup for full VM provisioning.
|
|
3
|
+
This approach installs Docker, clones your repo, and sets up everything.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig, SSHConfig
|
|
8
|
+
|
|
9
|
+
# Configuration
|
|
10
|
+
config = SetupRunnerConfig(
|
|
11
|
+
github_username=os.environ.get("GITHUB_USERNAME"),
|
|
12
|
+
github_token=os.environ.get("GITHUB_TOKEN"),
|
|
13
|
+
github_project_url=os.environ.get(
|
|
14
|
+
"GITHUB_PROJECT_URL", "https://github.com/user/repo.git"
|
|
15
|
+
),
|
|
16
|
+
github_branch=os.environ.get("GITHUB_BRANCH", "main"),
|
|
17
|
+
docker_compose_file_path=os.environ.get(
|
|
18
|
+
"DOCKER_COMPOSE_FILE", "docker-compose.yml"
|
|
19
|
+
),
|
|
20
|
+
dockerhub_username=os.environ.get("DOCKERHUB_USERNAME"),
|
|
21
|
+
dockerhub_password=os.environ.get("DOCKERHUB_PASSWORD"),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Initialize runner
|
|
25
|
+
runner = SetupRunner(config)
|
|
26
|
+
|
|
27
|
+
# SSH configuration for target VM(s)
|
|
28
|
+
ssh_configs = [
|
|
29
|
+
SSHConfig(
|
|
30
|
+
ssh_username=os.environ.get("EC2_USER", "ubuntu"),
|
|
31
|
+
ssh_hostname=os.environ.get("EC2_HOST"),
|
|
32
|
+
ssh_identity_file=os.environ.get("SSH_IDENTITY_FILE", "~/.ssh/id_rsa"),
|
|
33
|
+
),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Run full cloud setup
|
|
37
|
+
print("🚀 Starting full VM setup...")
|
|
38
|
+
print(f" Target: {ssh_configs[0].ssh_hostname}")
|
|
39
|
+
print(f" Repository: {config.github_project_url}")
|
|
40
|
+
print(f" Branch: {config.github_branch}")
|
|
41
|
+
|
|
42
|
+
runner.run_cloud_setup(ssh_configs)
|
|
43
|
+
|
|
44
|
+
print("✅ VM setup complete!")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
version: "3.8"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
web:
|
|
5
|
+
image: nginx:alpine
|
|
6
|
+
ports:
|
|
7
|
+
- "80:80"
|
|
8
|
+
volumes:
|
|
9
|
+
- ./html:/usr/share/nginx/html
|
|
10
|
+
restart: unless-stopped
|
|
11
|
+
healthcheck:
|
|
12
|
+
test:
|
|
13
|
+
["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
|
14
|
+
interval: 30s
|
|
15
|
+
timeout: 10s
|
|
16
|
+
retries: 3
|
|
17
|
+
start_period: 40s
|
|
18
|
+
|
|
19
|
+
api:
|
|
20
|
+
image: python:3.11-slim
|
|
21
|
+
working_dir: /app
|
|
22
|
+
volumes:
|
|
23
|
+
- ./app:/app
|
|
24
|
+
command: python -m http.server 8000
|
|
25
|
+
ports:
|
|
26
|
+
- "8000:8000"
|
|
27
|
+
restart: unless-stopped
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
30
|
+
interval: 30s
|
|
31
|
+
timeout: 10s
|
|
32
|
+
retries: 3
|
|
33
|
+
start_period: 40s
|
|
34
|
+
environment:
|
|
35
|
+
- ENVIRONMENT=production
|
|
36
|
+
- LOG_LEVEL=info
|
|
37
|
+
|
|
38
|
+
redis:
|
|
39
|
+
image: redis:alpine
|
|
40
|
+
ports:
|
|
41
|
+
- "6379:6379"
|
|
42
|
+
restart: unless-stopped
|
|
43
|
+
healthcheck:
|
|
44
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
45
|
+
interval: 30s
|
|
46
|
+
timeout: 3s
|
|
47
|
+
retries: 3
|
examples/ec2-setup.sh
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# EC2 Setup Script for GitHub Actions Deployment
|
|
4
|
+
# Run this on your EC2 instance
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "🚀 Setting up EC2 for GitHub Actions Deployment"
|
|
9
|
+
echo "================================================"
|
|
10
|
+
|
|
11
|
+
# Update system
|
|
12
|
+
echo "📦 Updating system packages..."
|
|
13
|
+
sudo apt update && sudo apt upgrade -y
|
|
14
|
+
|
|
15
|
+
# Install Docker
|
|
16
|
+
echo "🐳 Installing Docker..."
|
|
17
|
+
if ! command -v docker &> /dev/null; then
|
|
18
|
+
curl -fsSL https://get.docker.com -o get-docker.sh
|
|
19
|
+
sudo sh get-docker.sh
|
|
20
|
+
rm get-docker.sh
|
|
21
|
+
echo "✅ Docker installed"
|
|
22
|
+
else
|
|
23
|
+
echo "✅ Docker already installed"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Install Docker Compose
|
|
27
|
+
echo "🐳 Installing Docker Compose..."
|
|
28
|
+
if ! command -v docker-compose &> /dev/null; then
|
|
29
|
+
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
30
|
+
sudo chmod +x /usr/local/bin/docker-compose
|
|
31
|
+
echo "✅ Docker Compose installed"
|
|
32
|
+
else
|
|
33
|
+
echo "✅ Docker Compose already installed"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Create deploy user
|
|
37
|
+
echo "👤 Creating deploy user..."
|
|
38
|
+
if ! id "deploy" &>/dev/null; then
|
|
39
|
+
sudo adduser --disabled-password --gecos "" deploy
|
|
40
|
+
sudo usermod -aG docker deploy
|
|
41
|
+
sudo usermod -aG sudo deploy
|
|
42
|
+
echo "✅ Deploy user created"
|
|
43
|
+
else
|
|
44
|
+
echo "✅ Deploy user already exists"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Add current user to docker group
|
|
48
|
+
echo "👤 Adding $USER to docker group..."
|
|
49
|
+
sudo usermod -aG docker $USER
|
|
50
|
+
|
|
51
|
+
# Create app directory
|
|
52
|
+
echo "📁 Creating app directory..."
|
|
53
|
+
sudo mkdir -p /home/deploy/app
|
|
54
|
+
sudo chown -R deploy:deploy /home/deploy/app
|
|
55
|
+
|
|
56
|
+
# Create backups directory
|
|
57
|
+
echo "📁 Creating backups directory..."
|
|
58
|
+
sudo mkdir -p /home/deploy/backups
|
|
59
|
+
sudo chown -R deploy:deploy /home/deploy/backups
|
|
60
|
+
|
|
61
|
+
# Install useful tools
|
|
62
|
+
echo "🛠️ Installing useful tools..."
|
|
63
|
+
sudo apt install -y git curl wget htop ncdu
|
|
64
|
+
|
|
65
|
+
# Configure firewall
|
|
66
|
+
echo "🔒 Configuring firewall..."
|
|
67
|
+
if command -v ufw &> /dev/null; then
|
|
68
|
+
sudo ufw allow 22/tcp # SSH
|
|
69
|
+
sudo ufw allow 80/tcp # HTTP
|
|
70
|
+
sudo ufw allow 443/tcp # HTTPS
|
|
71
|
+
sudo ufw allow 8000/tcp # App port (adjust as needed)
|
|
72
|
+
echo "✅ Firewall configured (not enabled yet)"
|
|
73
|
+
echo "⚠️ To enable: sudo ufw enable"
|
|
74
|
+
else
|
|
75
|
+
echo "⚠️ UFW not installed"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Print SSH public key location
|
|
79
|
+
echo ""
|
|
80
|
+
echo "================================================"
|
|
81
|
+
echo "✅ EC2 Setup Complete!"
|
|
82
|
+
echo "================================================"
|
|
83
|
+
echo ""
|
|
84
|
+
echo "Next steps:"
|
|
85
|
+
echo "1. Add your GitHub Actions public key to ~/.ssh/authorized_keys"
|
|
86
|
+
echo "2. Test SSH connection from your local machine"
|
|
87
|
+
echo "3. Configure your GitHub repository secrets"
|
|
88
|
+
echo ""
|
|
89
|
+
echo "To add SSH key:"
|
|
90
|
+
echo " mkdir -p ~/.ssh"
|
|
91
|
+
echo " echo 'YOUR_PUBLIC_KEY' >> ~/.ssh/authorized_keys"
|
|
92
|
+
echo " chmod 600 ~/.ssh/authorized_keys"
|
|
93
|
+
echo " chmod 700 ~/.ssh"
|
|
94
|
+
echo ""
|
|
95
|
+
echo "Logout and login again to apply docker group changes"
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
name: Deploy to EC2 with vm_tool
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
EC2_HOST: ${{ secrets.EC2_HOST }}
|
|
10
|
+
EC2_USER: ${{ secrets.EC2_USER }}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
deploy:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Validate Required Secrets
|
|
21
|
+
run: |
|
|
22
|
+
echo "🔐 Validating GitHub Secrets..."
|
|
23
|
+
MISSING_SECRETS=()
|
|
24
|
+
|
|
25
|
+
# Check EC2_HOST
|
|
26
|
+
if [ -z "${{ secrets.EC2_HOST }}" ]; then
|
|
27
|
+
MISSING_SECRETS+=("EC2_HOST")
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Check EC2_USER
|
|
31
|
+
if [ -z "${{ secrets.EC2_USER }}" ]; then
|
|
32
|
+
MISSING_SECRETS+=("EC2_USER")
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Check EC2_SSH_KEY
|
|
36
|
+
if [ -z "${{ secrets.EC2_SSH_KEY }}" ]; then
|
|
37
|
+
MISSING_SECRETS+=("EC2_SSH_KEY")
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# If any secrets are missing, show detailed instructions
|
|
41
|
+
if [ ${#MISSING_SECRETS[@]} -ne 0 ]; then
|
|
42
|
+
echo ""
|
|
43
|
+
echo "❌ ERROR: Missing required GitHub Secrets!"
|
|
44
|
+
echo ""
|
|
45
|
+
echo "Missing secrets: ${MISSING_SECRETS[*]}"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
48
|
+
echo "📝 How to add GitHub Secrets:"
|
|
49
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
50
|
+
echo ""
|
|
51
|
+
echo "1. Go to your GitHub repository"
|
|
52
|
+
echo "2. Click: Settings → Secrets and variables → Actions"
|
|
53
|
+
echo "3. Click: New repository secret"
|
|
54
|
+
echo "4. Add each secret:"
|
|
55
|
+
echo ""
|
|
56
|
+
|
|
57
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " EC2_HOST " ]]; then
|
|
58
|
+
echo " 📍 EC2_HOST"
|
|
59
|
+
echo " Value: Your EC2 instance IP address"
|
|
60
|
+
echo " Example: 54.123.45.67"
|
|
61
|
+
echo ""
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " EC2_USER " ]]; then
|
|
65
|
+
echo " 👤 EC2_USER"
|
|
66
|
+
echo " Value: SSH username for your EC2 instance"
|
|
67
|
+
echo " Example: ubuntu (for Ubuntu) or ec2-user (for Amazon Linux)"
|
|
68
|
+
echo ""
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [[ " ${MISSING_SECRETS[*]} " =~ " EC2_SSH_KEY " ]]; then
|
|
72
|
+
echo " 🔑 EC2_SSH_KEY"
|
|
73
|
+
echo " Value: Your SSH private key"
|
|
74
|
+
echo ""
|
|
75
|
+
echo " To get your SSH private key, run on your LOCAL machine:"
|
|
76
|
+
echo " $ cat ~/.ssh/id_rsa"
|
|
77
|
+
echo ""
|
|
78
|
+
echo " OR if you use ed25519:"
|
|
79
|
+
echo " $ cat ~/.ssh/id_ed25519"
|
|
80
|
+
echo ""
|
|
81
|
+
echo " Copy the ENTIRE output (including BEGIN and END lines)"
|
|
82
|
+
echo ""
|
|
83
|
+
echo " Don't have an SSH key? Generate one:"
|
|
84
|
+
echo " $ ssh-keygen -t ed25519 -C 'github-actions'"
|
|
85
|
+
echo " $ cat ~/.ssh/id_ed25519.pub # Add to EC2 ~/.ssh/authorized_keys"
|
|
86
|
+
echo " $ cat ~/.ssh/id_ed25519 # Add to GitHub Secrets"
|
|
87
|
+
echo ""
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
91
|
+
echo "📚 For detailed instructions, see:"
|
|
92
|
+
echo " - docs/ec2-github-actions-guide.md"
|
|
93
|
+
echo " - docs/ssh-key-setup.md"
|
|
94
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
95
|
+
echo ""
|
|
96
|
+
exit 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
echo "✅ All required secrets are configured"
|
|
100
|
+
echo " - EC2_HOST: ${{ secrets.EC2_HOST }}"
|
|
101
|
+
echo " - EC2_USER: ${{ secrets.EC2_USER }}"
|
|
102
|
+
echo " - EC2_SSH_KEY: ✓ (configured)"
|
|
103
|
+
|
|
104
|
+
- name: Set up Python
|
|
105
|
+
uses: actions/setup-python@v4
|
|
106
|
+
with:
|
|
107
|
+
python-version: '3.11'
|
|
108
|
+
|
|
109
|
+
- name: Install vm_tool
|
|
110
|
+
run: pip install vm-tool
|
|
111
|
+
|
|
112
|
+
- name: Set up SSH
|
|
113
|
+
run: |
|
|
114
|
+
mkdir -p ~/.ssh
|
|
115
|
+
echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/deploy_key
|
|
116
|
+
chmod 600 ~/.ssh/deploy_key
|
|
117
|
+
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
|
|
118
|
+
|
|
119
|
+
- name: Validate SSH Connection
|
|
120
|
+
run: |
|
|
121
|
+
if [ ! -s ~/.ssh/deploy_key ]; then
|
|
122
|
+
echo "❌ ERROR: EC2_SSH_KEY is empty!"
|
|
123
|
+
echo ""
|
|
124
|
+
echo "📝 To get your SSH key, run on your local machine:"
|
|
125
|
+
echo " cat ~/.ssh/id_rsa"
|
|
126
|
+
echo ""
|
|
127
|
+
echo "Then add it to GitHub Secrets as EC2_SSH_KEY"
|
|
128
|
+
echo "See: docs/ssh-key-setup.md for detailed instructions"
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo "✅ Testing SSH connection..."
|
|
133
|
+
ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no \
|
|
134
|
+
${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} "echo 'Connection successful'" || {
|
|
135
|
+
echo "❌ SSH connection failed!"
|
|
136
|
+
echo "Check: docs/ssh-key-setup.md for troubleshooting"
|
|
137
|
+
exit 1
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- name: Copy project files to EC2
|
|
141
|
+
run: |
|
|
142
|
+
# Create app directory if it doesn't exist
|
|
143
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} \
|
|
144
|
+
'mkdir -p ~/app'
|
|
145
|
+
|
|
146
|
+
# Copy docker-compose and related files
|
|
147
|
+
scp -i ~/.ssh/deploy_key docker-compose.yml \
|
|
148
|
+
${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }}:~/app/
|
|
149
|
+
|
|
150
|
+
# Copy application code
|
|
151
|
+
rsync -avz -e "ssh -i ~/.ssh/deploy_key" \
|
|
152
|
+
--exclude '.git' \
|
|
153
|
+
--exclude 'node_modules' \
|
|
154
|
+
--exclude '.env' \
|
|
155
|
+
./ ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }}:~/app/
|
|
156
|
+
|
|
157
|
+
- name: Create backup before deployment
|
|
158
|
+
run: |
|
|
159
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
|
|
160
|
+
mkdir -p ~/backups
|
|
161
|
+
BACKUP_FILE=~/backups/backup-$(date +%Y%m%d-%H%M%S).tar.gz
|
|
162
|
+
if [ -d ~/app ]; then
|
|
163
|
+
cd ~/app && tar -czf $BACKUP_FILE . || true
|
|
164
|
+
echo "Backup created: $BACKUP_FILE"
|
|
165
|
+
fi
|
|
166
|
+
EOF
|
|
167
|
+
|
|
168
|
+
- name: Deploy with Docker Compose
|
|
169
|
+
run: |
|
|
170
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
|
|
171
|
+
cd ~/app
|
|
172
|
+
|
|
173
|
+
# Pull latest images
|
|
174
|
+
docker-compose pull
|
|
175
|
+
|
|
176
|
+
# Stop old containers
|
|
177
|
+
docker-compose down
|
|
178
|
+
|
|
179
|
+
# Start new containers
|
|
180
|
+
docker-compose up -d --build
|
|
181
|
+
|
|
182
|
+
# Wait for services to start
|
|
183
|
+
sleep 10
|
|
184
|
+
EOF
|
|
185
|
+
|
|
186
|
+
- name: Health check
|
|
187
|
+
run: |
|
|
188
|
+
# Wait for application to be ready
|
|
189
|
+
for i in {1..30}; do
|
|
190
|
+
if curl -f http://${{ secrets.EC2_HOST }}:8000/health 2>/dev/null; then
|
|
191
|
+
echo "✅ Health check passed"
|
|
192
|
+
exit 0
|
|
193
|
+
fi
|
|
194
|
+
echo "Waiting for application... ($i/30)"
|
|
195
|
+
sleep 2
|
|
196
|
+
done
|
|
197
|
+
echo "❌ Health check failed"
|
|
198
|
+
exit 1
|
|
199
|
+
|
|
200
|
+
- name: Verify deployment
|
|
201
|
+
run: |
|
|
202
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
|
|
203
|
+
cd ~/app
|
|
204
|
+
echo "📊 Container status:"
|
|
205
|
+
docker-compose ps
|
|
206
|
+
echo ""
|
|
207
|
+
echo "📝 Recent logs:"
|
|
208
|
+
docker-compose logs --tail=50
|
|
209
|
+
EOF
|
|
210
|
+
|
|
211
|
+
- name: Rollback on failure
|
|
212
|
+
if: failure()
|
|
213
|
+
run: |
|
|
214
|
+
echo "⚠️ Deployment failed, attempting rollback..."
|
|
215
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
|
|
216
|
+
cd ~/app
|
|
217
|
+
LATEST_BACKUP=$(ls -t ~/backups/*.tar.gz 2>/dev/null | head -1)
|
|
218
|
+
if [ -n "$LATEST_BACKUP" ]; then
|
|
219
|
+
echo "Rolling back to: $LATEST_BACKUP"
|
|
220
|
+
tar -xzf $LATEST_BACKUP
|
|
221
|
+
docker-compose up -d
|
|
222
|
+
echo "✅ Rollback complete"
|
|
223
|
+
else
|
|
224
|
+
echo "❌ No backup found for rollback"
|
|
225
|
+
fi
|
|
226
|
+
EOF
|
|
227
|
+
|
|
228
|
+
- name: Cleanup old backups
|
|
229
|
+
if: success()
|
|
230
|
+
run: |
|
|
231
|
+
ssh -i ~/.ssh/deploy_key ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
|
|
232
|
+
# Keep only last 5 backups
|
|
233
|
+
cd ~/backups
|
|
234
|
+
ls -t *.tar.gz 2>/dev/null | tail -n +6 | xargs rm -f || true
|
|
235
|
+
echo "✅ Cleanup complete"
|
|
236
|
+
EOF
|
|
237
|
+
|
|
238
|
+
- name: Send notification
|
|
239
|
+
if: always()
|
|
240
|
+
run: |
|
|
241
|
+
if [ "${{ job.status }}" == "success" ]; then
|
|
242
|
+
echo "✅ Deployment successful to ${{ secrets.EC2_HOST }}"
|
|
243
|
+
else
|
|
244
|
+
echo "❌ Deployment failed to ${{ secrets.EC2_HOST }}"
|
|
245
|
+
fi
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Full VM Setup and Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v4
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.11'
|
|
20
|
+
|
|
21
|
+
- name: Install vm_tool
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install vm-tool
|
|
25
|
+
|
|
26
|
+
- name: Create SSH identity file
|
|
27
|
+
run: |
|
|
28
|
+
mkdir -p ~/.ssh
|
|
29
|
+
echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/id_rsa
|
|
30
|
+
chmod 600 ~/.ssh/id_rsa
|
|
31
|
+
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
|
|
32
|
+
|
|
33
|
+
- name: Full VM Setup and Deploy
|
|
34
|
+
env:
|
|
35
|
+
GITHUB_USERNAME: ${{ secrets.GITHUB_USERNAME }}
|
|
36
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
+
GITHUB_PROJECT_URL: ${{ github.server_url }}/${{ github.repository }}
|
|
38
|
+
GITHUB_BRANCH: ${{ github.ref_name }}
|
|
39
|
+
DOCKER_COMPOSE_FILE: docker-compose.prod.yml
|
|
40
|
+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
41
|
+
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
|
|
42
|
+
EC2_USER: ${{ secrets.EC2_USER }}
|
|
43
|
+
EC2_HOST: ${{ secrets.EC2_HOST }}
|
|
44
|
+
SSH_IDENTITY_FILE: ~/.ssh/id_rsa
|
|
45
|
+
run: python scripts/deploy_to_vm.py
|
|
46
|
+
|
|
47
|
+
- name: Clean up SSH identity file
|
|
48
|
+
if: always()
|
|
49
|
+
run: rm -f ~/.ssh/id_rsa
|
|
50
|
+
|
|
51
|
+
- name: Notify
|
|
52
|
+
if: always()
|
|
53
|
+
run: |
|
|
54
|
+
if [ "${{ job.status }}" == "success" ]; then
|
|
55
|
+
echo "✅ Full setup and deployment successful!"
|
|
56
|
+
else
|
|
57
|
+
echo "❌ Deployment failed"
|
|
58
|
+
fi
|
examples/local/.keep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Keep this directory for local examples and templates.
|
examples/local/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Local VM setup examples package
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template: Local VM setup (customize for your use case)
|
|
3
|
+
- Use this template to create your own local VM setup script.
|
|
4
|
+
- Supports public/private GitHub, DockerHub, and custom Docker Compose file path.
|
|
5
|
+
- Fill in only the fields you need for your scenario.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from vm_tool.runner import SetupRunner, SetupRunnerConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
# Fill in only the fields you need for your use case
|
|
13
|
+
config = SetupRunnerConfig(
|
|
14
|
+
github_username=None, # e.g., 'your_github_username' (for private repos)
|
|
15
|
+
github_token=None, # e.g., 'your_github_token' (for private repos)
|
|
16
|
+
github_project_url="https://github.com/username/your-repo",
|
|
17
|
+
github_branch="main", # Optional, defaults to 'main'
|
|
18
|
+
docker_compose_file_path="docker-compose.yml", # Optional
|
|
19
|
+
dockerhub_username=None, # e.g., 'your_dockerhub_username' (if DockerHub login needed)
|
|
20
|
+
dockerhub_password=None, # e.g., 'your_dockerhub_password' (if DockerHub login needed)
|
|
21
|
+
)
|
|
22
|
+
runner = SetupRunner(config)
|
|
23
|
+
runner.run_setup()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|