Merge pull request #454 from lisafast/fix/auto-deploy #90
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 to AWS App Runner | |
| on: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| deployments: write | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| env: | |
| AWS_REGION: ${{ secrets.AWS_REGION }} | |
| AWS_ASSUME_ROLE_ARN: ${{ secrets.AWS_ASSUME_ROLE_ARN }} | |
| APP_RUNNER_SERVICE_ARN: ${{ secrets.APP_RUNNER_SERVICE_ARN }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | |
| GOOGLE_SEARCH_ENGINE_ID: ${{ secrets.GOOGLE_SEARCH_ENGINE_ID }} | |
| DOCDB_URI: ${{ secrets.DOCDB_URI }} | |
| JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} | |
| NODE_ENV: ${{ secrets.NODE_ENV }} | |
| steps: | |
| - name: Configure AWS credentials via OIDC | |
| uses: aws-actions/configure-aws-credentials@v2 | |
| with: | |
| role-to-assume: ${{ env.AWS_ASSUME_ROLE_ARN }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Create GitHub Deployment | |
| id: deployment | |
| uses: chrnorm/deployment-action@v2 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| environment: experimentation | |
| - name: Mark deployment as “in_progress” | |
| uses: chrnorm/deployment-status@v2 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
| state: in_progress | |
| - name: Write update-input.json | |
| run: | | |
| cat > update-input.json <<-EOF | |
| { | |
| "ServiceArn": "${{ env.APP_RUNNER_SERVICE_ARN }}", | |
| "SourceConfiguration": { | |
| "CodeRepository": { | |
| "RepositoryUrl": "https://github.com/${{ github.repository }}", | |
| "SourceCodeVersion": { | |
| "Type": "BRANCH", | |
| "Value": "main" | |
| }, | |
| "CodeConfiguration": { | |
| "ConfigurationSource": "API", | |
| "CodeConfigurationValues": { | |
| "Runtime": "NODEJS_22", | |
| "BuildCommand": "npm install && npm run build", | |
| "StartCommand": "npm run start-server", | |
| "RuntimeEnvironmentVariables": { | |
| "DEPLOY_RUN_ID": "${{ github.run_id }}", | |
| "ANTHROPIC_API_KEY": "${{ env.ANTHROPIC_API_KEY }}", | |
| "OPENAI_API_KEY": "${{ env.OPENAI_API_KEY }}", | |
| "GOOGLE_API_KEY": "${{ env.GOOGLE_API_KEY }}", | |
| "GOOGLE_SEARCH_ENGINE_ID": "${{ env.GOOGLE_SEARCH_ENGINE_ID }}", | |
| "DOCDB_URI": "${{ env.DOCDB_URI }}", | |
| "JWT_SECRET_KEY": "${{ env.JWT_SECRET_KEY }}", | |
| "NODE_ENV": "${{ env.NODE_ENV }}" | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "InstanceConfiguration": { "Cpu": "512", "Memory": "1024" }, | |
| "HealthCheckConfiguration": { | |
| "Protocol": "HTTP", | |
| "Path": "/health", | |
| "Interval": 20, | |
| "Timeout": 15, | |
| "HealthyThreshold": 1, | |
| "UnhealthyThreshold": 10 | |
| } | |
| } | |
| EOF | |
| - name: Wait up to 4h for idle | |
| run: | | |
| echo "Waiting for App Runner to be idle…" | |
| for i in {1..240}; do | |
| STATUS=$(aws apprunner describe-service \ | |
| --service-arn "${{ env.APP_RUNNER_SERVICE_ARN }}" \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --query 'Service.OperationSummary.Status' --output text) | |
| [[ "$STATUS" != "IN_PROGRESS" ]] && break | |
| sleep 60 | |
| done | |
| - name: Update service (retries) | |
| run: | | |
| set -e | |
| for i in {1..10}; do | |
| if aws apprunner update-service --cli-input-json file://update-input.json --region ${{ env.AWS_REGION }}; then | |
| echo "✅ update-service succeeded" | |
| break | |
| else | |
| echo "🔁 Retrying in 60s…" | |
| sleep 60 | |
| fi | |
| done | |
| - name: Wait up to 4h for RUNNING (force if idle) | |
| run: | | |
| echo "Waiting for service to deploy or forcing deployment if idle…" | |
| FORCED_FLAG=".forced_deployment" | |
| for i in {1..240}; do | |
| STATE=$(aws apprunner describe-service \ | |
| --service-arn "${{ env.APP_RUNNER_SERVICE_ARN }}" \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --query 'Service.Status' --output text) | |
| OP_STATUS=$(aws apprunner describe-service \ | |
| --service-arn "${{ env.APP_RUNNER_SERVICE_ARN }}" \ | |
| --region ${{ env.AWS_REGION }} \ | |
| --query 'Service.OperationSummary.Status' --output text) | |
| if [[ "$STATE" == "RUNNING" && "$OP_STATUS" != "IN_PROGRESS" ]]; then | |
| if [[ ! -f "$FORCED_FLAG" ]]; then | |
| echo "⚠️ Forcing a fresh deployment…" | |
| aws apprunner start-deployment \ | |
| --service-arn "${{ env.APP_RUNNER_SERVICE_ARN }}" \ | |
| --region ${{ env.AWS_REGION }} | |
| touch "$FORCED_FLAG" | |
| sleep 10 | |
| fi | |
| elif [[ "$STATE" == "RUNNING" && "$OP_STATUS" == "IN_PROGRESS" ]]; then | |
| echo "🟢 Deployment in progress." | |
| exit 0 | |
| elif [[ "$STATE" == "FAILED" ]]; then | |
| echo "❌ Deployment FAILED!" | |
| exit 1 | |
| else | |
| echo "⏳ State: $STATE, Op: $OP_STATUS… waiting." | |
| fi | |
| sleep 60 | |
| done | |
| echo "❌ Timed out"; exit 1 | |
| - name: Set deployment status to “success” | |
| if: success() | |
| uses: chrnorm/deployment-status@v2 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
| state: success | |
| environment-url: https://ai-answers.experimentation.ca | |
| - name: Set deployment status to “failure” | |
| if: failure() | |
| uses: chrnorm/deployment-status@v2 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
| state: failure |