Skip to content

Fix/remove otel deadlock #311

Fix/remove otel deadlock

Fix/remove otel deadlock #311

Workflow file for this run

name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
PROJECT_ID: hustleapp-production
REGION: us-central1
SERVICE_NAME: hustle-app
SERVICE_NAME_STAGING: hustle-app-staging
REGISTRY: us-central1-docker.pkg.dev/hustleapp-production/hustle-app
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
id-token: write
pull-requests: write
outputs:
url: ${{ steps.staging-url.outputs.url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
- name: Build and push Docker image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.SERVICE_NAME_STAGING }}:${{ github.sha }} \
-t ${{ env.REGISTRY }}/${{ env.SERVICE_NAME_STAGING }}:latest \
-f Dockerfile .
docker push ${{ env.REGISTRY }}/${{ env.SERVICE_NAME_STAGING }}:${{ github.sha }}
docker push ${{ env.REGISTRY }}/${{ env.SERVICE_NAME_STAGING }}:latest
- name: Deploy to Cloud Run (Staging)
run: |
gcloud run deploy ${{ env.SERVICE_NAME_STAGING }} \
--image ${{ env.REGISTRY }}/${{ env.SERVICE_NAME_STAGING }}:${{ github.sha }} \
--region ${{ env.REGION }} \
--project ${{ env.PROJECT_ID }} \
--platform managed \
--allow-unauthenticated \
--set-env-vars "NODE_ENV=staging,FIREBASE_PROJECT_ID=${{ env.PROJECT_ID }},NEXT_PUBLIC_FIREBASE_PROJECT_ID=${{ env.PROJECT_ID }},GOOGLE_CLOUD_PROJECT=${{ env.PROJECT_ID }},BILLING_ENABLED=false" \
--set-secrets "FIREBASE_SERVICE_ACCOUNT_JSON=FIREBASE_SERVICE_ACCOUNT_JSON:latest"
- name: Get staging URL
id: staging-url
run: |
URL=$(gcloud run services describe ${{ env.SERVICE_NAME_STAGING }} \
--region ${{ env.REGION }} \
--format 'value(status.url)')
echo "url=$URL" >> $GITHUB_OUTPUT
- name: Comment PR with staging URL
uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Deployed to staging: ${{ steps.staging-url.outputs.url }}`
})
smoke-test-staging:
name: Smoke Test (Staging)
runs-on: ubuntu-latest
needs: deploy-staging
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Get staging URL
id: staging-url
run: |
# Extract URL from previous job output (stored in GitHub outputs)
echo "url=${{ needs.deploy-staging.outputs.url }}" >> $GITHUB_OUTPUT
- name: Run smoke tests against staging
env:
SMOKE_TEST_URL: ${{ needs.deploy-staging.outputs.url }}
run: npm run smoke-test
- name: Comment PR with smoke test results
uses: actions/github-script@v7
if: always()
with:
script: |
const status = '${{ job.status }}' === 'success' ? '✅ Passed' : '❌ Failed';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Smoke tests ${status}\n\nTarget: ${{ needs.deploy-staging.outputs.url }}`
})
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check billing configuration (Phase 7 Task 6)
env:
# Default to false to allow deploys when Stripe secrets not configured
BILLING_ENABLED: ${{ secrets.BILLING_ENABLED || 'false' }}
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
STRIPE_PRICE_ID_STARTER: ${{ secrets.STRIPE_PRICE_ID_STARTER }}
STRIPE_PRICE_ID_PLUS: ${{ secrets.STRIPE_PRICE_ID_PLUS }}
STRIPE_PRICE_ID_PRO: ${{ secrets.STRIPE_PRICE_ID_PRO }}
run: |
if [ "$BILLING_ENABLED" = "true" ]; then
echo "✓ Billing is enabled - checking required Stripe env vars..."
# Check required Stripe variables
MISSING_VARS=""
if [ -z "$STRIPE_SECRET_KEY" ]; then
MISSING_VARS="$MISSING_VARS STRIPE_SECRET_KEY"
fi
if [ -z "$STRIPE_WEBHOOK_SECRET" ]; then
MISSING_VARS="$MISSING_VARS STRIPE_WEBHOOK_SECRET"
fi
if [ -z "$STRIPE_PRICE_ID_STARTER" ]; then
MISSING_VARS="$MISSING_VARS STRIPE_PRICE_ID_STARTER"
fi
if [ -z "$STRIPE_PRICE_ID_PLUS" ]; then
MISSING_VARS="$MISSING_VARS STRIPE_PRICE_ID_PLUS"
fi
if [ -z "$STRIPE_PRICE_ID_PRO" ]; then
MISSING_VARS="$MISSING_VARS STRIPE_PRICE_ID_PRO"
fi
if [ -n "$MISSING_VARS" ]; then
echo "❌ ERROR: Billing is enabled but required Stripe env vars are missing:"
echo "$MISSING_VARS"
echo ""
echo "Either:"
echo "1. Set missing Stripe secrets in GitHub (Settings → Secrets)"
echo "2. Set BILLING_ENABLED=false to disable billing"
exit 1
fi
echo "✓ All required Stripe env vars are present"
else
echo "⚠️ Billing is disabled (BILLING_ENABLED=false)"
echo "Checkout, portal, and invoice APIs will return 503"
fi
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
- name: Build and push Docker image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.SERVICE_NAME }}:${{ github.sha }} \
-t ${{ env.REGISTRY }}/${{ env.SERVICE_NAME }}:latest \
-f Dockerfile .
docker push ${{ env.REGISTRY }}/${{ env.SERVICE_NAME }}:${{ github.sha }}
docker push ${{ env.REGISTRY }}/${{ env.SERVICE_NAME }}:latest
- name: Deploy to Cloud Run (Production)
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image ${{ env.REGISTRY }}/${{ env.SERVICE_NAME }}:${{ github.sha }} \
--region ${{ env.REGION }} \
--project ${{ env.PROJECT_ID }} \
--platform managed \
--allow-unauthenticated \
--min-instances=1 \
--max-instances=10 \
--memory=1Gi \
--cpu=1 \
--timeout=300 \
--concurrency=80 \
--cpu-boost \
--set-env-vars "NODE_ENV=production,FIREBASE_PROJECT_ID=${{ env.PROJECT_ID }},NEXT_PUBLIC_FIREBASE_PROJECT_ID=${{ env.PROJECT_ID }},GOOGLE_CLOUD_PROJECT=${{ env.PROJECT_ID }},EMAIL_FROM=HUSTLE <[email protected]>,BILLING_ENABLED=false" \
--set-secrets "FIREBASE_SERVICE_ACCOUNT_JSON=FIREBASE_SERVICE_ACCOUNT_JSON:latest,RESEND_API_KEY=RESEND_API_KEY:latest"
- name: Configure health checks
run: |
# Add HTTP startup probe to ensure container is ready
gcloud run services update ${{ env.SERVICE_NAME }} \
--region ${{ env.REGION }} \
--update-probes=startup:httpGet:path=/api/health,port=8080,initial-delay=5,timeout=10,period=10,failure-threshold=3 \
--update-probes=liveness:httpGet:path=/api/health,port=8080,timeout=5,period=30,failure-threshold=3 || echo "Probe update skipped (may not be supported)"
- name: Verify deployment (GET + POST with retry)
run: |
URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} \
--region ${{ env.REGION }} \
--format 'value(status.url)')
# Function to test endpoint with retries
test_endpoint() {
local method=$1
local endpoint=$2
local max_retries=3
local retry_delay=10
for i in $(seq 1 $max_retries); do
echo "Attempt $i/$max_retries: $method $endpoint"
if [ "$method" = "GET" ]; then
if curl -sf --max-time 30 "$URL$endpoint" > /dev/null; then
echo "✅ $method $endpoint passed"
return 0
fi
else
if curl -sf --max-time 30 -X POST -H "Content-Type: application/json" -d '{"test":true}' "$URL$endpoint" > /dev/null; then
echo "✅ $method $endpoint passed"
return 0
fi
fi
if [ $i -lt $max_retries ]; then
echo "⏳ Retrying in ${retry_delay}s..."
sleep $retry_delay
fi
done
echo "❌ $method $endpoint failed after $max_retries attempts"
return 1
}
echo "=== Deployment Verification ==="
echo "URL: $URL"
echo ""
# Test GET endpoint
if ! test_endpoint "GET" "/api/healthcheck"; then
echo "❌ Deployment verification FAILED (GET)"
exit 1
fi
echo ""
# Test POST endpoint (critical for login/auth/forgot-password)
if ! test_endpoint "POST" "/api/healthcheck"; then
echo "❌ Deployment verification FAILED (POST)"
echo "⚠️ POST requests not working - login and auth will fail!"
exit 1
fi
echo ""
echo "🚀 Production deployment verified successfully!"
echo "Both GET and POST requests are working correctly."
echo "URL: $URL"