Fix/remove otel deadlock #311
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |