| name | Deployment Workflow |
| description | Staging and production deployment using Laravel Deployer with health checks, cache management, and rollback support (HUMAN-ONLY execution) |
| allowed-tools | Read, Grep, Glob |
Deployment Workflow
⚠️ CRITICAL: This skill documents deployment processes for understanding and troubleshooting only.
Claude Code CANNOT execute deployment commands - these are HUMAN-ONLY operations that can cause outages or data loss if misused.
When to Use
- Understanding deployment architecture
- Troubleshooting deployment failures
- Reviewing deployment logs
- Planning deployment strategy
- Documenting deployment procedures
NOT for: Actually executing deployments (human-only)
Scripts Overview
Production Deployment
Script: ./scripts/prod.sh
Purpose: Manage production environment
Restrictions: HUMAN-ONLY (never run via Claude)
# Commands available (HUMAN executes these)
./scripts/prod.sh deploy # Full deployment
./scripts/prod.sh rollback # Rollback to previous release
./scripts/prod.sh ssh # SSH to production server
./scripts/prod.sh logs # View deployment logs
./scripts/prod.sh health # Check production health
Staging Deployment
Script: ./scripts/staging.sh
Purpose: Manage staging environment
Restrictions: HUMAN-ONLY (can run destructive commands)
# Commands available (HUMAN executes these)
./scripts/staging.sh deploy # Full deployment
./scripts/staging.sh fresh # Reset database (DESTRUCTIVE)
./scripts/staging.sh seed # Seed database
./scripts/staging.sh ssh # SSH to staging server
./scripts/staging.sh health # Check staging health
Deployment Architecture
DRY Library System
Modular libraries eliminate code duplication:
Configuration (scripts/lib/config.sh):
- Single source of truth for all environment configs
- Production:
$PROD_HOST,$PROD_IP,$PROD_USER,$PROD_PATH - Staging:
$STAGING_HOST,$STAGING_USER,$STAGING_PATH - Helper functions:
get_remote_connection(),get_health_url()
Deployment (scripts/lib/deployment.sh):
- Parameterized functions work for both prod AND staging
health_check(env)- Unified health checkingdeploy_to_environment(env)- Complete workflowrollback_deployment(env)- Safe rollbacksverify_deployment_environment(env)- Pre-deployment validation
Benefits: ✅ ~250 lines of duplicate code eliminated ✅ Fix once, works everywhere ✅ SOLID/DRY/KISS principles
Deployment Flow (12 Steps)
Complete Deployment Sequence
1. deploy:update_code # Git pull latest code
2. deploy:shared # Link shared files (.env, storage)
3. deploy:composer:clear-cache # Clear Composer cache (verbose logging)
4. deploy:vendors # Install PHP dependencies (--prefer-source)
5. deploy:verify:packages # Verify critical packages exist
6. artisan:storage:link # Link storage directory
7. artisan:view:cache # Cache Blade views
8. artisan:config:cache # Cache configuration
9. artisan:route:cache # Cache routes
10. artisan:migrate # Run database migrations
11. deploy:npm_build # Build frontend assets
12. deploy:symlink # Switch to new release
Total Time: ~2-3 minutes per deployment
Enhanced Logging (October 2025)
Every deployment includes verbose output:
📦 Clearing Composer cache...
Cache directory: /home/deploy/.cache/composer
Files remaining in cache: 0
✅ Composer cache cleared
🎼 Installing PHP dependencies with Composer...
Using --prefer-source to force git clones for VCS packages
✅ Composer dependencies installed
🔍 Verifying critical package files...
✓ helpers.php exists (vendor/pcrcard/nova-medialibrary-bounding-box-field/src/helpers.php)
✓ nova-menus package exists
✅ Package verification passed
Cache Management
Automatic Composer Cache Clearing
Why: Prevents cache corruption for forked VCS packages When: Every deployment (staging + production) Impact: +5 seconds per deployment
Implementation (deploy.php):
task('deploy:composer:clear-cache', function () {
writeln('📦 Clearing Composer cache...');
$cacheDir = run('{{bin/composer}} config cache-dir 2>/dev/null || echo "~/.cache/composer"');
writeln(' Cache directory: ' . trim($cacheDir));
run('{{bin/composer}} clear-cache');
$files = run('find ' . trim($cacheDir) . ' -type f 2>/dev/null | wc -l || echo "0"');
writeln(' Files remaining in cache: ' . trim($files));
writeln('✅ Composer cache cleared');
});
Prefer Source Installation
Why: Forces git clones for VCS packages (ensures complete files) When: Every vendor installation Impact: +30 seconds per deployment
Implementation (deploy.php):
task('deploy:vendors', function () {
writeln('🎼 Installing PHP dependencies with Composer...');
writeln(' Using --prefer-source to force git clones for VCS packages');
run('cd {{release_path}} && {{bin/composer}} install --prefer-source --no-dev --optimize-autoloader --no-interaction');
writeln('✅ Composer dependencies installed');
});
Affected Packages:
pcrcard/nova-menuspcrcard/nova-medialibrary-bounding-box-field
Other Packages: Use fast Packagist dist archives (not affected)
Package Verification
Early Error Detection (October 2025)
New task catches missing files before autoload errors:
task('deploy:verify:packages', function () {
writeln('🔍 Verifying critical package files...');
// Check nova-medialibrary-bounding-box-field helpers.php
$helpersPath = '{{release_path}}/vendor/pcrcard/nova-medialibrary-bounding-box-field/src/helpers.php';
if (!test("[ -f $helpersPath ]")) {
throw new \Exception("Missing helpers.php in nova-medialibrary-bounding-box-field package!");
}
writeln(' ✓ helpers.php exists');
// Check nova-menus package
$menuPath = '{{release_path}}/vendor/pcrcard/nova-menus';
if (!test("[ -d $menuPath ]")) {
throw new \Exception("Missing nova-menus package!");
}
writeln(' ✓ nova-menus package exists');
writeln('✅ Package verification passed');
});
Benefits: ✅ Fails early with clear diagnostics ✅ Prevents cascading autoload errors ✅ Shows exactly what's missing ✅ +2 seconds deployment time (worth it)
Health Checks
Automated Health Verification
Function: health_check(env)
Purpose: Verify application is responding after deployment
Retry Logic: 3 attempts with 5-second delays
# Staging health check
health_check "staging"
# Checks: https://staging.pcrcard.com/health
# Production health check
health_check "production"
# Checks: https://pcrcard.com/health
Expected Response:
{
"status": "ok",
"services": {
"database": "connected",
"cache": "connected",
"storage": "writable"
}
}
On Failure: Deployment stops, rollback recommended
Rollback Support
Safe Recovery
Function: rollback_deployment(env)
Purpose: Revert to previous working release
Preserves: Database, uploaded files, .env configuration
# Rollback staging
./scripts/staging.sh rollback
# Rollback production (HUMAN-ONLY)
./scripts/prod.sh rollback
How It Works:
- Deployer keeps last 3 releases in
releases/directory current/symlink points to active release- Rollback changes symlink to previous release
- Takes ~5 seconds (no code download needed)
Rollback Flow:
releases/
20251028120000/ ← Previous (rollback target)
20251028140000/ ← Current (broken)
current/ → 20251028140000 # Before rollback
current/ → 20251028120000 # After rollback
Performance Impact
Reliability vs Speed Trade-off
| Operation | Time | Benefit |
|---|---|---|
| Cache clear | +5s | Prevents corruption |
| Prefer source | +30s | Complete file integrity |
| Package verification | +2s | Early error detection |
| Total | +37s | 100% reliable deployments |
Trade-off: Acceptable 37-second overhead for zero manual intervention
Emergency Production Fix
Manual Cache Clearing Script
When to use: Production deployment fails with package errors
Script: ./scripts/immediate-production-fix.sh
#!/bin/bash
# Emergency production cache fix (HUMAN-ONLY)
echo "🚨 Emergency Production Cache Fix"
echo "=================================="
echo ""
# SSH to production and clear cache
ssh deploy@production.pcrcard.com << 'EOF'
echo "📦 Clearing Composer cache..."
composer clear-cache
echo "📊 Cache status..."
composer config cache-dir
ls -la $(composer config cache-dir)
echo "✅ Cache cleared"
EOF
echo ""
echo "📋 Next steps:"
echo "1. Retry deployment: ./scripts/prod.sh deploy"
echo "2. Verify packages: ssh deploy@production 'ls -la current/vendor/pcrcard/'"
echo "3. Check health: ./scripts/prod.sh health"
Common Issues
Issue 1: Missing Package Files
Symptom: helpers.php not found autoload error
Cause: Composer cache corruption
Solution:
# 1. Clear cache on server
ssh deploy@staging "composer clear-cache"
# 2. Redeploy with --prefer-source
./scripts/staging.sh deploy
Issue 2: Deployment Timeout
Symptom: Deployment hangs at composer install
Cause: Network issues, large dependencies
Solution:
# Increase timeout in deploy.php
set('deploy_timeout', 600); // 10 minutes
# Or manually SSH and install
ssh deploy@staging
cd releases/20251028140000
composer install --prefer-source
Issue 3: Failed Health Check
Symptom: Health endpoint returns 500 error
Cause: Cache issues, permissions, database
Solution:
# SSH to server
./scripts/staging.sh ssh
# Check logs
tail -f storage/logs/laravel.log
# Clear application cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Fix permissions
chmod -R 775 storage bootstrap/cache
chown -R deploy:www-data storage bootstrap/cache
Documentation Links
- Deployment Guide:
docs/deployment/DEPLOYMENT-GUIDE.md - Deployment Checklist:
docs/deployment/DEPLOYMENT-CHECKLIST.md - Latest Fix:
docs/ci-cd/deployment/fixes/2025-10-24-production-deployment-enhancement.md - CI/CD Hub:
docs/ci-cd/README.md - Laravel Deployer: https://deployer.org/docs/7.x/getting-started