git_deployment.md 9.9 KB

Git-based Deployment in BYOP Engine

Overview

The BYOP Engine now supports Git-based deployments using Git hooks for continuous deployment. This allows developers to deploy applications by simply pushing to a Git repository.

How It Works

  1. Initial Setup: When a VM is initialized:

    • Creates a bare Git repository on the VM
    • Sets up a working directory for the component
    • Configures Git hooks for automatic deployment
  2. Continuous Deployment: After initial setup, developers can:

    • Add the remote repository to their local Git config
    • Push changes to trigger automatic deployment
    • Monitor deployment progress through the BYOP dashboard
  3. Component-Specific Deployment: Different components are handled appropriately:

    • Frontend: Built and served via Nginx
    • Backend: Built and managed via systemd or PM2
    • Database: Configuration files applied and services restarted

Usage

Adding a Remote Repository

After a component is deployed, add the remote repository to your Git config:

git remote add production ssh://root@<vm-ip>/opt/byop/repos/<component-id>.git

Deploying Changes

Push to the remote repository to trigger a deployment:

git push production <branch>

The post-receive hook will automatically:

  1. Check out the code to the working directory
  2. Install dependencies
  3. Build the application
  4. Restart or reload services as needed

Monitoring Deployments

You can monitor deployment status through:

  • The BYOP dashboard
  • SSH access to the VM to check logs
  • Component status indicators

Security Considerations

  • SSH access is controlled through credentials managed by BYOP
  • Deploy keys can be configured for secure repository access
  • All operations use secure SSH connections

Future Enhancements

  • Support for deployment rollbacks
  • Automated testing before deployment
  • Multi-stage deployment environments (dev, staging, production)
  • Notification system for deployment status updates

Post script hooks example


// createFrontendPostReceiveHook generates a Git hook for frontend components
func createFrontendPostReceiveHook(component models.Component, deployPath string) string {
	return fmt.Sprintf(`#!/bin/bash
echo "Deploying frontend component: %s"

# Get the target branch (usually main or master)
TARGET="%s"
while read oldrev newrev ref
do
    # Check if the pushed branch is our target branch
    if [[ $ref = refs/heads/$TARGET ]]; 
    then
        echo "Deploying $TARGET branch..."
        
        # Checkout code to the deployment directory
        GIT_WORK_TREE=%s git checkout -f $TARGET
        cd %s
        
        # Update environment variables
        echo '%s' > %s/.env
        
        # Install dependencies
        echo "Installing dependencies..."
        npm install
        
        # Build the application
        echo "Building application..."
        %s
        
        # Notify about completion
        echo "Frontend deployment completed successfully"
    fi
done
`, component.Name, component.Branch, deployPath, deployPath, component.EnvVariables, deployPath, component.BuildCommand)
}

// createGoPostReceiveHook generates a Git hook for Go components
func createGoPostReceiveHook(component models.Component, deployPath string) string {
	return fmt.Sprintf(`#!/bin/bash
echo "Deploying Go component: %s"

# Get the target branch (usually main or master)
TARGET="%s"
while read oldrev newrev ref
do
    # Check if the pushed branch is our target branch
    if [[ $ref = refs/heads/$TARGET ]]; 
    then
        echo "Deploying $TARGET branch..."
        
        # Checkout code to the deployment directory
        GIT_WORK_TREE=%s git checkout -f $TARGET
        cd %s
        
        # Update environment variables
        echo '%s' > %s/.env
        
        # Build the application
        echo "Building Go application..."
        go build -o app
        
        # Restart the service
        echo "Restarting service..."
        systemctl restart byop-%s
        
        # Notify about completion
        echo "Go deployment completed successfully"
    fi
done
`, component.Name, component.Branch, deployPath, deployPath, component.EnvVariables, deployPath, component.ID)
}

// createNodePostReceiveHook generates a Git hook for Node.js components
func createNodePostReceiveHook(component models.Component, deployPath string) string {
	return fmt.Sprintf(`#!/bin/bash
echo "Deploying Node.js component: %s"

# Get the target branch (usually main or master)
TARGET="%s"
while read oldrev newrev ref
do
    # Check if the pushed branch is our target branch
    if [[ $ref = refs/heads/$TARGET ]]; 
    then
        echo "Deploying $TARGET branch..."
        
        # Checkout code to the deployment directory
        GIT_WORK_TREE=%s git checkout -f $TARGET
        cd %s
        
        # Update environment variables
        echo '%s' > %s/.env
        
        # Install dependencies
        echo "Installing dependencies..."
        npm install
        
        # Build the application if there's a build command
        if [[ "%s" != "" ]]; then
            echo "Building application..."
            %s || true
        fi
        
        # Restart the PM2 process
        echo "Restarting PM2 process..."
        pm2 restart byop-%s || pm2 start npm --name "byop-%s" -- start
        pm2 save
        
        # Notify about completion
        echo "Node.js deployment completed successfully"
    fi
done
`, component.Name, component.Branch, deployPath, deployPath, component.EnvVariables, deployPath, component.BuildCommand, component.BuildCommand, component.ID, component.ID)
}

// createPythonPostReceiveHook generates a Git hook for Python components
func createPythonPostReceiveHook(component models.Component, deployPath string) string {
	return fmt.Sprintf(`#!/bin/bash
echo "Deploying Python component: %s"

# Get the target branch (usually main or master)
TARGET="%s"
while read oldrev newrev ref
do
    # Check if the pushed branch is our target branch
    if [[ $ref = refs/heads/$TARGET ]]; 
    then
        echo "Deploying $TARGET branch..."
        
        # Checkout code to the deployment directory
        GIT_WORK_TREE=%s git checkout -f $TARGET
        cd %s
        
        # Update environment variables
        echo '%s' > %s/.env
        
        # Update dependencies
        echo "Updating Python dependencies..."
        source venv/bin/activate
        pip install -r requirements.txt
        
        # Restart the service
        echo "Restarting service..."
        systemctl restart byop-%s
        
        # Notify about completion
        echo "Python deployment completed successfully"
    fi
done
`, component.Name, component.Branch, deployPath, deployPath, component.EnvVariables, deployPath, component.ID)
}

// createDatabasePostReceiveHook generates a Git hook for database components
func createDatabasePostReceiveHook(component models.Component, deployPath string, dbType string) string {
	var configUpdate, restartCmd string

	switch dbType {
	case "postgresql":
		configUpdate = fmt.Sprintf(`
        # Apply configuration changes if available
        if [ -f %s/postgresql.conf ]; then
            cp %s/postgresql.conf /etc/postgresql/*/main/
            echo "Updated PostgreSQL configuration"
        fi`, deployPath, deployPath)
		restartCmd = "systemctl restart postgresql"
	case "mariadb", "mysql":
		configUpdate = fmt.Sprintf(`
        # Apply configuration changes if available
        if [ -f %s/my.cnf ]; then
            cp %s/my.cnf /etc/mysql/
            echo "Updated MariaDB configuration"
        fi`, deployPath, deployPath)
		restartCmd = "systemctl restart mariadb"
	case "mongodb":
		configUpdate = fmt.Sprintf(`
        # Apply configuration changes if available
        if [ -f %s/mongodb.conf ]; then
            cp %s/mongodb.conf /etc/mongodb.conf
            echo "Updated MongoDB configuration"
        fi`, deployPath, deployPath)
		restartCmd = "systemctl restart mongodb"
	}

	return fmt.Sprintf(`#!/bin/bash
echo "Deploying database component: %s"

# Get the target branch (usually main or master)
TARGET="%s"
while read oldrev newrev ref
do
    # Check if the pushed branch is our target branch
    if [[ $ref = refs/heads/$TARGET ]]; 
    then
        echo "Deploying $TARGET branch..."
        
        # Checkout code to the deployment directory
        GIT_WORK_TREE=%s git checkout -f $TARGET
        cd %s
        
        # Update environment variables
        echo '%s' > %s/.env
        %s
        
        # Run any database migrations if available
        if [ -f %s/migrations/run.sh ]; then
            echo "Running database migrations..."
            bash %s/migrations/run.sh
        fi
        
        # Restart database service
        echo "Restarting database service..."
        %s
        
        # Notify about completion
        echo "Database component deployment completed successfully"
    fi
done
`, component.Name, component.Branch, deployPath, deployPath, component.EnvVariables, deployPath, configUpdate, deployPath, deployPath, restartCmd)
}

// createSystemdServiceCommand creates a systemd service file for the component
func createSystemdServiceCommand(component models.Component, deploymentPath string) string {
	var execStart string
	var workingDir string

	workingDir = deploymentPath

	switch component.Language {
	case "golang":
		execStart = fmt.Sprintf("%s/app", deploymentPath)
	case "python":
		execStart = fmt.Sprintf("%s/venv/bin/python %s/main.py", deploymentPath, deploymentPath)
	default:
		execStart = component.BuildCommand
	}

	serviceFile := fmt.Sprintf(`[Unit]
Description=BYOP Component %s
After=network.target

[Service]
ExecStart=%s
WorkingDirectory=%s
Restart=always
User=root
Group=root
Environment=PATH=/usr/bin:/usr/local/bin
EnvironmentFile=%s/.env

[Install]
WantedBy=multi-user.target
`, component.Name, execStart, workingDir, deploymentPath)

	return fmt.Sprintf("echo '%s' > /etc/systemd/system/byop-%s.service", serviceFile, component.ID)
}