Skip to content

Build

Build #135

Workflow file for this run

name: Build
on:
workflow_dispatch:
pull_request:
paths:
- "app/**"
- "components/**"
- "lib/**"
- "actions/**"
- "hooks/**"
- "prisma/**"
- "scripts/**"
- "content/**"
- "theme.config.tsx"
- "mdx-components.tsx"
- "package.json"
- "pnpm-lock.yaml"
- "Dockerfile"
- ".github/workflows/build.yaml"
push:
branches:
- main
- staging
paths:
- "app/**"
- "components/**"
- "lib/**"
- "actions/**"
- "hooks/**"
- "prisma/**"
- "scripts/**"
- "content/**"
- "theme.config.tsx"
- "mdx-components.tsx"
- "package.json"
- "pnpm-lock.yaml"
- "Dockerfile"
- ".github/workflows/build.yaml"
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/tea-app
PROD_APP_URL: https://assuranceplatform.azurewebsites.net
STAGING_APP_URL: https://staging-assuranceplatform.azurewebsites.net
jobs:
validate:
name: Validate Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate Prisma client
run: npx prisma generate --schema=prisma/schema.prisma
env:
DATABASE_URL: postgresql://build:build@localhost:5432/build
- name: Run linter
run: pnpm exec ultracite check
- name: Run type check
run: npx tsc --noEmit
build:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: validate
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Stage A: pnpm caching for faster dependency resolution
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
# Note: Documentation is now built as part of Next.js (Nextra integration)
# No separate tea-docs build step needed
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# Branch name
type=ref,event=branch
# PR number
type=ref,event=pr
# SHA (short) - only for branch pushes, not PRs
type=sha,prefix={{branch}}-,enable=${{ github.event_name != 'pull_request' }}
# For main branch
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }}
# For staging branch
type=raw,value=staging,enable=${{ github.ref == 'refs/heads/staging' }}
type=raw,value=staging-{{date 'YYYY-MM-DD'}}-{{sha}},enable=${{ github.ref == 'refs/heads/staging' }}
# Semantic versions (for releases)
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push Docker image (Production - main branch)
if: github.ref == 'refs/heads/main'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GITHUB_APP_CLIENT_ID=${{ secrets.GH_APP_CLIENT_ID }}
GITHUB_APP_CLIENT_SECRET=${{ secrets.GH_APP_CLIENT_SECRET }}
GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}
NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }}
NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL_MAIN }}
DATABASE_URL=postgresql://build:build@localhost:5432/build
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Deploy to Azure (Production)
if: github.ref == 'refs/heads/main'
id: deploy-prod
env:
WEBHOOK_URL: ${{ secrets.AZURE_WEBAPP_WEBHOOK_PROD }}
run: |
echo "Triggering Azure deployment webhook..."
# Webhook with retry logic
MAX_RETRIES=3
DEPLOY_SUCCESS=false
for i in $(seq 1 $MAX_RETRIES); do
echo "Deployment attempt $i of $MAX_RETRIES..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 30 \
-X POST "${WEBHOOK_URL}" \
-H "Content-Length: 0")
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
echo "Webhook triggered successfully (HTTP $HTTP_STATUS)"
DEPLOY_SUCCESS=true
break
fi
echo "Webhook returned HTTP $HTTP_STATUS"
[ $i -lt $MAX_RETRIES ] && sleep 10
done
if [ "$DEPLOY_SUCCESS" = "false" ]; then
echo "::error::Deployment webhook failed after $MAX_RETRIES attempts"
exit 1
fi
# Wait for deployment to start
echo "Waiting 60 seconds for deployment to propagate..."
sleep 60
# Health check with retries
echo "Checking application health..."
HEALTH_SUCCESS=false
for i in $(seq 1 10); do
echo "Health check attempt $i of 10..."
HEALTH_STATUS=$(curl -sL -o /dev/null -w "%{http_code}" \
--max-time 10 \
"${{ env.PROD_APP_URL }}/api/health")
if [ "$HEALTH_STATUS" = "200" ]; then
echo "Application is healthy!"
HEALTH_SUCCESS=true
break
fi
echo "Health check returned HTTP $HEALTH_STATUS"
[ $i -lt 10 ] && sleep 15
done
if [ "$HEALTH_SUCCESS" = "false" ]; then
echo "::warning::Application health check did not pass after deployment"
echo "DEPLOY_HEALTH=failed" >> $GITHUB_OUTPUT
else
echo "DEPLOY_HEALTH=success" >> $GITHUB_OUTPUT
fi
- name: Build and push Docker image (Staging branch)
if: github.ref == 'refs/heads/staging'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GITHUB_APP_CLIENT_ID=${{ secrets.GH_APP_CLIENT_ID_STAGING }}
GITHUB_APP_CLIENT_SECRET=${{ secrets.GH_APP_CLIENT_SECRET_STAGING }}
GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID_STAGING }}
GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET_STAGING }}
NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET_STAGING }}
NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL_STAGING }}
DATABASE_URL=postgresql://build:build@localhost:5432/build
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: Deploy to Azure (Staging)
if: github.ref == 'refs/heads/staging'
id: deploy-staging
env:
WEBHOOK_URL: ${{ secrets.AZURE_WEBAPP_WEBHOOK_STAGING }}
run: |
echo "Triggering Azure deployment webhook..."
# Webhook with retry logic
MAX_RETRIES=3
DEPLOY_SUCCESS=false
for i in $(seq 1 $MAX_RETRIES); do
echo "Deployment attempt $i of $MAX_RETRIES..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 30 \
-X POST "${WEBHOOK_URL}" \
-H "Content-Length: 0")
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
echo "Webhook triggered successfully (HTTP $HTTP_STATUS)"
DEPLOY_SUCCESS=true
break
fi
echo "Webhook returned HTTP $HTTP_STATUS"
[ $i -lt $MAX_RETRIES ] && sleep 10
done
if [ "$DEPLOY_SUCCESS" = "false" ]; then
echo "::error::Deployment webhook failed after $MAX_RETRIES attempts"
exit 1
fi
# Wait for deployment to start
echo "Waiting 60 seconds for deployment to propagate..."
sleep 60
# Health check with retries
echo "Checking application health..."
HEALTH_SUCCESS=false
for i in $(seq 1 10); do
echo "Health check attempt $i of 10..."
HEALTH_STATUS=$(curl -sL -o /dev/null -w "%{http_code}" \
--max-time 10 \
"${{ env.STAGING_APP_URL }}/api/health")
if [ "$HEALTH_STATUS" = "200" ]; then
echo "Application is healthy!"
HEALTH_SUCCESS=true
break
fi
echo "Health check returned HTTP $HEALTH_STATUS"
[ $i -lt 10 ] && sleep 15
done
if [ "$HEALTH_SUCCESS" = "false" ]; then
echo "::warning::Application health check did not pass after deployment"
echo "DEPLOY_HEALTH=failed" >> $GITHUB_OUTPUT
else
echo "DEPLOY_HEALTH=success" >> $GITHUB_OUTPUT
fi
- name: Build and push Docker image (PR/other branches)
if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/staging'
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GITHUB_APP_CLIENT_ID=${{ secrets.GH_APP_CLIENT_ID_STAGING }}
GITHUB_APP_CLIENT_SECRET=${{ secrets.GH_APP_CLIENT_SECRET_STAGING }}
GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID_STAGING }}
GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET_STAGING }}
NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET_STAGING }}
NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL_STAGING }}
DATABASE_URL=postgresql://build:build@localhost:5432/build
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
# GitHub Deployment Summary
- name: Create Deployment Summary - Success
if: success() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
ENV="Production"
HEALTH_STATUS="${{ steps.deploy-prod.outputs.DEPLOY_HEALTH }}"
APP_URL="${{ env.PROD_APP_URL }}"
else
ENV="Staging"
HEALTH_STATUS="${{ steps.deploy-staging.outputs.DEPLOY_HEALTH }}"
APP_URL="${{ env.STAGING_APP_URL }}"
fi
if [ "$HEALTH_STATUS" = "success" ]; then
HEALTH_ICON="✅"
else
HEALTH_ICON="⚠️"
fi
echo "## 🚀 Deployment Successful" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Environment** | ${ENV} |" >> $GITHUB_STEP_SUMMARY
echo "| **Health Check** | ${HEALTH_ICON} ${HEALTH_STATUS} |" >> $GITHUB_STEP_SUMMARY
echo "| **Commit** | \`${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Actor** | @${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Application** | [${APP_URL}](${APP_URL}) |" >> $GITHUB_STEP_SUMMARY
- name: Create Deployment Summary - Failure
if: failure() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
ENV="Production"
else
ENV="Staging"
fi
echo "## ❌ Deployment Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Environment** | ${ENV} |" >> $GITHUB_STEP_SUMMARY
echo "| **Branch** | ${{ github.ref_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Commit** | \`${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Actor** | @${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY