Skip to content

Instantly share code, notes, and snippets.

@chetanupare
Last active January 21, 2026 22:33
Show Gist options
  • Select an option

  • Save chetanupare/5d75653537c2bf4c2068b317381f2b2f to your computer and use it in GitHub Desktop.

Select an option

Save chetanupare/5d75653537c2bf4c2068b317381f2b2f to your computer and use it in GitHub Desktop.
Laravel auto-deploy script for AWS Ubuntu (GitHub webhook based)

Laravel AWS Auto-Deploy Script

A production-grade Bash script to automatically deploy Laravel applications on AWS Ubuntu servers using GitHub webhooks. This script handles secure SSH authentication, proper permission separation, and comprehensive error handling.

🚀 Features

  • GitHub Webhook Integration - Automatic deployments triggered by Git pushes
  • Secure SSH Authentication - No tokens or passwords required
  • Permission Management - Proper separation between repository owner (ubuntu) and web server user (www-data)
  • Git Safe Directory Handling - Automatically configures Git to work with different user ownership
  • Laravel Optimization - Automatic cache clearing and optimization
  • Database Tracking - Optional database-backed deployment status tracking via DeployLog model
  • Detailed Logging - Per-deployment log files with timestamps
  • Error Handling - Comprehensive error handling with automatic status updates
  • Interactive & Non-Interactive - Works both from terminal and webhook calls
  • Color-Coded Output - Beautiful terminal output when run interactively

✅ Tested On

  • Operating Systems: Ubuntu 20.04 / 22.04 (AWS EC2)
  • Web Servers: Apache + PHP-FPM / mod_php
  • PHP Versions: PHP 8.0, 8.1, 8.2
  • Laravel Versions: Laravel 9, 10, 11
  • Git Authentication: GitHub SSH (ed25519 / RSA)

📋 Prerequisites

Before using this script, ensure you have:

  1. Git Repository owned by ubuntu user (or your server user)
  2. Web Server User (www-data on Ubuntu/Debian, apache on CentOS)
  3. SSH Keys configured in /var/www/.ssh for www-data user
  4. Passwordless Sudo configured for:
    • /usr/bin/git (required)
    • /usr/bin/composer (optional, if using composer)
    • systemctl restart apache2 (optional, for automatic server restart)

🔧 Setup Instructions

Step 1: Configure SSH Keys for www-data

# Copy SSH keys from ubuntu user to www-data
sudo cp -r ~/.ssh /var/www/.ssh
sudo chown -R www-data:www-data /var/www/.ssh
sudo chmod 700 /var/www/.ssh
sudo chmod 600 /var/www/.ssh/*

Step 2: Configure Passwordless Sudo

# Edit sudoers file
sudo visudo

# Add these lines (replace 'ubuntu' with your server user if different):
www-data ALL=(ubuntu) NOPASSWD: /usr/bin/git
www-data ALL=(ubuntu) NOPASSWD: /usr/bin/composer
www-data ALL=(root) NOPASSWD: /bin/systemctl restart apache2

Step 3: Configure the Script

Edit the configuration section at the top of deploy.sh:

# Project name (used in output messages)
PROJECT_NAME="Your Project Name"

# Repository paths (check both common locations)
REPO_PATH_PRODUCTION="/var/www/your-project"
REPO_PATH_LOCAL="/var/www/html/your-project"

# Git branch to deploy (usually 'main' or 'master')
GIT_BRANCH="main"

# Server user who owns the repository
REPO_OWNER="ubuntu"

# Web server user
WEB_USER="www-data"

Step 4: Make Script Executable

chmod +x deploy.sh

Step 5: Test the Script

# Test manually
./deploy.sh

# Or with a deploy log ID (if using database tracking)
./deploy.sh your-deploy-log-id

🔗 GitHub Webhook Configuration

  1. Go to your GitHub repository

  2. Navigate to SettingsWebhooksAdd webhook

  3. Configure the webhook:

    • Payload URL: https://your-domain.com/deploy
    • Content type: application/json
    • Secret: (optional, add validation in your webhook route)
    • Events: Select "Just the push event"
    • Active: ✓
  4. In your Laravel routes/web.php, add:

Route::post('/deploy', function () {
    // Your webhook handler
    // Execute: shell_exec("cd /var/www/your-project && ./deploy.sh '{$deployId}' > /tmp/deploy.log 2>&1 &");
});

📖 Usage

Manual Deployment

# Basic usage
./deploy.sh

# With deploy log ID (for database tracking)
./deploy.sh 507f1f77bcf86cd799439011

Automatic Deployment (Webhook)

The script is automatically triggered when GitHub sends a webhook POST request to your configured endpoint.

📁 File Structure

/var/www/your-project/
├── deploy.sh              # Main deployment script
├── .git/                  # Git repository
├── artisan               # Laravel artisan
└── ...

🔍 How It Works

  1. Webhook Triggered: GitHub sends POST request to your webhook endpoint
  2. Script Execution: Laravel route executes deploy.sh in background
  3. SSH Configuration: Script configures SSH keys and GitHub host keys
  4. Git Pull: Pulls latest code using sudo -u ubuntu git pull
  5. Cache Clear: Runs php artisan optimize:clear
  6. Optimization: Runs php artisan optimize
  7. Server Restart: Restarts Apache/Nginx (if configured)
  8. Status Update: Updates deployment status in database (if using DeployLog)

📝 Logging

Deployment logs are stored in:

  • Per-deployment: /tmp/deploy_{deploy_id}.log
  • Global log: /tmp/deploy.log (if writable)

Logs include:

  • Timestamp for each operation
  • Success/error messages
  • Git commit information
  • Deployment duration

🛠️ Optional: Database Tracking

If you want to track deployments in your database, create a DeployLog model:

// app/Models/DeployLog.php
class DeployLog extends Model
{
    protected $fillable = [
        'triggered_by',
        'trigger_type', // 'webhook', 'manual'
        'status', // 'pending', 'running', 'success', 'failed'
        'started_at',
        'completed_at',
        'duration',
        'git_branch',
        'git_commit',
        'error_message',
    ];
}

Create an Artisan command to update status:

php artisan make:command UpdateDeployStatus

⚠️ Troubleshooting

"Permission denied" errors

  • Ensure SSH keys are owned by www-data: sudo chown -R www-data:www-data /var/www/.ssh
  • Check file permissions: chmod 600 /var/www/.ssh/id_*

"Git pull failed" errors

  • Verify passwordless sudo: sudo -u ubuntu git pull origin main
  • Check Git remote URL: git remote get-url origin
  • Ensure repository is owned by ubuntu user

"Apache restart failed" warnings

  • This is non-critical - deployment will still succeed
  • Manually restart Apache: sudo systemctl restart apache2
  • Or configure passwordless sudo for Apache restart

Script exits unexpectedly

  • Check log files: tail -f /tmp/deploy.log
  • Verify all paths in configuration section
  • Ensure PHP and Git are in PATH

🔒 Security Considerations

  • SSH Keys: Store in /var/www/.ssh with proper permissions (600)
  • Sudo Access: Limit to specific commands only
  • Webhook Validation: Add secret validation in your webhook route
  • Log Files: Regularly clean up old log files
  • Repository Access: Use SSH keys, not personal access tokens

📄 License

MIT License - feel free to use and modify for your projects.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📧 Support

For issues and questions:

  • Open an issue on GitHub
  • Check the troubleshooting section above
  • Review log files for detailed error messages

👤 Author

Chetan Upare

GitHub LinkedIn Twitter Email


Made with ❤️ for the Laravel community

#!/bin/bash
# ============================================================================
# Laravel Auto-Deploy Script for GitHub Webhooks
# ============================================================================
#
# Purpose: Automatically deploy Laravel applications on AWS Ubuntu servers
# when GitHub webhooks are triggered
#
# Requirements:
# - Git repository owned by 'ubuntu' user (or your server user)
# - Webhook runs as 'www-data' user (Apache/Nginx)
# - SSH keys configured in /var/www/.ssh for www-data
# - Passwordless sudo configured for git/composer/artisan commands
# - Laravel application with DeployLog model (optional, for tracking)
#
# Setup Instructions:
# 1. Place this script in your project root: /var/www/your-project/deploy.sh
# 2. Make it executable: chmod +x deploy.sh
# 3. Configure GitHub webhook to POST to: https://yourdomain.com/deploy
# 4. Ensure www-data can execute: sudo visudo
# Add: www-data ALL=(ubuntu) NOPASSWD: /usr/bin/git
# 5. Copy SSH keys: sudo cp -r ~/.ssh /var/www/.ssh && sudo chown -R www-data:www-data /var/www/.ssh
#
# Usage:
# Manual: ./deploy.sh [deploy_log_id]
# Webhook: Called automatically by GitHub webhook route
#
# Author: Chetan Upare
# License: MIT
# ============================================================================
# ============================================================================
# CONFIGURATION - CUSTOMIZE THESE VALUES FOR YOUR PROJECT
# ============================================================================
# Project name (used in output messages)
PROJECT_NAME="Your Project Name"
# Repository paths (check both common locations)
# Production: /var/www/your-project
# Local/Dev: /var/www/html/your-project
REPO_PATH_PRODUCTION="/var/www/your-project"
REPO_PATH_LOCAL="/var/www/html/your-project"
# Git branch to deploy (usually 'main' or 'master')
GIT_BRANCH="main"
# Server user who owns the repository (usually 'ubuntu' on AWS)
REPO_OWNER="ubuntu"
# Web server user (usually 'www-data' on Ubuntu/Debian, 'apache' on CentOS)
WEB_USER="www-data"
# ============================================================================
# SCRIPT START - DO NOT MODIFY BELOW UNLESS YOU KNOW WHAT YOU'RE DOING
# ============================================================================
# Get deploy log ID from argument or environment variable
# This is used to track deployment status in database (optional)
DEPLOY_LOG_ID="${1:-${DEPLOY_LOG_ID}}"
# Create a unique log file for this deployment
if [ -n "$DEPLOY_LOG_ID" ]; then
DEPLOY_LOG_FILE="/tmp/deploy_${DEPLOY_LOG_ID}.log"
PRIMARY_LOG_FILE="$DEPLOY_LOG_FILE"
echo "Deployment started with ID: $DEPLOY_LOG_ID" > "$DEPLOY_LOG_FILE"
echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" >> "$DEPLOY_LOG_FILE"
echo "=========================================" >> "$DEPLOY_LOG_FILE"
else
DEPLOY_LOG_FILE="/tmp/deploy.log"
PRIMARY_LOG_FILE="$DEPLOY_LOG_FILE"
fi
# Ensure log file is writable (create if needed)
touch "$PRIMARY_LOG_FILE" 2>/dev/null || true
# ============================================================================
# FUNCTION: Update deployment status in database
# ============================================================================
# This function updates the DeployLog model in Laravel to track deployment
# status. It tries the artisan command first, then falls back to tinker.
# ============================================================================
update_deploy_status() {
local status="$1"
local message="$2"
if [ -n "$DEPLOY_LOG_ID" ] && [ -n "$status" ]; then
# Determine the correct Laravel directory (production vs local)
local LARAVEL_DIR=""
if [ -d "$REPO_PATH_PRODUCTION" ] && [ -f "$REPO_PATH_PRODUCTION/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_PRODUCTION"
elif [ -d "$REPO_PATH_LOCAL" ] && [ -f "$REPO_PATH_LOCAL/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_LOCAL"
else
# Fallback: use REPO_PATH if set
LARAVEL_DIR="${REPO_PATH:-$REPO_PATH_LOCAL}"
fi
# Ensure we're in the correct directory
if [ -n "$LARAVEL_DIR" ] && [ -d "$LARAVEL_DIR" ]; then
cd "$LARAVEL_DIR" 2>/dev/null || {
# Try both paths if first fails
cd "$REPO_PATH_PRODUCTION" 2>/dev/null || cd "$REPO_PATH_LOCAL" 2>/dev/null || true
}
else
# Last resort: try both paths
cd "$REPO_PATH_PRODUCTION" 2>/dev/null || cd "$REPO_PATH_LOCAL" 2>/dev/null || true
fi
# Try artisan command first (cleaner approach)
if php artisan deploy:update-status "$DEPLOY_LOG_ID" "$status" "$message" 2>&1; then
return 0
else
# Fallback to tinker if artisan command fails
php artisan tinker --execute="
try {
\$log = \App\Models\DeployLog::find('$DEPLOY_LOG_ID');
if (\$log) {
\$log->status = '$status';
\$log->completed_at = now();
\$log->duration = \$log->started_at ? now()->diffInSeconds(\$log->started_at) : 0;
if ('$status' === 'failed') {
\$log->error_message = '$message';
} else {
\$log->output = '$message';
}
\$log->save();
echo 'Status updated via tinker';
} else {
echo 'Deploy log not found: $DEPLOY_LOG_ID';
}
} catch (\Exception \$e) {
echo 'Error: ' . \$e->getMessage();
}
" 2>&1 || true
fi
fi
}
# Trap to ensure status is updated on script exit (even if unexpected)
# This prevents deployments from getting stuck in "RUNNING" status
trap 'if [ $? -ne 0 ] && [ -n "$DEPLOY_LOG_ID" ]; then update_deploy_status "failed" "Script exited unexpectedly with code $?"; fi' EXIT
# ============================================================================
# COLOR CODES AND SYMBOLS FOR TERMINAL OUTPUT
# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
WHITE='\033[1;37m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Unicode symbols for better visual feedback
CHECK='✓'
CROSS='✗'
ARROW='➜'
ROCKET='🚀'
GEAR='⚙️'
FOLDER='📁'
KEY='🔑'
CACHE='💾'
SERVER='🖥️'
SUCCESS='✅'
ERROR='❌'
INFO='ℹ️'
# ============================================================================
# DETECT INTERACTIVE VS WEBHOOK EXECUTION
# ============================================================================
# When run from terminal: show colored output
# When run from webhook: log to file only (no colors)
# ============================================================================
if [ -t 0 ]; then
# Running interactively - show colored output
INTERACTIVE=true
USE_COLORS=true
else
# Running from webhook - log to file only (no colors)
INTERACTIVE=false
USE_COLORS=false
fi
# ============================================================================
# FUNCTION: Output message with colors and logging
# ============================================================================
# Usage: output "Message text" "type"
# Types: info, success, error, warning, header
# ============================================================================
output() {
local message="$1"
local type="${2:-info}" # Default to 'info' if not specified
if [ "$USE_COLORS" = true ]; then
case $type in
success)
echo -e "${GREEN}${CHECK} ${message}${NC}"
;;
error)
echo -e "${RED}${ERROR} ${message}${NC}"
;;
warning)
echo -e "${YELLOW}${INFO} ${message}${NC}"
;;
info)
echo -e "${CYAN}${ARROW} ${message}${NC}"
;;
header)
echo -e "${BOLD}${MAGENTA}${message}${NC}"
;;
*)
echo -e "${CYAN}${ARROW} ${message}${NC}"
;;
esac
else
echo "$message"
fi
# Always log to primary log file (deployment-specific or global)
if [ -n "$PRIMARY_LOG_FILE" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> "$PRIMARY_LOG_FILE" 2>/dev/null || true
fi
# Also log to global deploy.log if we have write permission (optional)
if [ -n "$PRIMARY_LOG_FILE" ] && [ "$PRIMARY_LOG_FILE" != "/tmp/deploy.log" ] && [ -w /tmp/deploy.log ] 2>/dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> /tmp/deploy.log 2>/dev/null || true
fi
}
# ============================================================================
# FUNCTION: Print deployment header
# ============================================================================
print_header() {
if [ "$USE_COLORS" = true ]; then
echo ""
echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${BLUE}║${NC} ${WHITE}${ROCKET} ${PROJECT_NAME} - Auto Deployment Script${NC} ${BOLD}${BLUE}║${NC}"
echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
else
echo "========================================="
echo "${PROJECT_NAME} - Auto Deployment Script"
echo "========================================="
fi
}
# ============================================================================
# FUNCTION: Print separator line
# ============================================================================
print_separator() {
if [ "$USE_COLORS" = true ]; then
echo -e "${CYAN}────────────────────────────────────────────────────────────${NC}"
else
echo "────────────────────────────────────────────────────────────"
fi
}
# ============================================================================
# DETERMINE REPOSITORY PATH
# ============================================================================
# Check both common locations for the Git repository
# ============================================================================
if [ -d "$REPO_PATH_LOCAL/.git" ]; then
REPO_PATH="$REPO_PATH_LOCAL"
elif [ -d "$REPO_PATH_PRODUCTION/.git" ]; then
REPO_PATH="$REPO_PATH_PRODUCTION"
else
print_header
output "Git repository not found in expected locations" "error"
output "Checked: $REPO_PATH_LOCAL and $REPO_PATH_PRODUCTION" "error"
exit 1
fi
# Change to repository directory
cd "$REPO_PATH" || {
print_header
output "Cannot change to $REPO_PATH directory" "error"
if [ -n "$DEPLOY_LOG_ID" ]; then
# Try to find Laravel directory for status update
LARAVEL_DIR=""
if [ -d "$REPO_PATH_PRODUCTION" ] && [ -f "$REPO_PATH_PRODUCTION/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_PRODUCTION"
elif [ -d "$REPO_PATH_LOCAL" ] && [ -f "$REPO_PATH_LOCAL/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_LOCAL"
fi
if [ -n "$LARAVEL_DIR" ]; then
cd "$LARAVEL_DIR" 2>/dev/null || true
update_deploy_status "failed" "Cannot change to repository directory"
fi
fi
exit 1
}
# ============================================================================
# PRINT HEADER AND START DEPLOYMENT
# ============================================================================
print_header
output "Deployment started at $(date '+%Y-%m-%d %H:%M:%S')" "info"
output "Repository: ${BOLD}$REPO_PATH${NC}" "info"
print_separator
# ============================================================================
# CONFIGURE SSH FOR GITHUB
# ============================================================================
# The script runs as www-data, so we need to use SSH keys from /var/www/.ssh
# ============================================================================
output "Configuring SSH for GitHub..." "info"
# Detect current user
CURRENT_USER=$(whoami)
output "Running as user: $CURRENT_USER" "info"
# Check for SSH keys in priority order:
# 1. /var/www/.ssh (for www-data user)
# 2. $HOME/.ssh (for current user)
if [ -d "/var/www/.ssh" ] && [ -f "/var/www/.ssh/id_ed25519" ]; then
output "Using ${WEB_USER} SSH keys from /var/www/.ssh" "success"
SSH_DIR="/var/www/.ssh"
SSH_KEY="/var/www/.ssh/id_ed25519"
elif [ -d "/var/www/.ssh" ] && [ -f "/var/www/.ssh/id_rsa" ]; then
output "Using ${WEB_USER} SSH keys from /var/www/.ssh" "success"
SSH_DIR="/var/www/.ssh"
SSH_KEY="/var/www/.ssh/id_rsa"
elif [ "$CURRENT_USER" = "$WEB_USER" ] || [ "$CURRENT_USER" = "apache" ]; then
# Running as web server user - must use /var/www/.ssh
output "Running as web server user, checking /var/www/.ssh..." "info"
if [ -d "/var/www/.ssh" ]; then
output "ERROR: /var/www/.ssh directory exists but no keys found" "error"
if [ -n "$DEPLOY_LOG_ID" ]; then
update_deploy_status "failed" "SSH keys not found in /var/www/.ssh"
fi
exit 1
else
output "ERROR: /var/www/.ssh directory does not exist" "error"
if [ -n "$DEPLOY_LOG_ID" ]; then
update_deploy_status "failed" "/var/www/.ssh directory not found"
fi
exit 1
fi
elif [ -f "$HOME/.ssh/id_ed25519" ]; then
output "Using SSH keys from $HOME/.ssh" "success"
SSH_DIR="$HOME/.ssh"
SSH_KEY="$HOME/.ssh/id_ed25519"
elif [ -f "$HOME/.ssh/id_rsa" ]; then
output "Using SSH keys from $HOME/.ssh" "success"
SSH_DIR="$HOME/.ssh"
SSH_KEY="$HOME/.ssh/id_rsa"
else
output "No SSH keys found. Please set up SSH keys for ${WEB_USER} in /var/www/.ssh" "error"
if [ -n "$DEPLOY_LOG_ID" ]; then
update_deploy_status "failed" "No SSH keys found"
fi
exit 1
fi
# Ensure known_hosts exists and has GitHub
# This prevents SSH from prompting for host key verification
if [ ! -f "$SSH_DIR/known_hosts" ] || ! grep -q "github.com" "$SSH_DIR/known_hosts" 2>/dev/null; then
output "Adding GitHub to known_hosts..." "info"
ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> "$SSH_DIR/known_hosts" 2>/dev/null
chmod 644 "$SSH_DIR/known_hosts"
output "GitHub SSH host key added" "success"
else
output "GitHub SSH already configured" "success"
fi
# Configure Git to use SSH with the correct key
export GIT_SSH_COMMAND="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes"
# ============================================================================
# ENSURE REMOTE IS USING SSH (NOT HTTPS)
# ============================================================================
# Convert HTTPS remotes to SSH for better security and authentication
# ============================================================================
REMOTE_URL=$(git remote get-url origin 2>/dev/null)
if [[ "$REMOTE_URL" == https://github.com/* ]]; then
output "Converting HTTPS remote to SSH..." "warning"
# Convert https://github.com/user/repo.git to git@github.com:user/repo.git
SSH_URL=$(echo "$REMOTE_URL" | sed 's|https://github.com/|git@github.com:|' | sed 's|\.git$||')
SSH_URL="${SSH_URL}.git"
git remote set-url origin "$SSH_URL"
output "Remote URL converted to SSH" "success"
fi
# ============================================================================
# CONFIGURE GIT SAFE DIRECTORY
# ============================================================================
# Git 2.35.2+ requires safe.directory configuration when repo is owned by
# a different user than the one running the script (www-data vs ubuntu)
# ============================================================================
output "Configuring Git safe directory..." "info"
# Ensure we're in the repo directory
cd "$REPO_PATH" 2>/dev/null || true
# Configure globally (for www-data user)
git config --global --add safe.directory "$REPO_PATH" 2>/dev/null || true
# Also configure locally (in case global config doesn't work)
git config --add safe.directory "$REPO_PATH" 2>/dev/null || true
output "Git safe directory configured" "success"
print_separator
# ============================================================================
# PULL LATEST CODE FROM GITHUB
# ============================================================================
# Use sudo -u to run git as the repository owner (avoids permission issues)
# ============================================================================
output "Pulling latest code from GitHub..." "info"
if [ "$INTERACTIVE" = true ]; then
if sudo -u "$REPO_OWNER" git pull origin "$GIT_BRANCH" 2>&1 | tee -a "$PRIMARY_LOG_FILE"; then
GIT_EXIT=0
else
GIT_EXIT=${PIPESTATUS[0]}
fi
else
sudo -u "$REPO_OWNER" git pull origin "$GIT_BRANCH" >> "$PRIMARY_LOG_FILE" 2>&1
GIT_EXIT=$?
fi
# Check if git pull was successful
if [ $GIT_EXIT -ne 0 ]; then
output "Git pull failed" "error"
output "Current remote URL: $(git remote get-url origin)" "error"
if [ -n "$DEPLOY_LOG_ID" ]; then
ERROR_MSG="Git pull failed: $(git remote get-url origin)"
update_deploy_status "failed" "$ERROR_MSG"
fi
exit 1
fi
# ============================================================================
# UPDATE GIT COMMIT INFO IN DATABASE (OPTIONAL)
# ============================================================================
# If DeployLog model exists, update it with current commit hash and branch
# ============================================================================
if [ -n "$DEPLOY_LOG_ID" ]; then
# Determine Laravel directory (same logic as update_deploy_status)
LARAVEL_DIR=""
if [ -d "$REPO_PATH_PRODUCTION" ] && [ -f "$REPO_PATH_PRODUCTION/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_PRODUCTION"
elif [ -d "$REPO_PATH_LOCAL" ] && [ -f "$REPO_PATH_LOCAL/artisan" ]; then
LARAVEL_DIR="$REPO_PATH_LOCAL"
else
LARAVEL_DIR="${REPO_PATH:-$REPO_PATH_LOCAL}"
fi
# Ensure we're in the correct directory
cd "$LARAVEL_DIR" 2>/dev/null || cd "$REPO_PATH" 2>/dev/null || true
# Get current commit hash and branch
GIT_COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "")
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "$GIT_BRANCH")
if [ -n "$GIT_COMMIT" ]; then
output "Updating git commit info in database..." "info"
php artisan tinker --execute="
\$log = \App\Models\DeployLog::find('$DEPLOY_LOG_ID');
if (\$log) {
\$log->git_commit = '$GIT_COMMIT';
\$log->git_branch = '$GIT_BRANCH';
\$log->save();
echo 'Git info updated';
} else {
echo 'Deploy log not found';
}
" 2>&1 || output "Failed to update git commit info" "warning"
fi
fi
output "Code pulled successfully" "success"
print_separator
# ============================================================================
# CLEAR AND OPTIMIZE LARAVEL CACHE
# ============================================================================
# Clear all caches and optimize for production
# ============================================================================
output "Clearing Laravel cache..." "info"
if [ "$INTERACTIVE" = true ]; then
php artisan optimize:clear 2>&1 | tee -a "$PRIMARY_LOG_FILE"
else
php artisan optimize:clear >> "$PRIMARY_LOG_FILE" 2>&1
fi
output "Optimizing Laravel..." "info"
if [ "$INTERACTIVE" = true ]; then
php artisan optimize 2>&1 | tee -a "$PRIMARY_LOG_FILE"
else
php artisan optimize >> "$PRIMARY_LOG_FILE" 2>&1
fi
output "Cache cleared and optimized" "success"
print_separator
# ============================================================================
# RESTART WEB SERVER
# ============================================================================
# Restart Apache/Nginx to ensure all changes are loaded
# ============================================================================
output "Restarting Apache..." "info"
# Check if we can use sudo without password
APACHE_EXIT=0
if command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
# Sudo available and passwordless - use it
if [ "$INTERACTIVE" = true ]; then
if sudo systemctl restart apache2 2>&1 | tee -a "$PRIMARY_LOG_FILE"; then
APACHE_EXIT=0
else
APACHE_EXIT=${PIPESTATUS[0]}
fi
else
sudo systemctl restart apache2 >> "$PRIMARY_LOG_FILE" 2>&1
APACHE_EXIT=$?
fi
else
# Sudo not available or requires password - try alternative methods
output "Sudo not available for Apache restart, trying alternative methods..." "warning"
# Try systemctl directly (if running as root)
if systemctl restart apache2 >> "$PRIMARY_LOG_FILE" 2>&1; then
APACHE_EXIT=0
output "Apache restarted using systemctl" "success"
# Try service command
elif command -v service >/dev/null 2>&1 && service apache2 restart >> "$PRIMARY_LOG_FILE" 2>&1; then
APACHE_EXIT=0
output "Apache restarted using service command" "success"
# Try init.d script
elif [ -f /etc/init.d/apache2 ] && /etc/init.d/apache2 restart >> "$PRIMARY_LOG_FILE" 2>&1; then
APACHE_EXIT=0
output "Apache restarted using init.d script" "success"
else
APACHE_EXIT=1
output "Could not restart Apache automatically - please restart manually" "warning"
output "Deployment completed but Apache restart is required" "warning"
fi
fi
# Don't fail deployment if Apache restart fails - it's not critical
if [ $APACHE_EXIT -ne 0 ]; then
output "Note: Apache restart failed but deployment was successful" "warning"
# Continue - don't exit
fi
output "Apache restarted successfully" "success"
print_separator
# ============================================================================
# PRINT SUCCESS FOOTER
# ============================================================================
if [ "$USE_COLORS" = true ]; then
echo ""
echo -e "${BOLD}${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${GREEN}║${NC} ${WHITE}${SUCCESS} Deployment completed successfully!${NC} ${BOLD}${GREEN}║${NC}"
echo -e "${BOLD}${GREEN}║${NC} ${CYAN} Completed at: $(date '+%Y-%m-%d %H:%M:%S')${NC} ${BOLD}${GREEN}║${NC}"
echo -e "${BOLD}${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
else
echo "========================================="
echo "Deployment completed successfully!"
echo "Completed at: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================="
fi
# ============================================================================
# UPDATE DEPLOYMENT STATUS IN DATABASE (SUCCESS)
# ============================================================================
# Mark deployment as successful in the database
# ============================================================================
if [ -n "$DEPLOY_LOG_ID" ]; then
# Get deployment output from log file
DEPLOY_OUTPUT=$(tail -100 "$PRIMARY_LOG_FILE" 2>/dev/null | head -50 || echo "Deployment completed successfully")
# Update status - ensure this runs even if previous steps had issues
output "Updating deployment status in database..." "info"
# Use the update function (has built-in error handling and fallback)
update_deploy_status "success" "$DEPLOY_OUTPUT"
output "Deployment status updated to success" "success"
fi
# Clear the trap since we're exiting successfully
trap - EXIT
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment