Fly Deployment #1244
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: Fly Deployment | |
| # This workflow is triggered by a pull request or push to the main branch. | |
| # Renovate and Nx migrations are ignored. | |
| # Deployments run when the preview label is present (auto-added on PR open) or when closing PRs for cleanup. | |
| # Can also be triggered manually for re-deployments. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| app: | |
| description: "App to deploy (leave empty for all affected apps)" | |
| required: false | |
| type: choice | |
| options: | |
| - "" | |
| - cms | |
| - web | |
| tenant: | |
| description: "Tenant ID (leave empty for all tenants, only applies to | |
| multi-tenant apps)" | |
| required: false | |
| type: string | |
| environment: | |
| description: "Target environment" | |
| required: true | |
| type: choice | |
| options: | |
| - preview | |
| - production | |
| default: production | |
| console-logs: | |
| description: "Show native logs from e.g. Fly and Docker" | |
| required: false | |
| type: boolean | |
| default: false | |
| # Primary trigger once this workflow is on the default branch: | |
| # deployment only starts after CI checks pass. | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| pull_request: | |
| types: | |
| - opened | |
| - reopened | |
| - synchronize | |
| - closed | |
| push: | |
| branches: | |
| - main | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NX_NO_CLOUD: true | |
| # Label used to indicate that a preview deployment is enabled for the PR | |
| FLY_PREVIEW_LABEL: preview-deploy | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.head_ref || github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| analyze-conditions: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| skip: ${{ steps.conditions.outputs.skip }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build conditions action | |
| run: pnpm nx build fly-conditions-action | |
| - name: Analyze deployment conditions | |
| id: conditions | |
| uses: ./packages/fly-conditions-action | |
| with: | |
| preview-label: ${{ env.FLY_PREVIEW_LABEL }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| pre-deploy: | |
| needs: analyze-conditions | |
| if: ${{ needs.analyze-conditions.outputs.skip != 'true' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| apps: ${{ steps.pre-deploy.outputs.apps }} | |
| environment: ${{ steps.pre-deploy.outputs.environment }} | |
| app-tenants: ${{ steps.pre-deploy.outputs.app-tenants }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build pre-deploy action | |
| run: pnpm nx build nx-pre-deploy-action | |
| - name: Set SHAs for affected calculations | |
| uses: nrwl/nx-set-shas@v4 | |
| with: | |
| main-branch-name: main | |
| set-environment-variables-for-job: true | |
| - name: Resolve PR number | |
| id: resolve-pr | |
| env: | |
| WR_PRS: ${{ toJSON(github.event.workflow_run.pull_requests) }} | |
| DIRECT_PR: ${{ github.event.number }} | |
| run: | | |
| PR=$(echo "$WR_PRS" | jq -r 'first | .number // empty') | |
| echo "number=${PR:-$DIRECT_PR}" >> "$GITHUB_OUTPUT" | |
| - name: Run pre-deploy | |
| id: pre-deploy | |
| uses: ./packages/nx-pre-deploy-action | |
| with: | |
| infisical-client-id: ${{ secrets.INFISICAL_READ_CLIENT_ID }} | |
| infisical-client-secret: ${{ secrets.INFISICAL_READ_CLIENT_SECRET }} | |
| infisical-project-id: ${{ secrets.INFISICAL_PROJECT_ID }} | |
| manual-app: ${{ github.event.inputs.app }} | |
| manual-tenant: ${{ github.event.inputs.tenant }} | |
| manual-environment: ${{ github.event.inputs.environment }} | |
| env: | |
| # Must be defined since `github.json` is validated and cms app uses env | |
| POSTGRES_PREVIEW: ${{ vars.FLY_POSTGRES_PREVIEW }} | |
| PR_NUMBER: ${{ steps.resolve-pr.outputs.number }} | |
| build: | |
| needs: pre-deploy | |
| if: >- | |
| ${{ needs.pre-deploy.outputs.environment != '' && | |
| needs.pre-deploy.outputs.apps != '[]' && github.event.action != 'closed' | |
| }} | |
| runs-on: ubuntu-latest | |
| env: | |
| SENTRY_ORG: ${{ secrets.INF_SENTRY_ORG }} | |
| outputs: | |
| images: ${{ steps.build.outputs.images }} | |
| steps: | |
| - uses: actions/create-github-app-token@v1 | |
| id: generate-token | |
| with: | |
| app-id: ${{ secrets.CDWR_ACTIONS_BOT_ID }} | |
| private-key: ${{ secrets.CDWR_ACTIONS_BOT_PRIVATE_KEY }} | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build build action | |
| run: pnpm nx build fly-build-action | |
| - name: Install Fly CLI | |
| uses: superfly/flyctl-actions/setup-flyctl@master | |
| with: | |
| version: ${{ vars.FLY_CLI_VERSION }} | |
| # The Sentry webpack plugin uploads source maps during the Docker build and expects | |
| # the release to already exist (next.config.mjs: create: false). So the release is | |
| # created here, before the build. If anything fails afterward it is deleted below. | |
| - name: Create Sentry release | |
| id: sentry-release | |
| if: env.SENTRY_ORG != '' | |
| uses: getsentry/action-release@v3 | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.INF_SENTRY_PROJECT }} | |
| with: | |
| environment: ${{ needs.pre-deploy.outputs.environment }} | |
| release: ${{ github.sha }} | |
| set_commits: "auto" | |
| finalize: false | |
| - name: Resolve flags | |
| id: flags | |
| run: | | |
| echo "console-logs=${{ github.event.inputs.console-logs == 'true' && 'true' || vars.FLY_CONSOLE_LOGS }}" >> "$GITHUB_OUTPUT" | |
| - name: Build Docker images | |
| id: build | |
| uses: ./packages/fly-build-action | |
| with: | |
| fly-api-token: ${{ secrets.FLY_API_TOKEN }} | |
| fly-org: ${{ vars.FLY_ORG }} | |
| fly-trace-cli: ${{ vars.FLY_TRACE_CLI }} | |
| fly-console-logs: ${{ steps.flags.outputs.console-logs }} | |
| token: ${{ steps.generate-token.outputs.token }} | |
| apps: ${{ needs.pre-deploy.outputs.apps }} | |
| environment: ${{ needs.pre-deploy.outputs.environment }} | |
| app-details: ${{ needs.pre-deploy.outputs.app-tenants }} | |
| # Build arguments for Docker build (client-side vars + Sentry source map upload) | |
| build-args: | | |
| NEXT_PUBLIC_DEPLOY_ENV=${{ needs.pre-deploy.outputs.environment }} | |
| NEXT_PUBLIC_SENTRY_DSN=${{ secrets.INF_SENTRY_DSN }} | |
| NEXT_PUBLIC_SENTRY_RELEASE=${{ github.sha }} | |
| SENTRY_ORG=${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT=${{ secrets.INF_SENTRY_PROJECT }} | |
| SENTRY_RELEASE=${{ github.sha }} | |
| SENTRY_AUTH_TOKEN=${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| opt-out-depot-builder: ${{ vars.FLY_OPT_OUT_DEPOT }} | |
| # Upload images map as artifact — job outputs are suppressed by GitHub | |
| # when they match registered secret patterns (Fly registry URLs can trigger this). | |
| # Artifact files bypass secret scanning and are read by the action's Node process. | |
| - name: Upload images artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fly-built-images-${{ github.run_id }} | |
| path: ${{ steps.build.outputs.images-path }} | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # Clean up the Sentry release if the build failed. | |
| - name: Delete Sentry release on build failure | |
| if: >- | |
| env.SENTRY_ORG != '' && steps.sentry-release.outcome == 'success' && | |
| steps.build.outcome != 'success' | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.INF_SENTRY_PROJECT }} | |
| run: pnpm sentry-cli releases delete "${{ github.sha }}" || true | |
| deploy: | |
| needs: [pre-deploy, build] | |
| if: needs.build.result == 'success' | |
| runs-on: ubuntu-latest | |
| # Not possible to provide url since we might have multiple deployments | |
| environment: ${{ needs.pre-deploy.outputs.environment }} | |
| outputs: | |
| environment: ${{ steps.deployment.outputs.environment }} | |
| deployed: ${{ steps.deployment.outputs.deployed }} | |
| failed: ${{ steps.deployment.outputs.failed }} | |
| projects: ${{ steps.deployment.outputs.projects }} | |
| steps: | |
| - uses: actions/create-github-app-token@v1 | |
| id: generate-token | |
| with: | |
| app-id: ${{ secrets.CDWR_ACTIONS_BOT_ID }} | |
| private-key: ${{ secrets.CDWR_ACTIONS_BOT_PRIVATE_KEY }} | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build deployment action | |
| run: pnpm nx build fly-deployment-action | |
| - name: Install Fly CLI | |
| uses: superfly/flyctl-actions/setup-flyctl@master | |
| with: | |
| version: ${{ vars.FLY_CLI_VERSION }} | |
| - name: Resolve flags | |
| id: flags | |
| run: | | |
| echo "console-logs=${{ github.event.inputs.console-logs == 'true' && 'true' || vars.FLY_CONSOLE_LOGS }}" >> "$GITHUB_OUTPUT" | |
| - name: Download images artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: fly-built-images-${{ github.run_id }} | |
| path: ${{ runner.temp }}/fly-images | |
| - name: Deploy to Fly | |
| id: deployment | |
| uses: ./packages/fly-deployment-action | |
| with: | |
| fly-api-token: ${{ secrets.FLY_API_TOKEN }} | |
| fly-org: ${{ vars.FLY_ORG }} | |
| fly-region: ${{ vars.FLY_REGION }} | |
| fly-trace-cli: ${{ vars.FLY_TRACE_CLI }} | |
| fly-console-logs: ${{ steps.flags.outputs.console-logs }} | |
| token: ${{ steps.generate-token.outputs.token }} | |
| apps: ${{ needs.pre-deploy.outputs.apps }} | |
| environment: ${{ needs.pre-deploy.outputs.environment }} | |
| app-details: ${{ needs.pre-deploy.outputs.app-tenants }} | |
| images-path: ${{ runner.temp }}/fly-images/fly-built-images.json | |
| # Runtime environment variables | |
| env: | | |
| INFISICAL_SITE=${{ vars.INFISICAL_SITE }} | |
| SENTRY_DSN=${{ secrets.INF_SENTRY_DSN }} | |
| SENTRY_ORG=${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT=${{ secrets.INF_SENTRY_PROJECT }} | |
| SENTRY_RELEASE=${{ github.sha }} | |
| # Sensitive secrets are also passed to app env on deployment | |
| secrets: | | |
| INFISICAL_CLIENT_ID=${{ secrets.INFISICAL_READ_CLIENT_ID }} | |
| INFISICAL_CLIENT_SECRET=${{ secrets.INFISICAL_READ_CLIENT_SECRET }} | |
| INFISICAL_PROJECT_ID=${{ secrets.INFISICAL_PROJECT_ID }} | |
| SENTRY_AUTH_TOKEN=${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| opt-out-depot-builder: ${{ vars.FLY_OPT_OUT_DEPOT }} | |
| - name: Finalize Sentry release | |
| if: steps.deployment.outcome == 'success' | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.INF_SENTRY_PROJECT }} | |
| run: pnpm sentry-cli releases finalize "${{ github.sha }}" | |
| # Clean up the Sentry release if deployment failed. | |
| - name: Delete Sentry release on deployment failure | |
| if: always() && steps.deployment.outcome != 'success' | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.INF_SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.INF_SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.INF_SENTRY_PROJECT }} | |
| run: pnpm sentry-cli releases delete "${{ github.sha }}" || true | |
| # Upload projects as artifact — job outputs containing Fly.io URLs can be suppressed | |
| # by GitHub Actions secret scanning. Artifact files bypass secret scanning. | |
| - name: Upload projects artifact | |
| if: always() && steps.deployment.outputs.projects-path != '' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fly-deployed-projects-${{ github.run_id }} | |
| path: ${{ steps.deployment.outputs.projects-path }} | |
| retention-days: 1 | |
| if-no-files-found: warn | |
| pr-comment: | |
| needs: [pre-deploy, deploy] | |
| if: >- | |
| always() && | |
| (github.event_name == 'pull_request' && github.event.action != 'closed' || | |
| github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request') && | |
| needs.deploy.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/create-github-app-token@v1 | |
| id: generate-token | |
| with: | |
| app-id: ${{ secrets.CDWR_ACTIONS_BOT_ID }} | |
| private-key: ${{ secrets.CDWR_ACTIONS_BOT_PRIVATE_KEY }} | |
| - name: Resolve PR number | |
| id: resolve-pr | |
| env: | |
| WR_PRS: ${{ toJSON(github.event.workflow_run.pull_requests) }} | |
| DIRECT_PR: ${{ github.event.number }} | |
| run: | | |
| PR=$(echo "$WR_PRS" | jq -r 'first | .number // empty') | |
| echo "number=${PR:-$DIRECT_PR}" >> "$GITHUB_OUTPUT" | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build PR comment action | |
| run: pnpm nx build pr-comment-action | |
| - name: Download projects artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: fly-deployed-projects-${{ github.run_id }} | |
| path: ${{ runner.temp }}/fly-projects | |
| continue-on-error: true | |
| - name: Post PR comment | |
| uses: ./packages/pr-comment-action | |
| with: | |
| pull-request: ${{ steps.resolve-pr.outputs.number }} | |
| environment: ${{ needs.deploy.outputs.environment }} | |
| deployed: ${{ needs.deploy.outputs.deployed }} | |
| failed: ${{ needs.deploy.outputs.failed }} | |
| projects-path: ${{ runner.temp }}/fly-projects/fly-projects.json | |
| token: ${{ steps.generate-token.outputs.token }} | |
| notify: | |
| needs: [pre-deploy, deploy] | |
| if: >- | |
| always() && | |
| needs.deploy.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve PR number | |
| id: resolve-pr | |
| env: | |
| WR_PRS: ${{ toJSON(github.event.workflow_run.pull_requests) }} | |
| DIRECT_PR: ${{ github.event.number }} | |
| run: | | |
| PR=$(echo "$WR_PRS" | jq -r 'first | .number // empty') | |
| echo "number=${PR:-$DIRECT_PR}" >> "$GITHUB_OUTPUT" | |
| - name: Download projects artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: fly-deployed-projects-${{ github.run_id }} | |
| path: ${{ runner.temp }}/fly-projects | |
| continue-on-error: true | |
| - name: Build Slack payload | |
| id: build-payload | |
| env: | |
| ENVIRONMENT: ${{ needs.pre-deploy.outputs.environment }} | |
| DEPLOY_RESULT: ${{ needs.deploy.result }} | |
| PR_NUMBER: ${{ steps.resolve-pr.outputs.number }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| PROJECTS_FILE: ${{ runner.temp }}/fly-projects/fly-projects.json | |
| run: | | |
| STATUS_EMOJI=$([ "$DEPLOY_RESULT" = "success" ] && echo "✅" || echo "❌") | |
| ENV_LABEL=$([ "$ENVIRONMENT" = "production" ] && echo "Production" || echo "Preview") | |
| PR_SUFFIX=$([ -n "$PR_NUMBER" ] && echo " · PR #${PR_NUMBER}" || echo "") | |
| HEADER="${STATUS_EMOJI} ${ENV_LABEL} Deployment${PR_SUFFIX}" | |
| APPS_TEXT="" | |
| if [ -f "$PROJECTS_FILE" ]; then | |
| APPS_TEXT=$(jq -r ' | |
| .[] | | |
| if .action == "deploy" then "✅ *" + .name + "* — <" + .url + "|" + .url + ">" | |
| elif .action == "failed" then "❌ *" + .appOrProject + "* — " + .error | |
| else empty | |
| end | |
| ' "$PROJECTS_FILE") | |
| fi | |
| PAYLOAD=$(jq -nc \ | |
| --arg header "$HEADER" \ | |
| --arg apps "$APPS_TEXT" \ | |
| --arg run_url "$RUN_URL" \ | |
| '{ | |
| "blocks": ( | |
| [{"type":"header","text":{"type":"plain_text","text":$header,"emoji":true}}] | |
| + (if ($apps | length) > 0 then [{"type":"section","text":{"type":"mrkdwn","text":$apps}}] else [] end) | |
| + [{"type":"context","elements":[{"type":"mrkdwn","text":("<" + $run_url + "|View workflow run>")}]}] | |
| ) | |
| }') | |
| echo "payload<<PAYLOAD_EOF" >> "$GITHUB_OUTPUT" | |
| echo "$PAYLOAD" >> "$GITHUB_OUTPUT" | |
| echo "PAYLOAD_EOF" >> "$GITHUB_OUTPUT" | |
| - name: Send Slack notification | |
| uses: slackapi/slack-github-action@v2 | |
| with: | |
| webhook: ${{ secrets.SLACK_WEBHOOK }} | |
| webhook-type: incoming-webhook | |
| errors: true | |
| payload: ${{ steps.build-payload.outputs.payload }} | |
| destroy: | |
| needs: pre-deploy | |
| if: >- | |
| github.event_name == 'pull_request' && github.event.action == 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/create-github-app-token@v1 | |
| id: generate-token | |
| with: | |
| app-id: ${{ secrets.CDWR_ACTIONS_BOT_ID }} | |
| private-key: ${{ secrets.CDWR_ACTIONS_BOT_PRIVATE_KEY }} | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| cache: "pnpm" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build destroy action | |
| run: pnpm nx build fly-destroy-action | |
| - name: Install Fly CLI | |
| uses: superfly/flyctl-actions/setup-flyctl@master | |
| with: | |
| version: ${{ vars.FLY_CLI_VERSION }} | |
| - name: Resolve flags | |
| id: flags | |
| run: | | |
| echo "console-logs=${{ github.event.inputs.console-logs == 'true' && 'true' || vars.FLY_CONSOLE_LOGS }}" >> "$GITHUB_OUTPUT" | |
| - name: Destroy preview apps | |
| id: destroy | |
| uses: ./packages/fly-destroy-action | |
| with: | |
| fly-api-token: ${{ secrets.FLY_API_TOKEN }} | |
| fly-trace-cli: ${{ vars.FLY_TRACE_CLI }} | |
| fly-console-logs: ${{ steps.flags.outputs.console-logs }} | |
| token: ${{ steps.generate-token.outputs.token }} | |
| summary: | |
| needs: | |
| [ | |
| analyze-conditions, | |
| pre-deploy, | |
| build, | |
| deploy, | |
| destroy, | |
| pr-comment, | |
| notify | |
| ] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Deployment Summary | |
| env: | |
| SKIPPED: ${{ needs.analyze-conditions.outputs.skip }} | |
| ENVIRONMENT: ${{ needs.pre-deploy.outputs.environment }} | |
| BUILD_RESULT: ${{ needs.build.result }} | |
| DEPLOY_RESULT: ${{ needs.deploy.result }} | |
| DESTROY_RESULT: ${{ needs.destroy.result }} | |
| IS_CLOSED_PR: | |
| ${{ github.event_name == 'pull_request' && github.event.action == | |
| 'closed' }} | |
| IS_MANUAL: ${{ github.event_name == 'workflow_dispatch' }} | |
| MANUAL_APP: ${{ github.event.inputs.app }} | |
| MANUAL_TENANT: ${{ github.event.inputs.tenant }} | |
| CONSOLE_LOGS: ${{ github.event.inputs.console-logs }} | |
| run: | | |
| if [ "$IS_MANUAL" = "true" ]; then | |
| echo "🔧 Manual deployment triggered" >> $GITHUB_STEP_SUMMARY | |
| [ -n "$MANUAL_APP" ] && echo " - App: $MANUAL_APP" >> $GITHUB_STEP_SUMMARY | |
| [ -n "$MANUAL_TENANT" ] && echo " - Tenant: $MANUAL_TENANT" >> $GITHUB_STEP_SUMMARY | |
| echo " - Environment: $ENVIRONMENT" >> $GITHUB_STEP_SUMMARY | |
| [ "$CONSOLE_LOGS" = "true" ] && echo " - Console logs: enabled" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "$IS_CLOSED_PR" = "true" ]; then | |
| echo "🚪 PR closed - running cleanup" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "$DESTROY_RESULT" = "success" ]; then | |
| echo "✅ Preview apps destroyed successfully" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$DESTROY_RESULT" = "skipped" ]; then | |
| echo "⏭️ No preview apps to destroy" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Preview app cleanup failed or was cancelled" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| exit 0 | |
| fi | |
| if [ "$SKIPPED" = "true" ]; then | |
| echo "⏭️ Deployment skipped - conditions not met" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$ENVIRONMENT" = "" ]; then | |
| echo "⏭️ No affected apps" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$DEPLOY_RESULT" = "success" ]; then | |
| echo "✅ Build and deployment completed successfully" >> $GITHUB_STEP_SUMMARY | |
| elif [ "$DEPLOY_RESULT" = "skipped" ] && [ "$BUILD_RESULT" = "skipped" ]; then | |
| echo "⏭️ No apps to deploy" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Build or deployment failed or was cancelled" >> $GITHUB_STEP_SUMMARY | |
| fi |