All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / deploy-staging (push) Successful in 36s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 0s
335 lines
13 KiB
YAML
335 lines
13 KiB
YAML
name: Build, Push and Deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [ main, master ]
|
|
workflow_dispatch:
|
|
inputs:
|
|
deploy_to_production:
|
|
description: 'Deploy to Production?'
|
|
required: true
|
|
default: 'no'
|
|
type: choice
|
|
options:
|
|
- 'yes'
|
|
- 'no'
|
|
image_tag:
|
|
description: 'Image tag to deploy (optional, uses latest if empty)'
|
|
required: false
|
|
type: string
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
image_tag: ${{ steps.vars.outputs.image_tag }}
|
|
image_sha: ${{ steps.vars.outputs.image_sha }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set variables
|
|
id: vars
|
|
run: |
|
|
echo "image_tag=$(echo ${{ gitea.sha }} | cut -c1-8)" >> $GITHUB_OUTPUT
|
|
echo "image_sha=${{ gitea.sha }}" >> $GITHUB_OUTPUT
|
|
echo "build_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Login to Gitea Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: kode.sadateknologi.site
|
|
username: ${{ gitea.repository_owner }}
|
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
|
|
- name: Build and push image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: .
|
|
push: true
|
|
tags: |
|
|
kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:latest
|
|
kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:${{ gitea.sha }}
|
|
kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:${{ steps.vars.outputs.image_tag }}
|
|
labels: |
|
|
org.opencontainers.image.created=${{ steps.vars.outputs.build_time }}
|
|
org.opencontainers.image.revision=${{ gitea.sha }}
|
|
org.opencontainers.image.version=${{ steps.vars.outputs.image_tag }}
|
|
|
|
deploy-staging:
|
|
runs-on: ubuntu-latest
|
|
needs: build-and-push
|
|
if: gitea.event_name == 'push' && (gitea.ref == 'refs/heads/main' || gitea.ref == 'refs/heads/master')
|
|
steps:
|
|
- name: Deploy to Staging (Local)
|
|
run: |
|
|
echo "🚀 Starting staging deployment on local server..."
|
|
|
|
# Pull the latest image
|
|
echo "Pulling image: kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:latest"
|
|
docker pull kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:latest
|
|
|
|
# Stop and remove existing container
|
|
echo "Stopping existing container..."
|
|
docker stop kulakpos_web_staging || true
|
|
docker rm kulakpos_web_staging || true
|
|
|
|
# Clean up old images (keep last 5)
|
|
docker image prune -f
|
|
|
|
# Run new container
|
|
echo "Starting new container..."
|
|
docker run -d \
|
|
--restart unless-stopped \
|
|
--health-cmd="curl -f http://127.0.0.1:8000 || exit 1" \
|
|
--health-interval=30s \
|
|
--health-timeout=10s \
|
|
--health-retries=3 \
|
|
--name kulakpos_web_staging \
|
|
--network="host" \
|
|
-v /opt/kulakpos/staging/.env:/var/www/html/.env \
|
|
-p 8000:8000 \
|
|
-e MODE=staging \
|
|
-e APP_ENV=staging \
|
|
kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:latest
|
|
|
|
# Check if container is running
|
|
sleep 5
|
|
if docker ps | grep kulakpos_web_staging; then
|
|
echo "✅ Staging deployment successful!"
|
|
docker logs --tail 20 kulakpos_web_staging
|
|
else
|
|
echo "❌ Staging deployment failed! Check logs:"
|
|
docker logs kulakpos_web_staging || echo "No logs available"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Verify Staging Deployment
|
|
run: |
|
|
curl -f http://localhost:8000/health || curl -f http://localhost:8000 || echo 'Health check endpoint not found, but service may be running'
|
|
echo "✅ Staging deployment verified!"
|
|
|
|
deploy-production:
|
|
runs-on: ubuntu-latest
|
|
needs: build-and-push
|
|
if: gitea.event_name == 'workflow_dispatch' && gitea.event.inputs.deploy_to_production == 'yes'
|
|
environment: production
|
|
steps:
|
|
# ============================================
|
|
# STEP 1: Setup SSH Key and Configuration
|
|
# ============================================
|
|
- name: Setup SSH Key and Config
|
|
run: |
|
|
echo "🔑 Setting up SSH authentication..."
|
|
|
|
mkdir -p ~/.ssh
|
|
chmod 700 ~/.ssh
|
|
|
|
# Decode the base64-encoded SSH key
|
|
echo "${{ secrets.PRODUCTION_SSH_KEY_BASE64 }}" | base64 -d > ~/.ssh/id_rsa
|
|
chmod 600 ~/.ssh/id_rsa
|
|
|
|
# Add host to known_hosts
|
|
ssh-keyscan -H ${{ secrets.PRODUCTION_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
|
|
|
# Create SSH config
|
|
cat > ~/.ssh/config << EOF
|
|
Host kulakpos_prod
|
|
HostName ${{ secrets.PRODUCTION_HOST }}
|
|
User ${{ secrets.PRODUCTION_USER }}
|
|
IdentityFile ~/.ssh/id_rsa
|
|
StrictHostKeyChecking accept-new
|
|
EOF
|
|
|
|
chmod 600 ~/.ssh/config
|
|
|
|
echo "✅ SSH configuration complete"
|
|
|
|
- name: Debug SSH Configuration
|
|
run: |
|
|
echo "Current user: $(whoami)"
|
|
echo "Home directory: $HOME"
|
|
echo "SSH config location: $HOME/.ssh/config"
|
|
echo "SSH config content:"
|
|
cat $HOME/.ssh/config || echo "No SSH config found"
|
|
echo ""
|
|
echo "SSH key permissions:"
|
|
ls -la ~/.ssh/
|
|
echo ""
|
|
echo "Testing SSH connection:"
|
|
ssh -v kulakpos_prod echo "Connection successful" || echo "Connection failed"
|
|
|
|
- name: Determine image tag for production
|
|
id: prod_image
|
|
run: |
|
|
if [ -n "${{ gitea.event.inputs.image_tag }}" ]; then
|
|
echo "image_tag=${{ gitea.event.inputs.image_tag }}" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "image_tag=latest" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Deploy to Production Server
|
|
run: |
|
|
ssh kulakpos_prod << 'EOF'
|
|
IMAGE_TAG="${{ steps.prod_image.outputs.image_tag }}"
|
|
|
|
echo "🚀 Starting production deployment with image tag: $IMAGE_TAG"
|
|
|
|
# Show current deployment
|
|
echo "Current production container:"
|
|
docker ps --filter name=kulakpos_web_prod
|
|
|
|
# Create backup of current deployment info
|
|
mkdir -p /opt/kulakpos/backups
|
|
docker inspect kulakpos_web_prod > /opt/kulakpos/backups/kulakpos_backup_$(date +%Y%m%d_%H%M%S).json || true
|
|
|
|
# Backup .env file
|
|
if [ -f "/opt/kulakpos/.env" ]; then
|
|
cp /opt/kulakpos/.env /opt/kulakpos/backups/.env_backup_$(date +%Y%m%d_%H%M%S)
|
|
fi
|
|
|
|
# Pull the specified image
|
|
echo "Pulling image: kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:$IMAGE_TAG"
|
|
docker pull kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:$IMAGE_TAG
|
|
|
|
# Stop and remove existing container
|
|
echo "Stopping existing container..."
|
|
docker stop kulakpos_web_prod || true
|
|
docker rm kulakpos_web_prod || true
|
|
|
|
# Run new container (using PHP's built-in server on port 8000 like staging)
|
|
echo "Starting new production container on port 8000..."
|
|
docker run -d \
|
|
--restart unless-stopped \
|
|
--health-cmd="curl -f http://127.0.0.1:8000 || exit 1" \
|
|
--health-interval=30s \
|
|
--health-timeout=10s \
|
|
--health-start-period=40s \
|
|
--health-retries=3 \
|
|
--name kulakpos_web_prod \
|
|
--network="host" \
|
|
-v /opt/kulakpos/.env:/var/www/html/.env \
|
|
-e MODE=production \
|
|
-e APP_ENV=production \
|
|
-e APP_DEBUG=false \
|
|
-e AUTO_MIGRATE=false \
|
|
kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:$IMAGE_TAG
|
|
|
|
# Wait for container to be ready
|
|
echo "Waiting for container to be ready..."
|
|
sleep 10
|
|
|
|
# Check if container is running
|
|
if docker ps | grep kulakpos_web_prod; then
|
|
echo "✅ Production deployment successful!"
|
|
echo ""
|
|
echo "Container status:"
|
|
docker ps --filter name=kulakpos_web_prod --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
|
echo ""
|
|
echo "Container logs (last 20 lines):"
|
|
docker logs --tail 20 kulakpos_web_prod
|
|
|
|
# Test the application locally
|
|
echo ""
|
|
echo "Testing application locally..."
|
|
curl -f http://127.0.0.1:8000 > /dev/null 2>&1 && echo "✅ Application is responding on port 8000" || echo "⚠️ Application not responding on port 8000"
|
|
else
|
|
echo "❌ Production deployment failed! Checking logs:"
|
|
docker logs kulakpos_web_prod || echo "No logs available"
|
|
|
|
# Attempt rollback to previous version if available
|
|
echo ""
|
|
echo "Attempting rollback to previous version..."
|
|
PREVIOUS_BACKUP=$(ls -t /opt/kulakpos/backups/kulakpos_backup_*.json 2>/dev/null | head -1)
|
|
if [ -f "$PREVIOUS_BACKUP" ]; then
|
|
PREVIOUS_IMAGE=$(jq -r '.[0].Config.Image' $PREVIOUS_BACKUP 2>/dev/null || echo "")
|
|
if [ -n "$PREVIOUS_IMAGE" ] && [ "$PREVIOUS_IMAGE" != "kode.sadateknologi.site/${{ gitea.repository_owner }}/kulakpos_web:$IMAGE_TAG" ]; then
|
|
echo "Rolling back to: $PREVIOUS_IMAGE"
|
|
docker run -d \
|
|
--restart unless-stopped \
|
|
--health-cmd="curl -f http://127.0.0.1:8000 || exit 1" \
|
|
--health-interval=30s \
|
|
--health-timeout=10s \
|
|
--health-start-period=40s \
|
|
--health-retries=3 \
|
|
--name kulakpos_web_prod \
|
|
--network="host" \
|
|
-v /opt/kulakpos/.env:/var/www/html/.env \
|
|
-e MODE=production \
|
|
-e APP_ENV=production \
|
|
-e APP_DEBUG=false \
|
|
-e AUTO_MIGRATE=false \
|
|
$PREVIOUS_IMAGE
|
|
echo "Rollback completed"
|
|
fi
|
|
fi
|
|
exit 1
|
|
fi
|
|
EOF
|
|
|
|
- name: Verify Production Deployment
|
|
run: |
|
|
ssh kulakpos_prod << 'EOF'
|
|
echo "🔍 Verifying production deployment..."
|
|
|
|
# Check container status (fixed - removed -q flag)
|
|
if docker ps --filter "name=kulakpos_web_prod" --format "{{.Status}}" | grep "Up" > /dev/null 2>&1; then
|
|
echo "✅ Container is running"
|
|
else
|
|
echo "❌ Container is not running"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if port 8000 is listening (with timeout)
|
|
if timeout 5 ss -tlnp 2>/dev/null | grep ":8000" 2>/dev/null || netstat -tlnp 2>/dev/null | grep ":8000" 2>/dev/null; then
|
|
echo "✅ Application is listening on port 8000"
|
|
else
|
|
echo "⚠️ Port 8000 not listening, checking container process..."
|
|
docker exec kulakpos_web_prod ps aux | grep php || echo "No PHP process found"
|
|
exit 1
|
|
fi
|
|
|
|
# Test application response (with timeout and retry)
|
|
echo "Testing application response..."
|
|
for i in 1 2 3 4 5; do
|
|
if curl -f -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000 | grep "200\|302"; then
|
|
echo "✅ Application is responding"
|
|
break
|
|
elif [ $i -eq 5 ]; then
|
|
echo "❌ Application not responding after 5 attempts"
|
|
docker logs --tail 20 kulakpos_web_prod
|
|
exit 1
|
|
fi
|
|
echo "Attempt $i/5 failed, waiting..."
|
|
sleep 2
|
|
done
|
|
|
|
echo "🎉 Production deployment verification complete!"
|
|
EOF
|
|
|
|
- name: Send Deployment Notification
|
|
if: always()
|
|
run: |
|
|
STATUS="${{ job.status }}"
|
|
if [ "$STATUS" = "success" ]; then
|
|
echo "✅ Production deployment completed successfully!"
|
|
echo "🌐 Application is now running at: https://prod-test.kulakpos.id"
|
|
echo "🐳 Container name: kulakpos_web_prod"
|
|
echo "🔌 Port: 8000 (internal) -> 80/443 (Nginx proxy)"
|
|
else
|
|
echo "❌ Production deployment failed!"
|
|
echo "Please check the logs above for details"
|
|
exit 1
|
|
fi
|
|
|
|
cleanup:
|
|
runs-on: ubuntu-latest
|
|
needs: [deploy-staging, deploy-production]
|
|
if: always()
|
|
steps:
|
|
- name: Cleanup old Docker images on runners
|
|
run: |
|
|
docker system prune -f --filter "until=24h" |