DEPLOY — Deploy Branch #96
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 — Deploy Branch | |
| on: | |
| workflow_call: | |
| inputs: | |
| environment: | |
| description: "Target — Environment (used for Environment-scoped vars/secrets)" | |
| required: true | |
| type: string | |
| component: | |
| description: "Target — Component to deploy (triggers the matching webhook list)" | |
| required: true | |
| type: string | |
| confirm: | |
| description: "Confirm — Choose the exact deploy action (mobile-friendly)" | |
| required: true | |
| type: string | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: "Target — Environment (used for Environment-scoped vars/secrets)" | |
| required: true | |
| type: choice | |
| default: production | |
| options: | |
| - production | |
| - preview | |
| component: | |
| description: "Target — Component to deploy (triggers the matching webhook list)" | |
| required: true | |
| type: choice | |
| options: | |
| - ui | |
| - server | |
| - website | |
| - docs | |
| confirm: | |
| description: "Confirm — Choose the exact deploy action (mobile-friendly)" | |
| required: true | |
| type: choice | |
| options: | |
| - deploy production ui | |
| - deploy production server | |
| - deploy production website | |
| - deploy production docs | |
| - deploy preview ui | |
| - deploy preview server | |
| - deploy preview website | |
| - deploy preview docs | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: deploy-${{ format('{0}/{1}', inputs.environment, inputs.component) }} | |
| cancel-in-progress: false | |
| jobs: | |
| release_actor_guard: | |
| name: Release actor guard | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Authorize release actor | |
| uses: ./.github/actions/release-actor-guard | |
| with: | |
| team_slug: release-admins | |
| trusted_actors: happier-release-bot[bot] | |
| app_id: ${{ secrets.RELEASE_BOT_APP_ID }} | |
| private_key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }} | |
| deploy: | |
| needs: [release_actor_guard] | |
| runs-on: ubuntu-latest | |
| if: ${{ needs.release_actor_guard.result == 'success' }} | |
| environment: ${{ inputs.environment }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22.x | |
| - name: Enforce trusted refs for manual dispatch | |
| run: | | |
| set -euo pipefail | |
| if [ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ]; then | |
| exit 0 | |
| fi | |
| case "${GITHUB_REF_NAME}" in | |
| dev|preview|main) ;; | |
| *) | |
| echo "Refusing workflow_dispatch from untrusted ref '${GITHUB_REF_NAME}'. Use dev, preview, or main." >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Resolve target (env + component) | |
| id: target | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| INPUT_ENV: ${{ inputs.environment }} | |
| INPUT_COMPONENT: ${{ inputs.component }} | |
| INPUT_CONFIRM: ${{ inputs.confirm }} | |
| run: | | |
| set -euo pipefail | |
| # Called workflows inherit `github.event_name` from the caller (e.g. `push`), so we must accept it here. | |
| if [ "$EVENT_NAME" != "workflow_dispatch" ] && [ "$EVENT_NAME" != "workflow_call" ] && [ "$EVENT_NAME" != "push" ]; then | |
| echo "Unsupported event for deploy workflow: $EVENT_NAME" >&2 | |
| exit 1 | |
| fi | |
| env_name="$INPUT_ENV" | |
| component="$INPUT_COMPONENT" | |
| expected="deploy ${env_name} ${component}" | |
| if [ "$INPUT_CONFIRM" != "$expected" ]; then | |
| echo "Confirmation mismatch." >&2 | |
| echo "Expected: $expected" >&2 | |
| echo "Got: $INPUT_CONFIRM" >&2 | |
| exit 1 | |
| fi | |
| case "$env_name" in | |
| production|preview) ;; | |
| *) | |
| echo "Unsupported environment: $env_name" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| case "$component" in | |
| ui|server|website|docs) ;; | |
| *) | |
| echo "Unsupported component: $component" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "environment=$env_name" >> "$GITHUB_OUTPUT" | |
| echo "component=$component" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "## Deploy trigger" | |
| echo "" | |
| echo "- ref: \`${GITHUB_REF_NAME}\`" | |
| echo "- environment: \`$env_name\`" | |
| echo "- component: \`$component\`" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Trigger deploy webhook(s) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| CF_WEBHOOK_DEPLOY_CLIENT_ID: ${{ secrets.CF_WEBHOOK_DEPLOY_CLIENT_ID }} | |
| CF_WEBHOOK_DEPLOY_CLIENT_SECRET: ${{ secrets.CF_WEBHOOK_DEPLOY_CLIENT_SECRET }} | |
| DEPLOY_WEBHOOK_URL: ${{ vars.DEPLOY_WEBHOOK_URL != '' && vars.DEPLOY_WEBHOOK_URL || secrets.DEPLOY_WEBHOOK_URL }} | |
| HAPPIER_UI_DEPLOY_WEBHOOKS: ${{ vars.HAPPIER_UI_DEPLOY_WEBHOOKS != '' && vars.HAPPIER_UI_DEPLOY_WEBHOOKS || secrets.HAPPIER_UI_DEPLOY_WEBHOOKS }} | |
| HAPPIER_WEBSITE_DEPLOY_WEBHOOKS: ${{ vars.HAPPIER_WEBSITE_DEPLOY_WEBHOOKS != '' && vars.HAPPIER_WEBSITE_DEPLOY_WEBHOOKS || secrets.HAPPIER_WEBSITE_DEPLOY_WEBHOOKS }} | |
| HAPPIER_DOCS_DEPLOY_WEBHOOKS: ${{ vars.HAPPIER_DOCS_DEPLOY_WEBHOOKS != '' && vars.HAPPIER_DOCS_DEPLOY_WEBHOOKS || secrets.HAPPIER_DOCS_DEPLOY_WEBHOOKS }} | |
| HAPPIER_SERVER_API_DEPLOY_WEBHOOKS: ${{ vars.HAPPIER_SERVER_API_DEPLOY_WEBHOOKS != '' && vars.HAPPIER_SERVER_API_DEPLOY_WEBHOOKS || secrets.HAPPIER_SERVER_API_DEPLOY_WEBHOOKS }} | |
| HAPPIER_SERVER_WORKER_DEPLOY_WEBHOOKS: ${{ vars.HAPPIER_SERVER_WORKER_DEPLOY_WEBHOOKS != '' && vars.HAPPIER_SERVER_WORKER_DEPLOY_WEBHOOKS || secrets.HAPPIER_SERVER_WORKER_DEPLOY_WEBHOOKS }} | |
| run: | | |
| set -euo pipefail | |
| node scripts/pipeline/run.mjs deploy \ | |
| --deploy-environment "${{ steps.target.outputs.environment }}" \ | |
| --component "${{ steps.target.outputs.component }}" \ | |
| --repository "${{ github.repository }}" \ | |
| --ref-name "deploy/${{ steps.target.outputs.environment }}/${{ steps.target.outputs.component }}" |