name: Production Deployment on: push: branches: [ main ] workflow_dispatch: inputs: custom_domain: description: 'Custom domain name' required: false default: 'miraclesinmotion.org' force_deploy: description: 'Force deployment even if tests fail' required: false default: 'false' env: NODE_VERSION: '22' AZURE_STATIC_WEB_APPS_API_TOKEN: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} jobs: build-and-test: runs-on: ubuntu-latest name: Build and Test steps: - uses: actions/checkout@v4 with: submodules: true lfs: false - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install main dependencies run: npm install --legacy-peer-deps - name: Install API dependencies run: | cd api npm install cd .. - name: Run linting run: npm run lint continue-on-error: true - name: Run tests run: npx vitest run --reporter=verbose continue-on-error: ${{ github.event.inputs.force_deploy == 'true' }} - name: Build application run: npm run build - name: Build API run: | cd api npm run build || npm run tsc cd .. - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-files path: | dist/ api/ staticwebapp.config.json deploy-infrastructure: runs-on: ubuntu-latest needs: build-and-test name: Deploy Infrastructure outputs: static-web-app-name: ${{ steps.deploy.outputs.staticWebAppName }} function-app-name: ${{ steps.deploy.outputs.functionAppName }} static-web-app-url: ${{ steps.deploy.outputs.staticWebAppUrl }} steps: - uses: actions/checkout@v4 - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Create Resource Group run: | az group create \ --name rg-miraclesinmotion-prod \ --location "East US" - name: Deploy Infrastructure id: deploy run: | DEPLOYMENT_NAME="mim-prod-$(date +%Y%m%d-%H%M%S)" # Deploy infrastructure DEPLOYMENT_OUTPUT=$(az deployment group create \ --resource-group rg-miraclesinmotion-prod \ --template-file infrastructure/main-production.bicep \ --parameters infrastructure/main-production.parameters.json \ --parameters stripePublicKey="${{ secrets.STRIPE_PUBLIC_KEY }}" \ --parameters customDomainName="${{ github.event.inputs.custom_domain || 'miraclesinmotion.org' }}" \ --parameters enableCustomDomain=true \ --name $DEPLOYMENT_NAME \ --output json) # Extract outputs STATIC_WEB_APP_NAME=$(echo $DEPLOYMENT_OUTPUT | jq -r '.properties.outputs.staticWebAppName.value') FUNCTION_APP_NAME=$(echo $DEPLOYMENT_OUTPUT | jq -r '.properties.outputs.functionAppName.value') STATIC_WEB_APP_URL=$(echo $DEPLOYMENT_OUTPUT | jq -r '.properties.outputs.staticWebAppUrl.value') # Set outputs echo "staticWebAppName=$STATIC_WEB_APP_NAME" >> $GITHUB_OUTPUT echo "functionAppName=$FUNCTION_APP_NAME" >> $GITHUB_OUTPUT echo "staticWebAppUrl=$STATIC_WEB_APP_URL" >> $GITHUB_OUTPUT echo "โœ… Infrastructure deployed successfully" echo "๐Ÿ“ฑ Static Web App: $STATIC_WEB_APP_NAME" echo "โšก Function App: $FUNCTION_APP_NAME" echo "๐ŸŒ URL: $STATIC_WEB_APP_URL" deploy-application: runs-on: ubuntu-latest needs: deploy-infrastructure name: Deploy Application environment: name: production url: ${{ needs.deploy-infrastructure.outputs.static-web-app-url }} steps: - uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: build-files - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Get Static Web App Deployment Token id: swa-token run: | DEPLOYMENT_TOKEN=$(az staticwebapp secrets list \ --name ${{ needs.deploy-infrastructure.outputs.static-web-app-name }} \ --resource-group rg-miraclesinmotion-prod \ --query "properties.apiKey" \ --output tsv) echo "::add-mask::$DEPLOYMENT_TOKEN" echo "token=$DEPLOYMENT_TOKEN" >> $GITHUB_OUTPUT - name: Setup Node.js for SWA CLI uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Install SWA CLI run: npm install -g @azure/static-web-apps-cli - name: Deploy to Static Web App run: | swa deploy ./dist \ --api-location ./api \ --env production \ --deployment-token ${{ steps.swa-token.outputs.token }} - name: Deploy Azure Functions run: | # Create deployment package cd api zip -r ../api-deployment.zip . -x "node_modules/*" "*.test.*" "*.md" cd .. # Deploy functions az functionapp deployment source config-zip \ --resource-group rg-miraclesinmotion-prod \ --name ${{ needs.deploy-infrastructure.outputs.function-app-name }} \ --src api-deployment.zip - name: Warm up application run: | echo "๐Ÿ”ฅ Warming up the deployed application..." curl -s ${{ needs.deploy-infrastructure.outputs.static-web-app-url }} > /dev/null curl -s ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/portals > /dev/null echo "โœ… Application warmed up successfully" post-deployment: runs-on: ubuntu-latest needs: [deploy-infrastructure, deploy-application] name: Post-Deployment Tasks steps: - name: Run smoke tests run: | echo "๐Ÿงช Running smoke tests..." # Test main page STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}) if [ $STATUS -eq 200 ]; then echo "โœ… Main page is accessible" else echo "โŒ Main page returned status: $STATUS" exit 1 fi # Test portals page STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/portals) if [ $STATUS -eq 200 ]; then echo "โœ… Portals page is accessible" else echo "โŒ Portals page returned status: $STATUS" exit 1 fi echo "๐ŸŽ‰ All smoke tests passed!" - name: Create deployment summary run: | echo "## ๐Ÿš€ Production Deployment Complete" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### ๐Ÿ“Š Deployment Details" >> $GITHUB_STEP_SUMMARY echo "- **Static Web App**: ${{ needs.deploy-infrastructure.outputs.static-web-app-name }}" >> $GITHUB_STEP_SUMMARY echo "- **Primary URL**: ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}" >> $GITHUB_STEP_SUMMARY echo "- **Portal Access**: ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/portals" >> $GITHUB_STEP_SUMMARY echo "- **Custom Domain**: https://${{ github.event.inputs.custom_domain || 'miraclesinmotion.org' }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### ๐Ÿ”— Quick Links" >> $GITHUB_STEP_SUMMARY echo "- [๐Ÿ  Main Site](${{ needs.deploy-infrastructure.outputs.static-web-app-url }})" >> $GITHUB_STEP_SUMMARY echo "- [๐Ÿšช Portals](${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/portals)" >> $GITHUB_STEP_SUMMARY echo "- [๐Ÿ’ฐ Donate](${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/donate)" >> $GITHUB_STEP_SUMMARY echo "- [๐Ÿค Volunteer](${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/volunteers)" >> $GITHUB_STEP_SUMMARY echo "- [๐Ÿ“Š Analytics](${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/analytics)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### ๐Ÿ“‹ Next Steps" >> $GITHUB_STEP_SUMMARY echo "1. Configure DNS records for custom domain" >> $GITHUB_STEP_SUMMARY echo "2. Update Stripe webhook endpoints" >> $GITHUB_STEP_SUMMARY echo "3. Test all portal functionality" >> $GITHUB_STEP_SUMMARY echo "4. Monitor application performance" >> $GITHUB_STEP_SUMMARY - name: Notify team if: success() run: | echo "๐ŸŽ‰ Production deployment completed successfully!" echo "๐ŸŒ Application is live at: ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}" echo "๐Ÿšช Portals are accessible at: ${{ needs.deploy-infrastructure.outputs.static-web-app-url }}/#/portals"