Skip to content

fix(aws/recommendations): case-insensitive EC2 tenancy so AWS RI recs… #703

fix(aws/recommendations): case-insensitive EC2 tenancy so AWS RI recs…

fix(aws/recommendations): case-insensitive EC2 tenancy so AWS RI recs… #703

Workflow file for this run

# Deploy to GCP Cloud Run
#
# This workflow deploys the CUDly application to GCP Cloud Run.
# Serverless container platform with automatic scaling.
#
# Required GitHub Secrets:
# - ADMIN_EMAIL: Admin email for notifications
#
# Required GitHub Variables:
# - GCP_REGION: GCP region (e.g., us-central1)
# - GCP_PROJECT_ID: GCP project ID
# - GCP_WORKLOAD_IDENTITY_PROVIDER: WIF provider resource name for OIDC keyless auth
# - GCP_SERVICE_ACCOUNT: SA email for OIDC keyless auth
# - ARTIFACT_REGISTRY_REPO: Artifact Registry repository name (default: cudly)
#
# Triggered by:
# - Pushes to main or feat/multicloud-web-frontend branch
# - Manual workflow dispatch
# - Workflow call from deploy-all.yml
name: Deploy to GCP Cloud Run
permissions:
id-token: write
contents: read
concurrency:
group: deploy-gcp-${{ github.ref }}
cancel-in-progress: false
on:
push:
branches: [main, feat/multicloud-web-frontend]
workflow_dispatch:
inputs:
environment:
description: 'Environment'
required: true
type: choice
options: [dev, staging, prod]
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
ADMIN_EMAIL:
required: true
TF_BACKEND_GCP:
required: true
env:
GCP_REGION: ${{ vars.GCP_REGION || 'us-central1' }}
ARTIFACT_REGISTRY_REPO: ${{ vars.ARTIFACT_REGISTRY_REPO || 'cudly' }}
TF_VERSION: '1.10.0'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# Determine deployment environment
prepare:
name: Prepare Deployment
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
steps:
- name: Determine environment
id: set-env
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_call" ]]; then
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
else
echo "environment=dev" >> $GITHUB_OUTPUT
fi
# Build Docker image (via Terraform build module) and deploy
build-and-deploy:
name: Build & Deploy
runs-on: ubuntu-latest
needs: prepare
outputs:
service_url: ${{ steps.deploy.outputs.service_url }}
steps:
- name: Checkout code
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Setup Terraform
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Init
env:
TF_BACKEND: ${{ secrets.TF_BACKEND_GCP }}
ENVIRONMENT: ${{ needs.prepare.outputs.environment }}
run: |
printf '%s\nprefix = "github-%s"\n' "$TF_BACKEND" "$ENVIRONMENT" > /tmp/backend.tfbackend
# Break any stale state lock from a previous failed run
BUCKET=$(grep -E '^\s*bucket\s*=' /tmp/backend.tfbackend 2>/dev/null | tr -d ' "' | cut -d= -f2)
if [ -n "$BUCKET" ]; then
LOCK_FILE="gs://${BUCKET}/github-${ENVIRONMENT}/default.tflock"
gsutil rm "${LOCK_FILE}" 2>/dev/null || true
fi
cd terraform/environments/gcp
terraform init -backend-config=/tmp/backend.tfbackend
- name: Terraform Plan
env:
TF_VAR_admin_email: ${{ secrets.ADMIN_EMAIL }}
run: |
cd terraform/environments/gcp
terraform plan \
-var-file="github-${{ needs.prepare.outputs.environment }}.tfvars" \
-var="project_id=${{ vars.GCP_PROJECT_ID }}" \
-out=tfplan
- name: Terraform Apply
id: deploy
env:
TF_VAR_admin_email: ${{ secrets.ADMIN_EMAIL }}
run: |
cd terraform/environments/gcp
terraform apply -auto-approve tfplan
# Get service URL
SERVICE_URL=$(terraform output -raw cloud_run_service_url 2>/dev/null | grep -v '::' || echo "")
echo "service_url=$SERVICE_URL" >> $GITHUB_OUTPUT
- name: Release state lock on failure
if: failure() || cancelled()
env:
ENVIRONMENT: ${{ needs.prepare.outputs.environment }}
run: |
BUCKET=$(grep -E '^\s*bucket\s*=' /tmp/backend.tfbackend 2>/dev/null | tr -d ' "' | cut -d= -f2)
if [ -n "$BUCKET" ]; then
LOCK_FILE="gs://${BUCKET}/github-${ENVIRONMENT}/default.tflock"
echo "Releasing GCS state lock: ${LOCK_FILE}"
gsutil rm "${LOCK_FILE}" 2>/dev/null || echo "No lock file found (already clean)"
fi
- name: Save deployment info
run: |
cat <<EOF > deployment-info.json
{
"environment": "${{ needs.prepare.outputs.environment }}",
"service_url": "${{ steps.deploy.outputs.service_url }}",
"deployed_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"deployed_by": "${{ github.actor }}",
"commit": "${{ github.sha }}"
}
EOF
cat deployment-info.json
- name: Upload deployment info
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: deployment-info-gcp-${{ needs.prepare.outputs.environment }}
path: deployment-info.json
retention-days: 90
# Test the deployment
test-deployment:
name: Test Deployment
runs-on: ubuntu-latest
needs: [prepare, build-and-deploy]
if: always() && needs.build-and-deploy.result == 'success'
steps:
- name: Get Service URL
id: get-url
run: |
SERVICE_URL="${{ needs.build-and-deploy.outputs.service_url }}"
if [ -z "$SERVICE_URL" ]; then
echo "Failed to get service URL from deploy outputs"
exit 1
fi
SERVICE_URL="${SERVICE_URL%/}"
echo "url=$SERVICE_URL" >> $GITHUB_OUTPUT
- name: Wait for Cloud Run to be ready
run: |
echo "Waiting 30 seconds for Cloud Run to be ready..."
sleep 30
- name: Test health endpoint
run: |
URL="${{ steps.get-url.outputs.url }}"
echo "Testing health endpoint: $URL/health"
for i in {1..5}; do
if curl -f -s "$URL/health" > /dev/null; then
RESPONSE=$(curl -s "$URL/health")
echo "Response: $RESPONSE"
if echo "$RESPONSE" | grep -q '"status"'; then
echo "Health check passed!"
exit 0
fi
fi
echo "Attempt $i failed, retrying in 10 seconds..."
sleep 10
done
echo "Health check failed after 5 attempts"
exit 1
- name: Run smoke tests
run: |
URL="${{ steps.get-url.outputs.url }}"
echo "Running basic smoke tests..."
# Test health endpoint multiple times
for i in {1..3}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL/health")
if [ "$STATUS" -eq 200 ]; then
echo "Health check $i: passed (HTTP $STATUS)"
else
echo "Health check $i: failed (HTTP $STATUS)"
exit 1
fi
sleep 2
done
echo "All smoke tests passed!"
# Post deployment summary
summary:
name: Deployment Summary
runs-on: ubuntu-latest
needs: [prepare, build-and-deploy, test-deployment]
if: always()
steps:
- name: Download deployment info
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: deployment-info-gcp-${{ needs.prepare.outputs.environment }}
continue-on-error: true
- name: Post summary
run: |
echo "## GCP Cloud Run Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Environment:** ${{ needs.prepare.outputs.environment }}" >> $GITHUB_STEP_SUMMARY
echo "**Region:** ${{ env.GCP_REGION }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f deployment-info.json ]; then
echo "### Deployment Details" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY
cat deployment-info.json >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Job Results" >> $GITHUB_STEP_SUMMARY
echo "- Deploy: ${{ needs.build-and-deploy.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Test: ${{ needs.test-deployment.result }}" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.build-and-deploy.result }}" == "success" ] && [ "${{ needs.test-deployment.result }}" == "success" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ **Deployment successful!**" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "❌ **Deployment failed. Check logs for details.**" >> $GITHUB_STEP_SUMMARY
fi