Files
mykopkb-be/.gitea/workflows/deploy-prod.yml
T
ismailmasseran 3ff62ccec6
Build Docker Image / build-backend (push) Successful in 30s
DONE: update cicd for backend
2026-10-25 14:38:26 +08:00

156 lines
5.9 KiB
YAML

name: Deploy to Production
# Manual production deploy — pulls pre-built images from the registry
on:
workflow_dispatch:
inputs:
image_tag:
description: "Docker image tag to deploy (commit SHA or version tag, e.g. v1.0.0)"
required: true
default: "latest"
type: string
env:
BACKEND_IMAGE: git.koppkb.com/kopkb/my-kopkb-be
jobs:
deploy:
runs-on: mykopkb-production
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.image_tag }}
- name: Verify image exists in registry
run: |
set -e
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
docker login git.koppkb.com \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
IMAGE="${BACKEND_IMAGE}:${{ inputs.image_tag }}"
echo "Checking if image exists: ${IMAGE}"
docker manifest inspect "${IMAGE}" > /dev/null
echo "✓ ${IMAGE} found"
- name: Deploy over SSH
env:
PROD_HOST: ${{ secrets.PROD_HOST }}
PROD_USER: ${{ secrets.PROD_USER }}
PROD_SSH_KEY: ${{ secrets.PROD_SSH_KEY }}
PROD_DEPLOY_DIR: ${{ secrets.PROD_DEPLOY_DIR }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
set -euo pipefail
if [ ! -f .env.production ]; then
echo "ERROR: .env.production is missing from the repository checkout"
exit 1
fi
if [ ! -f docker/production/nginx/nginx.conf ]; then
echo "ERROR: docker/production/nginx/nginx.conf is missing from the repository checkout"
exit 1
fi
if ! command -v ssh >/dev/null 2>&1; then
apt-get update && apt-get install -y openssh-client
fi
mkdir -p ~/.ssh
umask 077
# Gitea often stores a multiline key as one line. Prefer a base64
# secret (one line). A raw key that still contains BEGIN is accepted too.
if printf '%s' "$PROD_SSH_KEY" | grep -q -- '-----BEGIN'; then
printf '%b\n' "$PROD_SSH_KEY" | tr -d '\r' > ~/.ssh/id_ed25519
else
printf '%s' "$PROD_SSH_KEY" | tr -d '[:space:]' | base64 -d > ~/.ssh/id_ed25519
fi
chmod 600 ~/.ssh/id_ed25519
if ! ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null; then
echo "ERROR: PROD_SSH_KEY is not a readable private key."
echo "On the runner, run: base64 -w0 ~/.ssh/mykopkb_prod"
echo "Put that single line in the PROD_SSH_KEY secret."
exit 1
fi
ssh-keyscan -H "$PROD_HOST" >> ~/.ssh/known_hosts
SSH=(ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes "${PROD_USER}@${PROD_HOST}")
IMAGE="${BACKEND_IMAGE}:${IMAGE_TAG}"
NGINX_CONF="${PROD_DEPLOY_DIR}/nginx/nginx.conf"
"${SSH[@]}" "mkdir -p '${PROD_DEPLOY_DIR}/nginx'"
scp -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes \
docker/production/docker-compose.production.yml \
"${PROD_USER}@${PROD_HOST}:${PROD_DEPLOY_DIR}/docker-compose.yml"
scp -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes \
.env.production \
"${PROD_USER}@${PROD_HOST}:${PROD_DEPLOY_DIR}/.env"
scp -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes \
docker/production/nginx/nginx.conf \
"${PROD_USER}@${PROD_HOST}:${NGINX_CONF}"
"${SSH[@]}" "bash -s" << EOF
set -euo pipefail
echo "${REGISTRY_PASSWORD}" | docker login git.koppkb.com \
-u "${REGISTRY_USERNAME}" --password-stdin
echo "Checking if image exists: ${IMAGE}"
docker manifest inspect "${IMAGE}" > /dev/null
echo "✓ ${IMAGE} found"
echo "Deploying backend IMAGE=${IMAGE}"
chmod 600 "${PROD_DEPLOY_DIR}/.env"
docker network inspect mykopkb-production-network >/dev/null 2>&1 \
|| docker network create mykopkb-production-network
cd "${PROD_DEPLOY_DIR}"
export IMAGE_TAG="${IMAGE_TAG}"
export NGINX_CONF="${NGINX_CONF}"
docker rm -f mykopkb-be-production || true
docker compose -f docker-compose.yml pull backend
docker compose -f docker-compose.yml up -d backend
echo "Waiting for the backend to start..."
sleep 10
STATUS=\$(docker compose -f docker-compose.yml ps --status running --format '{{.Name}}' backend || true)
if [ -z "\$STATUS" ]; then
echo "ERROR: backend is not running"
docker compose -f docker-compose.yml ps
docker compose -f docker-compose.yml logs --tail=50 backend || true
exit 1
fi
echo "✓ backend is running (\$STATUS)"
BE_CONTAINER=\$(docker compose -f docker-compose.yml ps -q backend)
if docker exec "\$BE_CONTAINER" test -f /var/www/.env; then
echo "✓ Backend .env file exists"
else
echo "WARNING: Backend .env file not found"
fi
API_PORT=\$(grep -E '^API_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"' || true)
API_PORT=\${API_PORT:-8080}
for i in 1 2 3 4 5 6; do
if curl -sf "http://127.0.0.1:\${API_PORT}/health" > /dev/null; then
echo "✓ backend health check passed (http://127.0.0.1:\${API_PORT}/health)"
echo "✓ Deployed backend IMAGE_TAG=${IMAGE_TAG}"
exit 0
fi
echo "Waiting for backend... attempt \${i}/6"
sleep 5
done
echo "ERROR: backend health check failed (http://127.0.0.1:\${API_PORT}/health)"
docker compose -f docker-compose.yml logs --tail=50 backend || true
exit 1
EOF