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-receiveset -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 serverset -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.shset -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.shset -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.shset -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 deploymentTools Tambahan:
– Webhooks untuk external integrations
– Slack/Discord notifications
– Automated testing sebelum deploy
– Canary deployment untuk zero-downtime
Ditulis oleh
Hendra Wijaya