Skip to content

cleanup

cleanup #212

Workflow file for this run

# =============================================================================
# Scaleway Deployment Workflow
# =============================================================================
# Deploys Cella to Scaleway infrastructure using Terraform
# Triggered on pushes to main (prod) or manually for other environments
# =============================================================================
export const stxRequestSchema = z
permissions:

Check failure on line 10 in .github/workflows/deploy.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/deploy.yml

Invalid workflow file

You have an error in your yaml syntax on line 10
contents: read
on:
# Automatic deployment to production on main branch
push:
branches:
- main
paths:
- 'backend/**'
- 'cdc/**'
- 'frontend/**'
- 'infra/**'
- '.github/workflows/deploy.yml'
# Manual deployment for any environment
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'dev'
type: choice
options:
- dev
- staging
- prod
# Prevent concurrent deployments to the same environment
concurrency:
group: deploy-${{ github.event.inputs.environment || 'prod' }}
cancel-in-progress: false
env:
SCW_REGION: nl-ams
TERRAFORM_VERSION: '1.7.0'
jobs:
# -------------------------------------------------------------------------
# Determine which environment to deploy
# -------------------------------------------------------------------------
setup:
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
image_tag: ${{ steps.set-env.outputs.image_tag }}
steps:
- name: Determine environment
id: set-env
- key: NODE_VERSION
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
else
echo "environment=prod" >> $GITHUB_OUTPUT
fi
echo "image_tag=${{ github.sha }}" >> $GITHUB_OUTPUT
# -------------------------------------------------------------------------
# Build and push Docker images
# -------------------------------------------------------------------------
build-backend:
runs-on: ubuntu-latest
needs: setup
environment: ${{ needs.setup.outputs.environment }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Login to Scaleway Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: rg.${{ env.SCW_REGION }}.scw.cloud
username: nologin
password: ${{ secrets.SCW_SECRET_KEY }}
- name: Build and push backend image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: backend/Dockerfile
push: true
tags: |
rg.${{ env.SCW_REGION }}.scw.cloud/${{ needs.setup.outputs.environment }}cella/backend:${{ needs.setup.outputs.image_tag }}
rg.${{ env.SCW_REGION }}.scw.cloud/${{ needs.setup.outputs.environment }}cella/backend:latest
cache-from: type=gha
cache-to: type=gha,mode=max
build-cdc:
runs-on: ubuntu-latest
needs: setup
environment: ${{ needs.setup.outputs.environment }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Login to Scaleway Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: rg.${{ env.SCW_REGION }}.scw.cloud
username: nologin
password: ${{ secrets.SCW_SECRET_KEY }}
- name: Build and push CDC image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: cdc/Dockerfile
push: true
tags: |
rg.${{ env.SCW_REGION }}.scw.cloud/${{ needs.setup.outputs.environment }}cella/cdc:${{ needs.setup.outputs.image_tag }}
rg.${{ env.SCW_REGION }}.scw.cloud/${{ needs.setup.outputs.environment }}cella/cdc:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# -------------------------------------------------------------------------
# Build and deploy frontend to Object Storage
# -------------------------------------------------------------------------
build-frontend:
runs-on: ubuntu-latest
needs: setup
environment: ${{ needs.setup.outputs.environment }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build frontend
run: pnpm --filter frontend build
env:
VITE_BACKEND_URL: ${{ vars.BACKEND_URL }}
VITE_FRONTEND_URL: ${{ vars.FRONTEND_URL }}
- name: Install Scaleway CLI
run: |
curl -s https://raw.githubusercontent.com/scaleway/scaleway-cli/master/scripts/get.sh | sh
scw version
- name: Configure Scaleway CLI
run: |
scw config set access-key=${{ secrets.SCW_ACCESS_KEY }}
scw config set secret-key=${{ secrets.SCW_SECRET_KEY }}
scw config set default-project-id=${{ secrets.SCW_PROJECT_ID }}
scw config set default-region=${{ env.SCW_REGION }}
- name: Upload frontend to Object Storage
run: |
BUCKET_NAME="${{ needs.setup.outputs.environment }}-cella-frontend"
# Sync all files with appropriate cache headers
# Hashed assets get immutable caching
for file in frontend/dist/assets/*; do
scw object put "$file" \
--bucket "$BUCKET_NAME" \
--key "assets/$(basename $file)" \
--cache-control "public, max-age=31536000, immutable"
done
# index.html gets no-cache to always fetch latest
scw object put frontend/dist/index.html \
--bucket "$BUCKET_NAME" \
--key "index.html" \
--cache-control "no-cache, no-store, must-revalidate"
# Other static files
for file in frontend/dist/*.{js,css,ico,svg,png,jpg,webp} 2>/dev/null || true; do
[ -f "$file" ] && scw object put "$file" \
--bucket "$BUCKET_NAME" \
--key "$(basename $file)" \
--cache-control "public, max-age=86400"
done
- name: Purge Edge Services cache for index.html
run: |
# Purge only index.html - hashed assets don't need purging
PIPELINE_ID=$(scw edge-services pipeline list -o json | jq -r '.[] | select(.name | contains("${{ needs.setup.outputs.environment }}-cella")) | .id')
if [ -n "$PIPELINE_ID" ]; then
scw edge-services purge-request create \
pipeline-id="$PIPELINE_ID" \
assets.0="index.html"
fi
# -------------------------------------------------------------------------
# Apply Terraform infrastructure
# -------------------------------------------------------------------------
terraform:
runs-on: ubuntu-latest
needs: [setup, build-backend, build-cdc]
environment: ${{ needs.setup.outputs.environment }}
defaults:
run:
working-directory: infra
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}
- name: Configure Scaleway credentials
run: |
echo "SCW_ACCESS_KEY=${{ secrets.SCW_ACCESS_KEY }}" >> $GITHUB_ENV
echo "SCW_SECRET_KEY=${{ secrets.SCW_SECRET_KEY }}" >> $GITHUB_ENV
echo "SCW_DEFAULT_PROJECT_ID=${{ secrets.SCW_PROJECT_ID }}" >> $GITHUB_ENV
echo "SCW_DEFAULT_REGION=${{ env.SCW_REGION }}" >> $GITHUB_ENV
- name: Terraform Init
run: |
terraform init \
-backend-config="bucket=cella-terraform-state" \
-backend-config="key=${{ needs.setup.outputs.environment }}/terraform.tfstate" \
-backend-config="region=${{ env.SCW_REGION }}" \
-backend-config="endpoint=s3.${{ env.SCW_REGION }}.scw.cloud" \
-backend-config="access_key=${{ secrets.SCW_ACCESS_KEY }}" \
-backend-config="secret_key=${{ secrets.SCW_SECRET_KEY }}" \
-backend-config="skip_credentials_validation=true" \
-backend-config="skip_region_validation=true" \
-backend-config="skip_metadata_api_check=true"
- name: Select Terraform workspace
run: |
terraform workspace select ${{ needs.setup.outputs.environment }} || \
terraform workspace new ${{ needs.setup.outputs.environment }}
- name: Terraform Plan
run: |
terraform plan \
-var-file="environments/${{ needs.setup.outputs.environment }}.tfvars" \
-var="backend_image_tag=${{ needs.setup.outputs.image_tag }}" \
-var="cdc_image_tag=${{ needs.setup.outputs.image_tag }}" \
-var="argon_secret=${{ secrets.ARGON_SECRET }}" \
-var="cookie_secret=${{ secrets.COOKIE_SECRET }}" \
-var="unsubscribe_token_secret=${{ secrets.UNSUBSCRIBE_TOKEN_SECRET }}" \
-var="cdc_ws_secret=${{ secrets.CDC_WS_SECRET }}" \
-out=tfplan
- name: Terraform Apply
run: terraform apply -auto-approve tfplan
- name: Output deployment info
run: |
echo "## Deployment Complete 🚀" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Environment:** ${{ needs.setup.outputs.environment }}" >> $GITHUB_STEP_SUMMARY
echo "**Image Tag:** ${{ needs.setup.outputs.image_tag }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### URLs" >> $GITHUB_STEP_SUMMARY
terraform output -raw frontend_url >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
terraform output -raw backend_url >> $GITHUB_STEP_SUMMARY
# -------------------------------------------------------------------------
# Post-deployment health check
# -------------------------------------------------------------------------
health-check:
runs-on: ubuntu-latest
needs: [setup, terraform, build-frontend]
environment: ${{ needs.setup.outputs.environment }}
steps:
- name: Wait for deployment to stabilize
run: sleep 30
- name: Check backend health
run: |
HEALTH_URL="${{ vars.BACKEND_URL }}/health"
echo "Checking health at $HEALTH_URL"
for i in {1..10}; do
response=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" || echo "000")
if [ "$response" = "200" ]; then
echo "✅ Backend is healthy!"
exit 0
fi
echo "Attempt $i: Got $response, waiting..."
sleep 10
done
echo "❌ Backend health check failed"
exit 1
- name: Check frontend availability
run: |
FRONTEND_URL="${{ vars.FRONTEND_URL }}"
echo "Checking frontend at $FRONTEND_URL"
response=$(curl -s -o /dev/null -w "%{http_code}" "$FRONTEND_URL" || echo "000")
if [ "$response" = "200" ]; then
echo "✅ Frontend is available!"
else
echo "⚠️ Frontend returned $response"
fi