feat: implement CI/CD workflows for Docker image build and deployment
Some checks failed
Deploy Production / deploy (push) Failing after 3m40s

This commit is contained in:
Frank John Begornia
2025-12-19 22:06:43 +08:00
parent da7c6e0cf6
commit 103101c5d9
5 changed files with 304 additions and 81 deletions

View File

@@ -0,0 +1,57 @@
name: Build and Push Docker Image
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Docker image tag (e.g., v1.0.0, latest)'
required: false
default: 'latest'
push_to_registry:
description: 'Push to registry?'
required: false
default: 'true'
jobs:
build-and-push:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY_URL }}/slipmatz-web:buildcache,mode=max

View File

@@ -0,0 +1,140 @@
name: Deploy Development
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
# 1⃣ Checkout code
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
cd /workspace/repo
git checkout $GITHUB_REF_NAME
# 2⃣ Build image
- name: Build Docker image
shell: sh
run: |
cd /workspace/repo
docker build -t slipmatz-web:dev .
docker save slipmatz-web:dev | gzip > slipmatz-web_dev.tar.gz
# 3⃣ Setup SSH
- name: Setup SSH
shell: sh
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
# 4⃣ Upload artifacts
- name: Upload image and compose
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
scp -i ~/.ssh/id_ed25519 \
/workspace/repo/slipmatz-web_dev.tar.gz \
/workspace/repo/docker-compose.yml \
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
# 5⃣ Deploy on server
- name: Deploy on server
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF'
set -e
DEPLOY_DIR="/var/www/apps/slipmatz-web_dev"
mkdir -p "$DEPLOY_DIR"
echo "📦 Loading image"
docker load < /tmp/slipmatz-web_dev.tar.gz
echo "🧹 Removing old slipmatz-web images"
docker images | grep slipmatz-web | grep -v "$(docker images slipmatz-web:dev -q)" | awk '{print $3}' | xargs -r docker rmi -f || true
echo "📄 Updating compose file"
cp /tmp/docker-compose.yml "$DEPLOY_DIR/"
cd "$DEPLOY_DIR"
echo "🔍 Checking .env file"
if [ ! -f .env ]; then
echo "❌ .env file not found at $DEPLOY_DIR/.env"
echo "Please create it first with required variables:"
echo " - NUXT_PUBLIC_*, STRIPE_SECRET_KEY"
exit 1
fi
echo "🔧 Fixing .env permissions"
sudo chown $USER:$USER .env
sudo chmod 600 .env
echo "🌐 Ensure networks"
docker network inspect traefik-public >/dev/null 2>&1 || \
docker network create traefik-public
docker network inspect crew-app-net >/dev/null 2>&1 || \
docker network create crew-app-net
echo "🚀 Starting containers (env vars from .env file)"
export COMPOSE_PROJECT_NAME=slipmatz-web-dev
docker compose down || true
docker compose up -d --remove-orphans
echo "🧪 Testing container health"
sleep 15
if ! docker ps | grep -q slipmatz-web; then
echo "❌ Container not running"
docker compose logs --tail=50
exit 1
fi
echo "🎉 Dev deployment complete!"
docker compose ps
echo "🧹 Cleanup"
rm -f /tmp/slipmatz-web_dev.tar.gz /tmp/docker-compose.yml
echo "🗑️ Aggressive Docker cleanup to reclaim space"
docker image prune -af --filter "until=24h" || true
docker container prune -f || true
docker volume prune -f || true
docker builder prune -af --filter "until=48h" || true
echo "💾 Docker space usage:"
docker system df
echo "✅ Dev deployment completed successfully"
EOF
# 6⃣ Verify deployment
- name: Verify deployment
shell: sh
env:
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
sleep 5
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST \
"docker ps | grep slipmatz-web || exit 1"

View File

@@ -0,0 +1,89 @@
name: Deploy Production
on:
push:
branches:
- main
- master
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout code
shell: sh
run: |
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo || true
cd /workspace/repo
git fetch origin $GITHUB_REF_NAME
git checkout $GITHUB_REF_NAME
git pull origin $GITHUB_REF_NAME
- name: Build Docker Image
shell: sh
run: |
cd /workspace/repo
docker build -t slipmatz-web:latest .
docker save slipmatz-web:latest | gzip > slipmatz-web.tar.gz
- name: Setup SSH and Deploy
shell: sh
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$PROD_DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keygen -y -f ~/.ssh/deploy_key > /dev/null 2>&1 || { echo "Error: Invalid SSH key format"; exit 1; }
cd /workspace/repo
scp -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key slipmatz-web.tar.gz docker-compose.yml "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST:/tmp/"
ssh -o StrictHostKeyChecking=no -i ~/.ssh/deploy_key "$PROD_DEPLOY_USER@$PROD_DEPLOY_HOST" "
DEPLOY_DIR='/var/www/apps/slipmatz-web'
mkdir -p \$DEPLOY_DIR
cd /tmp
docker load < slipmatz-web.tar.gz
echo 'Removing old slipmatz-web images'
docker images | grep slipmatz-web | grep -v "\$(docker images slipmatz-web:latest -q)" | awk '{print \$3}' | xargs -r docker rmi -f || true
cp docker-compose.yml \$DEPLOY_DIR/
cd \$DEPLOY_DIR
# .env file should already exist on server with all required variables
# Required: NUXT_PUBLIC_*, STRIPE_SECRET_KEY, etc.
# If it doesn't exist, deployment will fail (this is intentional for security)
docker compose down || true
docker image prune -f
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public
export DOMAIN=slipmatz.com
docker compose up -d
sleep 10
rm -f /tmp/slipmatz-web.tar.gz /tmp/docker-compose.yml
echo 'Aggressive Docker cleanup to reclaim space'
docker image prune -af --filter "until=24h" || true
docker container prune -f || true
docker volume prune -f || true
docker builder prune -af --filter "until=48h" || true
echo 'Docker space usage:'
docker system df
echo 'Production deployment completed successfully!'
echo 'Application available at: https://slipmatz.com'
"
env:
PROD_DEPLOY_SSH_KEY: ${{ secrets.PROD_DEPLOY_SSH_KEY }}
PROD_DEPLOY_USER: ${{ secrets.PROD_DEPLOY_USER }}
PROD_DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }}
- name: Health Check
shell: sh
run: |
sleep 10
curl -f https://slipmatz.com || exit 1

View File

@@ -1,81 +0,0 @@
name: Build and Push Docker Image
on:
push:
branches:
- main
- dev
- 'feat/**'
- 'fix/**'
tags:
- 'v*'
pull_request:
branches:
- main
- dev
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# - name: Deploy to production server
# if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
# uses: appleboy/ssh-action@v1.2.0
# with:
# host: ${{ secrets.SERVER_HOST }}
# username: ${{ secrets.SERVER_USER }}
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# port: ${{ secrets.SERVER_PORT || 22 }}
# script: |
# cd ${{ secrets.SERVER_PATH || '/root/crew-infrastructure' }}
# export SLIPMATZ_WEB_TAG=main
# docker compose pull slipmatz_web
# docker compose up -d slipmatz_web
# docker compose ps slipmatz_web

View File

@@ -20,7 +20,21 @@ services:
- NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.slipmatz-web.rule=Host(`${DOMAIN:-slipmatz.com}`)"
- "traefik.http.routers.slipmatz-web.entrypoints=websecure"
- "traefik.http.routers.slipmatz-web.tls=true"
- "traefik.http.routers.slipmatz-web.tls.certresolver=le"
- "traefik.http.services.slipmatz-web.loadbalancer.server.port=3000"
# HTTP to HTTPS redirect
- "traefik.http.routers.slipmatz-web-http.rule=Host(`${DOMAIN:-slipmatz.com}`)"
- "traefik.http.routers.slipmatz-web-http.entrypoints=web"
- "traefik.http.routers.slipmatz-web-http.middlewares=https-redirect"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
networks:
- traefik-public
- crew-app-net
- slipmatz-network
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
@@ -30,5 +44,9 @@ services:
start_period: 40s
networks:
traefik-public:
external: true
crew-app-net:
external: true
slipmatz-network:
driver: bridge