name: Deploy Frontend to Production # Manual production deploy — pulls pre-built admin + teller + customer images on: workflow_dispatch: inputs: image_tag: description: "Docker image tag to deploy (commit SHA or version tag, e.g. v1.0.0)" required: true type: string env: ADMIN_IMAGE: git.koppkb.com/kopkb/qms-fe-admin TELLER_IMAGE: git.koppkb.com/kopkb/qms-fe-teller CUSTOMER_IMAGE: git.koppkb.com/kopkb/qms-fe-customer DEPLOY_DIR: /home/arrahn/Project/qms-fe jobs: deploy: runs-on: host steps: - name: Checkout repository uses: actions/checkout@v4 with: ref: ${{ inputs.image_tag }} - name: Login to Docker registry run: | echo "${{ secrets.REGISTRY_PASSWORD }}" | \ docker login git.koppkb.com \ -u "${{ secrets.REGISTRY_USERNAME }}" \ --password-stdin - name: Verify frontend images exist in registry run: | set -e IMAGE_TAG="${{ inputs.image_tag }}" for IMAGE in "${ADMIN_IMAGE}:${IMAGE_TAG}" "${TELLER_IMAGE}:${IMAGE_TAG}" "${CUSTOMER_IMAGE}:${IMAGE_TAG}"; do echo "Checking if image exists: ${IMAGE}" if ! docker manifest inspect "${IMAGE}" > /dev/null 2>&1; then echo "ERROR: Image ${IMAGE} does not exist in registry!" exit 1 fi echo "✓ ${IMAGE} found" done - name: Pull frontend images run: | set -e IMAGE_TAG="${{ inputs.image_tag }}" docker pull "${ADMIN_IMAGE}:${IMAGE_TAG}" docker pull "${TELLER_IMAGE}:${IMAGE_TAG}" docker pull "${CUSTOMER_IMAGE}:${IMAGE_TAG}" echo "✓ Images pulled successfully" - name: Sync compose file and deploy run: | set -e IMAGE_TAG="${{ inputs.image_tag }}" mkdir -p "${DEPLOY_DIR}" cp fe/web/docker-compose.yml "${DEPLOY_DIR}/docker-compose.yml" cd "${DEPLOY_DIR}" if [ ! -f .env ]; then echo "ERROR: ${DEPLOY_DIR}/.env is missing on the server" echo "Create it from fe/web/.env.example (or fe/web/.env.production) before deploying." exit 1 fi export IMAGE_TAG export ADMIN_IMAGE export TELLER_IMAGE export CUSTOMER_IMAGE docker compose -f docker-compose.yml pull admin teller customer docker compose -f docker-compose.yml up -d --remove-orphans echo "✓ Deployed IMAGE_TAG=${IMAGE_TAG}" - name: Verify deployment run: | set -e cd "${DEPLOY_DIR}" echo "Waiting for services to start..." sleep 8 for SERVICE in admin teller customer; do STATUS=$(docker compose -f docker-compose.yml ps --status running --format '{{.Name}}' "$SERVICE" 2>/dev/null || true) if [ -z "$STATUS" ]; then echo "ERROR: ${SERVICE} is not running" docker compose -f docker-compose.yml ps docker compose -f docker-compose.yml logs --tail=50 "$SERVICE" || true exit 1 fi echo "✓ ${SERVICE} is running (${STATUS})" done ADMIN_HOST_PORT=$(grep -E '^ADMIN_HOST_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"' || true) TELLER_HOST_PORT=$(grep -E '^TELLER_HOST_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"' || true) CUSTOMER_HOST_PORT=$(grep -E '^CUSTOMER_HOST_PORT=' .env 2>/dev/null | cut -d= -f2- | tr -d '"' || true) ADMIN_HOST_PORT=${ADMIN_HOST_PORT:-5000} TELLER_HOST_PORT=${TELLER_HOST_PORT:-3001} CUSTOMER_HOST_PORT=${CUSTOMER_HOST_PORT:-3000} check_http() { NAME=$1 PORT=$2 for i in 1 2 3 4 5 6; do CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${PORT}/" || echo "000") if [ "${CODE}" != "000" ]; then echo "✓ ${NAME} is responding on port ${PORT} (HTTP ${CODE})" return 0 fi echo "Waiting for ${NAME}... attempt ${i}/6" sleep 3 done echo "ERROR: ${NAME} is not responding on port ${PORT}" return 1 } check_http "admin" "${ADMIN_HOST_PORT}" check_http "teller" "${TELLER_HOST_PORT}" check_http "customer" "${CUSTOMER_HOST_PORT}"