Skip to content

Merge pull request #332 from bounswe/feat/web/playwright-acceptance-t… #67

Merge pull request #332 from bounswe/feat/web/playwright-acceptance-t…

Merge pull request #332 from bounswe/feat/web/playwright-acceptance-t… #67

Workflow file for this run

name: Deploy Production (Azure ACA)
on:
push:
branches:
- main
paths:
- "backend/**"
- "frontend/**"
- ".github/workflows/deploy.yml"
workflow_dispatch:
inputs:
target:
description: "Which app to deploy"
required: true
default: "both"
type: choice
options: [both, backend, frontend]
env:
RESOURCE_GROUP: sem-backend-rg
ACA_ENV: sem-backend-env
ACR_NAME: semgroup9acr
ACR_SERVER: semgroup9acr.azurecr.io
BACKEND_APP: sem-backend
FRONTEND_APP: sem-frontend
# Frontend calls backend via the stable custom domain
FRONTEND_API_BASE_URL: https://api.thesocialeventmapper.social
jobs:
# ── Detect which parts changed ──────────────────────────────────────────────
detect-changes:
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.decide.outputs.backend }}
frontend: ${{ steps.decide.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- name: Detect paths changed
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
workflow:
- '.github/workflows/deploy.yml'
- name: Decide which apps to deploy
id: decide
run: |
# Manual dispatch honors the "target" input
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
case "${{ github.event.inputs.target }}" in
both) echo "backend=true" >> "$GITHUB_OUTPUT"; echo "frontend=true" >> "$GITHUB_OUTPUT" ;;
backend) echo "backend=true" >> "$GITHUB_OUTPUT"; echo "frontend=false" >> "$GITHUB_OUTPUT" ;;
frontend) echo "backend=false" >> "$GITHUB_OUTPUT"; echo "frontend=true" >> "$GITHUB_OUTPUT" ;;
esac
exit 0
fi
# If the workflow itself changed, re-deploy both to validate new pipeline
if [ "${{ steps.filter.outputs.workflow }}" = "true" ]; then
echo "backend=true" >> "$GITHUB_OUTPUT"
echo "frontend=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "backend=${{ steps.filter.outputs.backend }}" >> "$GITHUB_OUTPUT"
echo "frontend=${{ steps.filter.outputs.frontend }}" >> "$GITHUB_OUTPUT"
# ── Backend: build + push + deploy ──────────────────────────────────────────
deploy-backend:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Install containerapp extension
run: az extension add --name containerapp --upgrade --only-show-errors
- uses: docker/setup-buildx-action@v3
- name: ACR Docker login
uses: docker/login-action@v3
with:
registry: ${{ env.ACR_SERVER }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Build & push backend image
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile
push: true
tags: |
${{ env.ACR_SERVER }}/sem-backend:${{ github.sha }}
${{ env.ACR_SERVER }}/sem-backend:latest
- name: Deploy backend to ACA
id: deploy
run: |
set -e
IMAGE="${{ env.ACR_SERVER }}/sem-backend:${{ github.sha }}"
if az containerapp show -n ${{ env.BACKEND_APP }} -g ${{ env.RESOURCE_GROUP }} >/dev/null 2>&1; then
echo "Backend app exists — syncing secrets from GitHub Secrets..."
az containerapp secret set \
-n ${{ env.BACKEND_APP }} \
-g ${{ env.RESOURCE_GROUP }} \
--secrets \
supabase-url="${{ secrets.SUPABASE_URL }}" \
supabase-key="${{ secrets.SUPABASE_KEY }}" \
jwt-secret="${{ secrets.JWT_SECRET }}" \
google-client-id="${{ secrets.GOOGLE_CLIENT_ID }}" \
google-client-secret="${{ secrets.GOOGLE_CLIENT_SECRET }}" \
google-redirect-uri="${{ secrets.GOOGLE_REDIRECT_URI }}" \
smtp-host="${{ secrets.SMTP_HOST }}" \
smtp-port="${{ secrets.SMTP_PORT }}" \
smtp-user="${{ secrets.SMTP_USER }}" \
smtp-password="${{ secrets.SMTP_PASSWORD }}" \
smtp-from-email="${{ secrets.SMTP_FROM_EMAIL }}" \
smtp-from-name="${{ secrets.SMTP_FROM_NAME }}" \
--output none
echo "Updating image..."
az containerapp update \
-n ${{ env.BACKEND_APP }} \
-g ${{ env.RESOURCE_GROUP }} \
--image "$IMAGE" \
--output none
else
echo "Creating backend container app..."
az containerapp create \
-n ${{ env.BACKEND_APP }} \
-g ${{ env.RESOURCE_GROUP }} \
--environment ${{ env.ACA_ENV }} \
--image "$IMAGE" \
--registry-server ${{ env.ACR_SERVER }} \
--registry-username "${{ secrets.ACR_USERNAME }}" \
--registry-password "${{ secrets.ACR_PASSWORD }}" \
--target-port 8000 \
--ingress external \
--min-replicas 0 \
--max-replicas 3 \
--cpu 0.5 \
--memory 1.0Gi \
--secrets \
supabase-url="${{ secrets.SUPABASE_URL }}" \
supabase-key="${{ secrets.SUPABASE_KEY }}" \
jwt-secret="${{ secrets.JWT_SECRET }}" \
google-client-id="${{ secrets.GOOGLE_CLIENT_ID }}" \
google-client-secret="${{ secrets.GOOGLE_CLIENT_SECRET }}" \
google-redirect-uri="${{ secrets.GOOGLE_REDIRECT_URI }}" \
smtp-host="${{ secrets.SMTP_HOST }}" \
smtp-port="${{ secrets.SMTP_PORT }}" \
smtp-user="${{ secrets.SMTP_USER }}" \
smtp-password="${{ secrets.SMTP_PASSWORD }}" \
smtp-from-email="${{ secrets.SMTP_FROM_EMAIL }}" \
smtp-from-name="${{ secrets.SMTP_FROM_NAME }}" \
--env-vars \
"SUPABASE_URL=secretref:supabase-url" \
"SUPABASE_KEY=secretref:supabase-key" \
"JWT_SECRET=secretref:jwt-secret" \
"ENVIRONMENT=production" \
"FRONTEND_URL=${{ secrets.FRONTEND_URL }}" \
"BACKEND_URL=${{ secrets.BACKEND_URL }}" \
"CORS_ORIGINS=${{ secrets.CORS_ORIGINS }}" \
"COOKIE_DOMAIN=.thesocialeventmapper.social" \
"GOOGLE_CLIENT_ID=secretref:google-client-id" \
"GOOGLE_CLIENT_SECRET=secretref:google-client-secret" \
"GOOGLE_REDIRECT_URI=secretref:google-redirect-uri" \
"SMTP_HOST=secretref:smtp-host" \
"SMTP_PORT=secretref:smtp-port" \
"SMTP_USER=secretref:smtp-user" \
"SMTP_PASSWORD=secretref:smtp-password" \
"SMTP_FROM_EMAIL=secretref:smtp-from-email" \
"SMTP_FROM_NAME=secretref:smtp-from-name" \
--output none
fi
BACKEND_FQDN=$(az containerapp show -n ${{ env.BACKEND_APP }} -g ${{ env.RESOURCE_GROUP }} --query properties.configuration.ingress.fqdn -o tsv)
echo "backend_fqdn=$BACKEND_FQDN" >> $GITHUB_OUTPUT
- name: Backend health check
run: |
# Prefer custom domain when bound, fall back to ACA FQDN
for URL in "https://api.thesocialeventmapper.social/health" "https://${{ steps.deploy.outputs.backend_fqdn }}/health"; do
for i in $(seq 1 20); do
if curl --fail --silent "$URL" >/dev/null; then
echo "Backend healthy at $URL"
exit 0
fi
sleep 10
done
done
echo "Backend health check failed"
exit 1
# ── Frontend: build + push + deploy ─────────────────────────────────────────
deploy-frontend:
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Install containerapp extension
run: az extension add --name containerapp --upgrade --only-show-errors
- uses: docker/setup-buildx-action@v3
- name: ACR Docker login
uses: docker/login-action@v3
with:
registry: ${{ env.ACR_SERVER }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Build & push frontend image
uses: docker/build-push-action@v6
with:
context: ./frontend
file: ./frontend/Dockerfile
push: true
build-args: |
NEXT_PUBLIC_API_BASE_URL=${{ env.FRONTEND_API_BASE_URL }}
tags: |
${{ env.ACR_SERVER }}/sem-frontend:${{ github.sha }}
${{ env.ACR_SERVER }}/sem-frontend:latest
- name: Deploy frontend to ACA
id: deploy
run: |
set -e
IMAGE="${{ env.ACR_SERVER }}/sem-frontend:${{ github.sha }}"
if az containerapp show -n ${{ env.FRONTEND_APP }} -g ${{ env.RESOURCE_GROUP }} >/dev/null 2>&1; then
echo "Frontend app exists — updating image..."
az containerapp update \
-n ${{ env.FRONTEND_APP }} \
-g ${{ env.RESOURCE_GROUP }} \
--image "$IMAGE" \
--output none
else
echo "Creating frontend container app..."
az containerapp create \
-n ${{ env.FRONTEND_APP }} \
-g ${{ env.RESOURCE_GROUP }} \
--environment ${{ env.ACA_ENV }} \
--image "$IMAGE" \
--registry-server ${{ env.ACR_SERVER }} \
--registry-username "${{ secrets.ACR_USERNAME }}" \
--registry-password "${{ secrets.ACR_PASSWORD }}" \
--target-port 3000 \
--ingress external \
--min-replicas 0 \
--max-replicas 3 \
--cpu 0.5 \
--memory 1.0Gi \
--output none
fi
FRONTEND_FQDN=$(az containerapp show -n ${{ env.FRONTEND_APP }} -g ${{ env.RESOURCE_GROUP }} --query properties.configuration.ingress.fqdn -o tsv)
echo "frontend_fqdn=$FRONTEND_FQDN" >> $GITHUB_OUTPUT
- name: Frontend health check
run: |
for URL in "https://thesocialeventmapper.social/login" "https://${{ steps.deploy.outputs.frontend_fqdn }}/login"; do
for i in $(seq 1 20); do
if curl --fail --silent "$URL" >/dev/null; then
echo "Frontend healthy at $URL"
exit 0
fi
sleep 10
done
done
echo "Frontend health check failed"
exit 1