Lewati ke konten
Kembali ke Blog

Cara Automasi Deployment Website dengan Bash Script dan Git

· · 9 menit baca

Automasi deployment adalah kunci untuk streamline proses development. Artikel ini membahas cara membuat deployment automation menggunakan bash script dan Git hooks untuk CI/CD pipeline yang sederhana namun powerful.

1. Setup Repository dan Git Hooks

Git Hooks Basics

Git hooks adalah script yang dieksekusi pada event tertentu dalam Git workflow.

# Lokasi hooks dalam repository
ls -la .git/hooks/

Buat hook baru (contoh: post-receive untuk deployment)

touch .git/hooks/post-receive chmod +x .git/hooks/post-receive

Hook Types yang Berguna

# Client-side hooks
pre-commit          # Sebelum commit dibuat
prepare-commit-msg  # Sebelum editor commit message dibuka
commit-msg          # Sebelum commit diterima
post-commit         # Setelah commit dibuat

Server-side hooks

pre-receive # Sebelum menerima push update # Sebelum update ref post-receive # Setelah menerima push (untuk deployment)

2. Deployment Script dengan Git

Server-side Deployment Hook

#!/bin/bash
# /var/repo/website.git/hooks/post-receive

set -euo pipefail

Configuration

DEPLOY_DIR="/var/www/html" GIT_DIR="/var/repo/website.git" BACKUP_DIR="/backup/website" BRANCH="main" LOG_FILE="/var/log/deploy.log"

Logging function

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" }

Main deployment logic

deploy() { log "Starting deployment..."

# Read stdin dari git push
while read oldrev newrev refname; do
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)

    if [ "$branch" = "$BRANCH" ]; then
        log "Deploying branch: $branch"
        log "Commit: $newrev"

        # Backup current version
        if [ -d "$DEPLOY_DIR" ]; then
            backup_name="backup_$(date +%Y%m%d_%H%M%S)"
            log "Creating backup: $backup_name"
            cp -r "$DEPLOY_DIR" "$BACKUP_DIR/$backup_name"
        fi

        # Checkout code ke deploy directory
        log "Checking out code..."
        GIT_WORK_TREE="$DEPLOY_DIR" git checkout -f "$BRANCH"

        # Change ke deploy directory
        cd "$DEPLOY_DIR"

        # Run deployment tasks
        log "Running deployment tasks..."

        # Install dependencies
        if [ -f "composer.json" ]; then
            log "Installing composer dependencies..."
            composer install --no-dev --optimize-autoloader
        fi

        if [ -f "package.json" ]; then
            log "Installing npm dependencies..."
            npm ci --production
            log "Building assets..."
            npm run build
        fi

        # Set permissions
        log "Setting permissions..."
        chown -R www-data:www-data "$DEPLOY_DIR"
        find "$DEPLOY_DIR" -type d -exec chmod 755 {} \;
        find "$DEPLOY_DIR" -type f -exec chmod 644 {} \;

        # Clear caches
        log "Clearing caches..."
        if [ -f "artisan" ]; then
            php artisan cache:clear
            php artisan config:clear
            php artisan view:clear
            php artisan migrate --force
        fi

        # Restart services jika diperlukan
        if [ -f "/etc/init.d/nginx" ]; then
            log "Reloading nginx..."
            sudo systemctl reload nginx
        fi

        log "Deployment completed successfully!"

        # Send notification
        echo "Deployment successful: $newrev" | mail -s "Deployment Success" [email protected]
    fi
done

}

Run deployment

deploy

Local Deployment Script

#!/bin/bash
# deploy.sh - Run locally untuk deploy ke server

set -euo pipefail

Configuration

REMOTE_HOST="server.example.com" REMOTE_USER="deploy" REPO_PATH="/var/repo/website.git" BRANCH="main" SSH_KEY="$HOME/.ssh/deploy_key"

Colors untuk output

RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color

log_info() { echo -e "${GREEN}[INFO]${NC} $1" }

log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" }

log_error() { echo -e "${RED}[ERROR]${NC} $1" }

Pre-deployment checks

pre_deploy_checks() { log_info "Running pre-deployment checks..."

# Check working directory clean
if ! git diff --quiet; then
    log_error "Working directory tidak clean. Commit atau stash perubahan terlebih dahulu."
    exit 1
fi

# Check tests pass
if [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
    log_info "Running tests..."
    if ! ./vendor/bin/phpunit; then
        log_error "Tests failed!"
        exit 1
    fi
fi

if [ -f "package.json" ]; then
    if grep -q '"test"' package.json; then
        log_info "Running npm tests..."
        if ! npm test; then
            log_error "NPM tests failed!"
            exit 1
        fi
    fi
fi

log_info "All checks passed!"

}

Deploy ke remote server

deploy_to_remote() {
log_info "Deploying to $REMOTE_HOST..."

# Push ke remote
log_info "Pushing to $BRANCH branch..."
git push origin "$BRANCH"

# Trigger deployment via SSH
log_info "Triggering deployment on remote server..."
ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" \
    "cd $REPO_PATH && git fetch origin && git reset --hard origin/$BRANCH"

log_info "Deployment triggered successfully!"

}

Rollback function

rollback() {
log_warn "Initiating rollback..."

ssh -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" \
    "cd $DEPLOY_DIR && git revert HEAD --no-edit && sudo systemctl reload nginx"

log_info "Rollback completed!"

}

Main execution

main() {
case "${1:-deploy}" in
deploy)
pre_deploy_checks
deploy_to_remote
;;
rollback)
rollback
;;
*)
echo "Usage: $0 [deploy|rollback]"
exit 1
;;
esac
}

main "$@"

3. Advanced Deployment Features

Blue-Green Deployment

#!/bin/bash
# blue-green-deploy.sh

set -euo pipefail

CURRENT_DIR="/var/www/current" BLUE_DIR="/var/www/blue" GREEN_DIR="/var/www/green" NGINX_CONF="/etc/nginx/sites-available/default" LOG_FILE="/var/log/blue-green-deploy.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" }

Detect current active environment

detect_active() { if [ -L "$CURRENT_DIR" ]; then current_target=$(readlink "$CURRENT_DIR") if [[ "$current_target" == "$BLUE_DIR" ]]; then echo "blue" else echo "green" fi else echo "blue" # Default fi }

Deploy ke inactive environment

deploy() { local active=$(detect_active) local target

if [ "$active" = "blue" ]; then
    target="$GREEN_DIR"
    log "Deploying to GREEN environment"
else
    target="$BLUE_DIR"
    log "Deploying to BLUE environment"
fi

# Clear target directory
rm -rf "$target"/*

# Checkout new code
GIT_WORK_TREE="$target" git checkout -f main

# Setup target
cd "$target"

# Install dependencies
if [ -f "composer.json" ]; then
    composer install --no-dev --optimize-autoloader
fi

if [ -f "package.json" ]; then
    npm ci
    npm run build
fi

# Run database migrations
if [ -f "artisan" ]; then
    php artisan migrate --force
fi

# Health check
log "Running health check on new environment..."
# Tambahkan health check di sini

# Switch symlink
log "Switching to new environment..."
rm -f "$CURRENT_DIR"
ln -s "$target" "$CURRENT_DIR"

# Reload nginx
sudo systemctl reload nginx

log "Blue-green deployment completed!"

}

main() {
deploy
}

main "$@"

Database Migration Handler

#!/bin/bash
# migrate.sh

set -euo pipefail

DB_HOST="${DB_HOST:-localhost}" DB_NAME="${DB_NAME:-myapp}" DB_USER="${DB_USER:-root}" DB_PASS="${DB_PASS:-}" BACKUP_DIR="/backup/db" MIGRATION_DIR="/var/www/current/database/migrations"

Backup database before migration

backup_database() { local backup_name="pre migration$(date +%Y%m%d_%H%M%S).sql" local backup_path="$BACKUP_DIR/$backup_name"

echo "Creating database backup: $backup_name"
mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$backup_path"
gzip "$backup_path"

echo "Backup created: ${backup_path}.gz"

}

Run migrations with rollback capability

run_migration() {
local migration_file="$1"

echo "Running migration: $migration_file"

if mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$migration_file"; then
    echo "Migration successful: $migration_file"
else
    echo "Migration failed: $migration_file"
    exit 1
fi

}

Main migration process

main() {

Check prerequisites

if [ -z "$DB_PASS" ]; then
    echo "Error: Database password not set"
    exit 1
fi

# Create backup
backup_database

# Run migrations
for migration in "$MIGRATION_DIR"/*.sql; do
    if [ -f "$migration" ]; then
        run_migration "$migration"
    fi
done

echo "All migrations completed successfully!"

}

main "$@"

4. Environment Configuration

Environment-based Configuration

#!/bin/bash
# setup-environment.sh

set -euo pipefail

ENV="${1:-production}" CONFIG_DIR="/etc/myapp" ENV_FILE="$CONFIG_DIR/.env"

case "$ENV" in development) DB_HOST="localhost" DB_NAME="myapp_dev" DEBUG=true ;; staging) DB_HOST="staging-db.internal" DB_NAME="myapp_staging" DEBUG=false ;; production) DB_HOST="prod-db.internal" DB_NAME="myapp_prod" DEBUG=false ;; *) echo "Unknown environment: $ENV" exit 1 ;; esac

Generate environment file

cat > "$ENV_FILE" << EOF APP_ENV=$ENV APP_DEBUG=$DEBUG DB_HOST=$DB_HOST DB_DATABASE=$DB_NAME DB_USERNAME=app_user DB_PASSWORD=$(openssl rand -base64 32)

CACHE_DRIVER=redis QUEUE_DRIVER=redis

LOG_LEVEL=info EOF

chown root:www-data "$ENV_FILE" chmod 640 "$ENV_FILE"

echo "Environment configuration created for: $ENV"

Kesimpulan

Automasi deployment dengan bash script dan Git hooks menyederhanakan proses deployment dan mengurangi human error. Dengan Git hooks, deployment terjadi secara otomatis saat push, sementara bash script menangani task-task seperti dependency installation, database migration, dan cache clearing.

Best Practices:
1. Selalu backup sebelum deploy
2. Implementasikan rollback mechanism
3. Gunakan environment variables untuk konfigurasi
4. Log semua deployment activities
5. Test deployment script di staging terlebih dahulu
6. Gunakan health checks setelah deployment

Tools Tambahan:
– Webhooks untuk external integrations
– Slack/Discord notifications
– Automated testing sebelum deploy
– Canary deployment untuk zero-downtime

Ditulis oleh

Hendra Wijaya

Tinggalkan Komentar

Email tidak akan ditampilkan.