mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 15:04:14 +00:00
feat: add staging deployment workflow and script
- Add GitHub Actions workflow for automatic staging deployment on develop branch - Add manual deploy script with --skip-build and --skip-migrations options - Update CLAUDE.md with deployment documentation - Mark staging-deployment TODO as complete Deployment target: pl.c2sgmbh.de (37.24.237.181) Triggers: push to develop, manual workflow_dispatch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
05fba7f1d7
commit
e3c7d92121
4 changed files with 395 additions and 8 deletions
189
.github/workflows/deploy-staging.yml
vendored
Normal file
189
.github/workflows/deploy-staging.yml
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
name: Deploy to Staging
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [develop]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
skip_tests:
|
||||
description: 'Skip tests before deployment'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: staging-deployment
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
NODE_VERSION: '20'
|
||||
PNPM_VERSION: '9'
|
||||
STAGING_HOST: '37.24.237.181'
|
||||
STAGING_USER: 'payload'
|
||||
STAGING_PATH: '/home/payload/payload-cms'
|
||||
|
||||
jobs:
|
||||
# ===========================================================================
|
||||
# Pre-deployment checks (optional, can be skipped via workflow_dispatch)
|
||||
# ===========================================================================
|
||||
pre-checks:
|
||||
name: Pre-deployment Checks
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.inputs.skip_tests != 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run ESLint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: pnpm test:unit
|
||||
env:
|
||||
CSRF_SECRET: test-csrf-secret
|
||||
PAYLOAD_SECRET: test-payload-secret
|
||||
PAYLOAD_PUBLIC_SERVER_URL: https://test.example.com
|
||||
NEXT_PUBLIC_SERVER_URL: https://test.example.com
|
||||
EMAIL_DELIVERY_DISABLED: 'true'
|
||||
|
||||
# ===========================================================================
|
||||
# Deploy to Staging Server
|
||||
# ===========================================================================
|
||||
deploy:
|
||||
name: Deploy to Staging
|
||||
runs-on: ubuntu-latest
|
||||
needs: [pre-checks]
|
||||
if: always() && (needs.pre-checks.result == 'success' || needs.pre-checks.result == 'skipped')
|
||||
environment:
|
||||
name: staging
|
||||
url: https://pl.c2sgmbh.de
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.STAGING_SSH_KEY }}" > ~/.ssh/staging_key
|
||||
chmod 600 ~/.ssh/staging_key
|
||||
cat >> ~/.ssh/config << EOF
|
||||
Host staging
|
||||
HostName ${{ env.STAGING_HOST }}
|
||||
User ${{ env.STAGING_USER }}
|
||||
IdentityFile ~/.ssh/staging_key
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
EOF
|
||||
|
||||
- name: Deploy to Staging
|
||||
run: |
|
||||
ssh staging << 'ENDSSH'
|
||||
set -e
|
||||
|
||||
echo "=== Staging Deployment Started ==="
|
||||
echo "Time: $(date)"
|
||||
echo "Branch: ${{ github.ref_name }}"
|
||||
echo "Commit: ${{ github.sha }}"
|
||||
|
||||
cd ${{ env.STAGING_PATH }}
|
||||
|
||||
# Stash any local changes
|
||||
git stash --include-untracked || true
|
||||
|
||||
# Fetch and checkout
|
||||
echo "=== Fetching latest code ==="
|
||||
git fetch origin develop
|
||||
git checkout develop
|
||||
git reset --hard origin/develop
|
||||
|
||||
# Install dependencies
|
||||
echo "=== Installing dependencies ==="
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# Run migrations if any
|
||||
echo "=== Running migrations ==="
|
||||
pnpm payload migrate || echo "No migrations to run"
|
||||
|
||||
# Build application
|
||||
echo "=== Building application ==="
|
||||
NODE_OPTIONS="--max-old-space-size=2048" pnpm build
|
||||
|
||||
# Restart services
|
||||
echo "=== Restarting services ==="
|
||||
pm2 restart payload --update-env || pm2 start ecosystem.config.cjs --only payload
|
||||
pm2 restart queue-worker --update-env || pm2 start ecosystem.config.cjs --only queue-worker
|
||||
|
||||
# Wait for service to be healthy
|
||||
echo "=== Waiting for service to start ==="
|
||||
sleep 10
|
||||
|
||||
# Health check
|
||||
echo "=== Running health check ==="
|
||||
curl -sf http://localhost:3000/api/health || curl -sf http://localhost:3000/admin || echo "Health check endpoint not available"
|
||||
|
||||
# Show status
|
||||
echo "=== Deployment Complete ==="
|
||||
pm2 status
|
||||
echo "Time: $(date)"
|
||||
ENDSSH
|
||||
|
||||
- name: Verify Deployment
|
||||
run: |
|
||||
echo "Verifying staging deployment..."
|
||||
sleep 5
|
||||
|
||||
# Check if staging is responding
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://pl.c2sgmbh.de/admin || echo "000")
|
||||
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 400 ]; then
|
||||
echo "Staging is responding with HTTP $HTTP_STATUS"
|
||||
else
|
||||
echo "Warning: Staging returned HTTP $HTTP_STATUS"
|
||||
fi
|
||||
|
||||
- name: Create deployment summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Staging Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Environment | Staging |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| URL | https://pl.c2sgmbh.de |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Branch | ${{ github.ref_name }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Commit | \`${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Triggered by | ${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Time | $(date -u '+%Y-%m-%d %H:%M:%S UTC') |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# ===========================================================================
|
||||
# Notify on failure
|
||||
# ===========================================================================
|
||||
notify-failure:
|
||||
name: Notify on Failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy]
|
||||
if: failure()
|
||||
steps:
|
||||
- name: Create failure summary
|
||||
run: |
|
||||
echo "## Staging Deployment Failed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "The deployment to staging failed. Please check the logs above for details." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Actor:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
|
||||
33
CLAUDE.md
33
CLAUDE.md
|
|
@ -831,6 +831,39 @@ pnpm build # Production Build
|
|||
- **CodeQL**: Static Analysis (SAST)
|
||||
- **Security Tests**: Unit & Integration Tests für Security-Module
|
||||
|
||||
### deploy-staging.yml (Staging Deployment)
|
||||
Automatisches Deployment auf Staging-Server bei Push auf `develop`:
|
||||
|
||||
| Trigger | Aktion |
|
||||
|---------|--------|
|
||||
| Push auf `develop` | Automatisches Deployment |
|
||||
| `workflow_dispatch` | Manuelles Deployment (optional: skip_tests) |
|
||||
|
||||
**Deployment-Ziel:**
|
||||
- **URL:** https://pl.c2sgmbh.de
|
||||
- **Server:** 37.24.237.181 (sv-payload)
|
||||
|
||||
**Ablauf:**
|
||||
1. Pre-deployment Checks (Lint, Tests)
|
||||
2. SSH-Verbindung zum Staging-Server
|
||||
3. Git Pull + Dependencies installieren
|
||||
4. Migrations ausführen
|
||||
5. Build + PM2 Restart
|
||||
6. Health Check
|
||||
|
||||
**Manuelles Staging-Deployment:**
|
||||
```bash
|
||||
# Auf dem Staging-Server (pl.c2sgmbh.de)
|
||||
./scripts/deploy-staging.sh
|
||||
|
||||
# Mit Optionen
|
||||
./scripts/deploy-staging.sh --skip-build # Nur Code-Update
|
||||
./scripts/deploy-staging.sh --skip-migrations # Ohne Migrationen
|
||||
```
|
||||
|
||||
**GitHub Secret erforderlich:**
|
||||
- `STAGING_SSH_KEY` - SSH Private Key für `payload@37.24.237.181`
|
||||
|
||||
## Dokumentation
|
||||
|
||||
- `CLAUDE.md` - Diese Datei (Projekt-Übersicht)
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
|--------|------|---------|
|
||||
| [ ] | Media-Backup zu S3/MinIO | Backup |
|
||||
| [ ] | CDN-Integration (Cloudflare) | Caching |
|
||||
| [ ] | CI/CD Pipeline erweitern (Lint/Test/Build) | DevOps |
|
||||
| [ ] | Staging-Deployment | DevOps |
|
||||
| [x] | CI/CD Pipeline erweitern (Lint/Test/Build) | DevOps |
|
||||
| [x] | Staging-Deployment | DevOps |
|
||||
| [ ] | Memory-Problem lösen (Swap) | Infrastruktur |
|
||||
| [ ] | PM2 Cluster Mode testen | Infrastruktur |
|
||||
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
| [ ] | Email-Log Cleanup Cron | Data Retention |
|
||||
| [ ] | Dashboard-Widget für Email-Status | Admin UX |
|
||||
| [ ] | TypeScript Strict Mode | Tech Debt |
|
||||
| [ ] | E2E Tests für kritische Flows | Testing |
|
||||
| [x] | E2E Tests für kritische Flows | Testing |
|
||||
|
||||
### Dokumentation
|
||||
| Status | Task |
|
||||
|
|
@ -117,11 +117,11 @@
|
|||
|
||||
## Testing & CI/CD
|
||||
|
||||
- [ ] **CI/CD Pipeline**
|
||||
- [ ] Automatisches Lint/Test/Build Workflow
|
||||
- [ ] Staging-Deployment
|
||||
- [x] **CI/CD Pipeline** *(erledigt: `.github/workflows/ci.yml`)*
|
||||
- [x] Automatisches Lint/Test/Build Workflow
|
||||
- [x] Staging-Deployment *(erledigt: `.github/workflows/deploy-staging.yml`)*
|
||||
|
||||
- [ ] **E2E Tests für kritische Flows**
|
||||
- [x] **E2E Tests für kritische Flows** *(erledigt: `tests/e2e/`)*
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
## Technische Schulden
|
||||
|
||||
- [ ] TypeScript Strict Mode aktivieren
|
||||
- [ ] E2E Tests für kritische Flows
|
||||
- [x] E2E Tests für kritische Flows
|
||||
- [ ] Code-Review für Security-relevante Bereiche
|
||||
- [ ] Performance-Audit der Datenbank-Queries
|
||||
|
||||
|
|
|
|||
165
scripts/deploy-staging.sh
Executable file
165
scripts/deploy-staging.sh
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# Staging Deployment Script
|
||||
# =============================================================================
|
||||
# Usage: ./scripts/deploy-staging.sh [--skip-build] [--skip-migrations]
|
||||
#
|
||||
# This script deploys the current develop branch to the staging environment.
|
||||
# Run this on the staging server (pl.c2sgmbh.de) or via SSH.
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="/home/payload/payload-cms"
|
||||
BRANCH="${DEPLOY_BRANCH:-develop}" # Use env var or default to develop
|
||||
LOG_FILE="/home/payload/logs/deploy-staging.log"
|
||||
|
||||
# Parse arguments
|
||||
SKIP_BUILD=false
|
||||
SKIP_MIGRATIONS=false
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--skip-build)
|
||||
SKIP_BUILD=true
|
||||
shift
|
||||
;;
|
||||
--skip-migrations)
|
||||
SKIP_MIGRATIONS=true
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Functions
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure log directory exists
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
echo "=============================================="
|
||||
echo " Staging Deployment Script"
|
||||
echo " Environment: pl.c2sgmbh.de"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
log "Starting staging deployment..."
|
||||
log "Branch: $BRANCH"
|
||||
log "Skip build: $SKIP_BUILD"
|
||||
log "Skip migrations: $SKIP_MIGRATIONS"
|
||||
|
||||
# Change to project directory
|
||||
cd "$PROJECT_DIR" || error "Could not change to project directory: $PROJECT_DIR"
|
||||
log "Working directory: $(pwd)"
|
||||
|
||||
# Check current branch and stash changes
|
||||
log "Checking git status..."
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then
|
||||
warn "Currently on branch '$CURRENT_BRANCH', switching to '$BRANCH'"
|
||||
fi
|
||||
|
||||
# Stash any local changes
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
warn "Stashing local changes..."
|
||||
git stash --include-untracked
|
||||
fi
|
||||
|
||||
# Fetch and reset to origin
|
||||
log "Fetching latest code from origin/$BRANCH..."
|
||||
git fetch origin "$BRANCH"
|
||||
git checkout "$BRANCH"
|
||||
git reset --hard "origin/$BRANCH"
|
||||
success "Code updated to latest origin/$BRANCH"
|
||||
|
||||
# Show current commit
|
||||
COMMIT_SHA=$(git rev-parse --short HEAD)
|
||||
COMMIT_MSG=$(git log -1 --pretty=%s)
|
||||
log "Current commit: $COMMIT_SHA - $COMMIT_MSG"
|
||||
|
||||
# Install dependencies
|
||||
log "Installing dependencies..."
|
||||
pnpm install --frozen-lockfile
|
||||
success "Dependencies installed"
|
||||
|
||||
# Run migrations (unless skipped)
|
||||
if [ "$SKIP_MIGRATIONS" = false ]; then
|
||||
log "Running database migrations..."
|
||||
pnpm payload migrate || warn "No migrations to run or migration failed"
|
||||
else
|
||||
warn "Skipping migrations (--skip-migrations flag)"
|
||||
fi
|
||||
|
||||
# Build (unless skipped)
|
||||
if [ "$SKIP_BUILD" = false ]; then
|
||||
log "Building application..."
|
||||
|
||||
# Stop PM2 to free memory for build
|
||||
pm2 stop payload 2>/dev/null || true
|
||||
pm2 stop queue-worker 2>/dev/null || true
|
||||
|
||||
# Build with memory limit
|
||||
NODE_OPTIONS="--max-old-space-size=2048" pnpm build
|
||||
success "Build completed"
|
||||
else
|
||||
warn "Skipping build (--skip-build flag)"
|
||||
fi
|
||||
|
||||
# Restart services
|
||||
log "Restarting PM2 services..."
|
||||
pm2 restart payload --update-env 2>/dev/null || pm2 start ecosystem.config.cjs --only payload
|
||||
pm2 restart queue-worker --update-env 2>/dev/null || pm2 start ecosystem.config.cjs --only queue-worker
|
||||
success "Services restarted"
|
||||
|
||||
# Wait for service to start
|
||||
log "Waiting for service to start..."
|
||||
sleep 5
|
||||
|
||||
# Health check
|
||||
log "Running health check..."
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/admin 2>/dev/null || echo "000")
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 400 ]; then
|
||||
success "Health check passed (HTTP $HTTP_STATUS)"
|
||||
else
|
||||
warn "Health check returned HTTP $HTTP_STATUS"
|
||||
fi
|
||||
|
||||
# Show PM2 status
|
||||
echo ""
|
||||
log "PM2 Status:"
|
||||
pm2 status
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo -e "${GREEN} Staging Deployment Complete!${NC}"
|
||||
echo "=============================================="
|
||||
echo " URL: https://pl.c2sgmbh.de"
|
||||
echo " Admin: https://pl.c2sgmbh.de/admin"
|
||||
echo " Commit: $COMMIT_SHA"
|
||||
echo " Time: $(date)"
|
||||
echo "=============================================="
|
||||
|
||||
log "Deployment finished successfully"
|
||||
Loading…
Reference in a new issue