Manual Deploy [boxel] #1232
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: Manual Deploy [boxel] | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: Deployment environment | |
| required: false | |
| default: staging | |
| workflow_call: | |
| inputs: | |
| environment: | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| deployments: write | |
| id-token: write | |
| jobs: | |
| create-deployment: | |
| name: Create GitHub deployment | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| deployment-id: ${{ steps.create.outputs.deployment_id }} | |
| environment-url: ${{ steps.env.outputs.environment_url }} | |
| steps: | |
| - id: env | |
| run: | | |
| if [ "${{ inputs.environment }}" = "production" ]; then | |
| echo "environment_url=https://app.boxel.ai" >> "$GITHUB_OUTPUT" | |
| elif [ "${{ inputs.environment }}" = "staging" ]; then | |
| echo "environment_url=https://realms-staging.stack.cards" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "environment_url=" >> "$GITHUB_OUTPUT" | |
| fi | |
| - id: create | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const environment = '${{ inputs.environment }}'; | |
| const response = await github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: context.sha, | |
| required_contexts: [], | |
| auto_merge: false, | |
| environment, | |
| description: `Manual deploy to ${environment}`, | |
| transient_environment: false, | |
| production_environment: environment === 'production', | |
| }); | |
| core.setOutput('deployment_id', response.data.id.toString()); | |
| - name: Mark deployment in progress | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const deployment_id = Number('${{ steps.create.outputs.deployment_id }}'); | |
| const environmentUrlValue = '${{ steps.env.outputs.environment_url }}'; | |
| const environment_url = | |
| environmentUrlValue && environmentUrlValue.length > 0 | |
| ? environmentUrlValue | |
| : undefined; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id, | |
| state: 'in_progress', | |
| environment_url, | |
| log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, | |
| description: 'Deployment started', | |
| }); | |
| build-ai-bot: | |
| name: Build ai-bot Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-ai-bot-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/ai-bot/Dockerfile" | |
| deploy-ai-bot: | |
| needs: [build-ai-bot, post-migrate-db] | |
| name: Deploy ai-bot to AWS ECS | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-ai-bot" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-ai-bot-${{ inputs.environment }}" | |
| image: ${{ needs.build-ai-bot.outputs.image }} | |
| wait-for-service-stability: false | |
| build-bot-runner: | |
| name: Build bot-runner Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-bot-runner-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/bot-runner/Dockerfile" | |
| deploy-bot-runner: | |
| needs: [build-bot-runner, post-migrate-db] | |
| name: Deploy bot-runner to AWS ECS | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-bot-runner" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-bot-runner-${{ inputs.environment }}" | |
| image: ${{ needs.build-bot-runner.outputs.image }} | |
| wait-for-service-stability: false | |
| build-host: | |
| name: Build host | |
| uses: ./.github/workflows/build-host.yml | |
| secrets: inherit | |
| with: | |
| environment: ${{ inputs.environment }} | |
| deploy-host: | |
| name: Deploy host | |
| needs: [build-host] | |
| uses: ./.github/workflows/deploy-host.yml | |
| secrets: inherit | |
| with: | |
| environment: ${{ inputs.environment }} | |
| deploy-ui: | |
| name: Deploy boxel-ui | |
| if: inputs.environment == 'staging' | |
| uses: ./.github/workflows/deploy-ui.yml | |
| secrets: inherit | |
| with: | |
| environment: staging | |
| build-realm-server: | |
| name: Build realm-server Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-realm-server-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/realm-server/realm-server.Dockerfile" | |
| build-args: | | |
| "realm_server_script=start:${{ inputs.environment }}" | |
| build-prerender-manager: | |
| name: Build prerender manager Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-prerender-manager-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/realm-server/prerender-manager.Dockerfile" | |
| build-args: | | |
| "prerender_manager_script=start:prerender-manager" | |
| build-prerender: | |
| name: Build prerender Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-prerender-server-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/realm-server/prerender.Dockerfile" | |
| build-args: | | |
| "prerender_script=start:prerender-${{ inputs.environment }}" | |
| build-worker: | |
| name: Build worker Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-worker-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/realm-server/worker.Dockerfile" | |
| build-args: | | |
| "worker_script=start:worker-${{ inputs.environment }}" | |
| build-pg-migration: | |
| name: Build pg-migration Docker image | |
| uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main | |
| secrets: inherit | |
| with: | |
| repository: "boxel-pg-migration-${{ inputs.environment }}" | |
| environment: ${{ inputs.environment }} | |
| dockerfile: "packages/postgres/Dockerfile" | |
| migrate-db: | |
| # use "deploy-host" and "build-realm-server" as deps so we can run | |
| # migrations at last possible moment in order to reduce the amount of time | |
| # that old code is pointing to new schema | |
| needs: [build-pg-migration, build-realm-server, deploy-host] | |
| name: Deploy and run DB migrations | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-pg-migration" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-pg-migration-${{ inputs.environment }}" | |
| image: ${{ needs.build-pg-migration.outputs.image }} | |
| wait-for-service-stability: false | |
| # the wait-for-service-stability flag doesn't seem to work in | |
| # aws-actions/amazon-ecs-deploy-task-definition@v2. we keep getting timeouts | |
| # waiting for service stability. So we are manually waiting here. | |
| post-migrate-db: | |
| name: Wait for db-migration | |
| needs: [migrate-db] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: sleep 180 | |
| deploy-prerender: | |
| name: Deploy prerender | |
| needs: [build-prerender] | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-prerender-server" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-prerender-server-${{ inputs.environment }}" | |
| image: ${{ needs.build-prerender.outputs.image }} | |
| timeout-minutes: 10 | |
| wait-for-service-stability: true | |
| deploy-prerender-manager: | |
| name: Deploy prerender manager | |
| needs: [build-prerender-manager, deploy-prerender] | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-prerender-manager" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-prerender-manager-${{ inputs.environment }}" | |
| image: ${{ needs.build-prerender-manager.outputs.image }} | |
| timeout-minutes: 10 | |
| wait-for-service-stability: true | |
| deploy-worker: | |
| name: Deploy worker | |
| needs: | |
| [build-worker, deploy-host, post-migrate-db, deploy-prerender-manager] | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-worker" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-worker-${{ inputs.environment }}" | |
| image: ${{ needs.build-worker.outputs.image }} | |
| wait-for-service-stability: false | |
| # the wait-for-service-stability flag doesn't seem to work in | |
| # aws-actions/amazon-ecs-deploy-task-definition@v2. we keep getting timeouts | |
| # waiting for service stability. So we are manually waiting here. | |
| post-deploy-worker: | |
| name: Wait for worker | |
| needs: [deploy-worker] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - run: sleep 180 | |
| deploy-realm-server: | |
| name: Deploy realm server | |
| needs: | |
| [post-deploy-worker, build-realm-server, deploy-host, post-migrate-db] | |
| uses: cardstack/gh-actions/.github/workflows/ecs-deploy.yml@main | |
| secrets: inherit | |
| with: | |
| container-name: "boxel-realm-server" | |
| environment: ${{ inputs.environment }} | |
| cluster: ${{ inputs.environment }} | |
| service-name: "boxel-realm-server-${{ inputs.environment }}" | |
| image: ${{ needs.build-realm-server.outputs.image }} | |
| timeout-minutes: 10 | |
| wait-for-service-stability: true | |
| post-deploy-realm-server: | |
| name: After realm server stable deployment | |
| needs: [deploy-realm-server] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Call post-deployment endpoint on realm server | |
| run: | | |
| if [ "${{ inputs.environment }}" = "production" ]; then | |
| URL="https://app.boxel.ai/_post-deployment?authHeader=${{ secrets.PRODUCTION_REALM_SERVER_SECRET }}" | |
| elif [ "${{ inputs.environment }}" = "staging" ]; then | |
| URL="https://realms-staging.stack.cards/_post-deployment?authHeader=${{ secrets.STAGING_REALM_SERVER_SECRET }}" | |
| else | |
| echo "Unknown environment: ${{ inputs.environment }}" | |
| exit 1 | |
| fi | |
| response=$(curl -s -w "\n%{http_code}" -X POST "$URL") | |
| response_body=$(echo "$response" | head -n -1) | |
| response_code=$(echo "$response" | tail -n 1) | |
| echo "Response body: $response_body" | |
| if [ "$response_code" != "200" ]; then | |
| echo "Post-deployment endpoint returned $response_code, expected 200" | |
| echo "Response body: $response_body" | |
| exit 1 | |
| fi | |
| finalize-deployment: | |
| name: Update GitHub deployment status | |
| needs: | |
| [ | |
| create-deployment, | |
| build-ai-bot, | |
| deploy-ai-bot, | |
| build-bot-runner, | |
| deploy-bot-runner, | |
| build-host, | |
| deploy-host, | |
| build-realm-server, | |
| build-prerender-manager, | |
| build-prerender, | |
| build-worker, | |
| build-pg-migration, | |
| migrate-db, | |
| post-migrate-db, | |
| deploy-prerender, | |
| deploy-prerender-manager, | |
| deploy-worker, | |
| post-deploy-worker, | |
| deploy-realm-server, | |
| post-deploy-realm-server, | |
| ] | |
| if: github.event_name == 'workflow_dispatch' && always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set deployment status | |
| uses: actions/github-script@v7 | |
| env: | |
| NEEDS: ${{ toJson(needs) }} | |
| DEPLOYMENT_ID: ${{ needs.create-deployment.outputs.deployment-id }} | |
| ENVIRONMENT_URL: ${{ needs.create-deployment.outputs.environment-url }} | |
| with: | |
| script: | | |
| const deploymentIdRaw = process.env.DEPLOYMENT_ID; | |
| if (!deploymentIdRaw) { | |
| core.info('No deployment id found; skipping status update.'); | |
| return; | |
| } | |
| const needs = JSON.parse(process.env.NEEDS); | |
| const results = Object.values(needs).map((job) => job.result); | |
| let state = 'success'; | |
| if (results.includes('failure')) { | |
| state = 'failure'; | |
| } else if (results.includes('cancelled')) { | |
| state = 'inactive'; | |
| } | |
| const environment_url = | |
| process.env.ENVIRONMENT_URL && | |
| process.env.ENVIRONMENT_URL.length > 0 | |
| ? process.env.ENVIRONMENT_URL | |
| : undefined; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: Number(deploymentIdRaw), | |
| state, | |
| environment_url, | |
| log_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, | |
| description: | |
| state === 'success' | |
| ? 'Deployment finished' | |
| : 'Deployment finished with errors', | |
| }); |