/
githubmirror
/
novu
Обзор
Документация
Войти
/
githubmirror
/
novu
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
next
.github/workflows/deploy.yml
841 строка
34 KB
Himanshu Garg
chore(root): changes for release and bumping up node to a security release (#12250)
07 авг 2026, 11:06
Не верифицирован
07 авг 2026, 11:06
db33feb
Код
Авторство
О чём код?
name: Deploy to Novu Cloud run-name: > Deploying to ${{ github.event.inputs.deploy_api == 'true' && 'api, ' || '' }}${{ github.event.inputs.deploy_worker == 'true' && 'worker, ' || '' }}${{ github.event.inputs.deploy_ws == 'true' && 'ws, ' || '' }}${{ github.event.inputs.deploy_webhook == 'true' && 'webhook, ' || '' }}${{ github.event.inputs.deploy_inbound_mail == 'true' && 'inbound-mail, ' || '' }}${{ github.event.inputs.deploy_thalamus_observer == 'true' && 'thalamus-observer ' || '' }}on ${{ github.event.inputs.environment }} description: | This workflow deploys the Novu Cloud application to different environments and services based on the selected options. It builds Docker images, pushes them to Amazon ECR, and deploys them to Amazon ECS. Optionally deploys the thalamus-observer Cloudflare Worker via Wrangler. Additionally, it creates Sentry releases and New Relic deployment markers. env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} # One deploy per environment + service combination at a time, so deploys of # unrelated services don't block each other. Staging cancels the in-flight # run so only the latest commit deploys; production queues instead of # cancelling to never interrupt an in-progress production deploy. concurrency: group: >- deploy-${{ github.event.inputs.environment }}${{ github.event.inputs.deploy_api == 'true' && '-api' || '' }}${{ github.event.inputs.deploy_worker == 'true' && '-worker' || '' }}${{ github.event.inputs.deploy_ws == 'true' && '-ws' || '' }}${{ github.event.inputs.deploy_webhook == 'true' && '-webhook' || '' }}${{ github.event.inputs.deploy_inbound_mail == 'true' && '-inbound-mail' || '' }}${{ github.event.inputs.deploy_thalamus_observer == 'true' && '-thalamus-observer' || '' }} cancel-in-progress: ${{ startsWith(github.event.inputs.environment, 'staging') }} on: workflow_dispatch: inputs: environment: description: "Environment to deploy to" required: true type: choice default: staging options: - staging - staging-sg - production-us - production-eu - production-sg - production-au - production-uk - production-jp - production-kr - production-us-and-eu - production-sg-au-uk-jp-kr runner: description: "Build runner" required: true type: choice default: blacksmith options: - blacksmith - github deploy_api: description: "Deploy API" required: true type: boolean default: true deploy_worker: description: "Deploy Worker" required: true type: boolean default: false deploy_ws: description: "Deploy WS" required: true type: boolean default: false deploy_webhook: description: "Deploy Webhook (staging / production-us / production-eu only)" required: true type: boolean default: false deploy_inbound_mail: description: "Deploy Inbound Mail (staging / production-us / production-eu only)" required: true type: boolean default: false deploy_thalamus_observer: description: "Deploy thalamus-observer Cloudflare Worker (staging / production-us / production-us-and-eu only)" required: true type: boolean default: false jobs: prepare-matrix: runs-on: ubuntu-latest outputs: env_matrix: ${{ steps.set-matrix.outputs.env_matrix }} service_matrix: ${{ steps.set-matrix.outputs.service_matrix }} deploy_matrix: ${{ steps.set-matrix.outputs.deploy_matrix }} nr_matrix: ${{ steps.set-matrix.outputs.nr_matrix }} has_ecs_services: ${{ steps.set-matrix.outputs.has_ecs_services }} deploy_thalamus_observer: ${{ steps.set-matrix.outputs.deploy_thalamus_observer }} thalamus_wrangler_env: ${{ steps.set-matrix.outputs.thalamus_wrangler_env }} thalamus_gh_environment: ${{ steps.set-matrix.outputs.thalamus_gh_environment }} steps: - name: Validate Selected Services env: INPUT_ENVIRONMENT: ${{ github.event.inputs.environment }} INPUT_DEPLOY_API: ${{ github.event.inputs.deploy_api }} INPUT_DEPLOY_WORKER: ${{ github.event.inputs.deploy_worker }} INPUT_DEPLOY_WS: ${{ github.event.inputs.deploy_ws }} INPUT_DEPLOY_WEBHOOK: ${{ github.event.inputs.deploy_webhook }} INPUT_DEPLOY_INBOUND_MAIL: ${{ github.event.inputs.deploy_inbound_mail }} INPUT_DEPLOY_THALAMUS_OBSERVER: ${{ github.event.inputs.deploy_thalamus_observer }} run: | if [ "$INPUT_DEPLOY_API" != "true" ] && \ [ "$INPUT_DEPLOY_WORKER" != "true" ] && \ [ "$INPUT_DEPLOY_WS" != "true" ] && \ [ "$INPUT_DEPLOY_WEBHOOK" != "true" ] && \ [ "$INPUT_DEPLOY_INBOUND_MAIL" != "true" ] && \ [ "$INPUT_DEPLOY_THALAMUS_OBSERVER" != "true" ]; then echo "Error: At least one service must be selected for deployment." exit 1 fi # webhook and inbound-mail only have infra in staging-eu, prod-us, prod-eu. # Fail fast if someone selects them with an unsupported environment. if [ "$INPUT_DEPLOY_WEBHOOK" = "true" ] || \ [ "$INPUT_DEPLOY_INBOUND_MAIL" = "true" ]; then case "$INPUT_ENVIRONMENT" in staging|production-us|production-eu|production-us-and-eu) ;; *) echo "Error: webhook and inbound-mail can only deploy to staging, production-us, production-eu, or production-us-and-eu." echo "Selected environment: $INPUT_ENVIRONMENT" exit 1 ;; esac fi # thalamus-observer is a single CF Worker (staging + one production). if [ "$INPUT_DEPLOY_THALAMUS_OBSERVER" = "true" ]; then case "$INPUT_ENVIRONMENT" in staging|production-us|production-us-and-eu) ;; *) echo "Error: thalamus-observer can only deploy to staging, production-us, or production-us-and-eu." echo "Selected environment: $INPUT_ENVIRONMENT" exit 1 ;; esac fi - name: Generate Environment, Service, and Deploy Matrices id: set-matrix env: WORKER_SERVICE: ${{ vars.WORKER_SERVICE }} API_SERVICE: ${{ vars.API_SERVICE }} INPUT_ENVIRONMENT: ${{ github.event.inputs.environment }} INPUT_DEPLOY_API: ${{ github.event.inputs.deploy_api }} INPUT_DEPLOY_WORKER: ${{ github.event.inputs.deploy_worker }} INPUT_DEPLOY_WS: ${{ github.event.inputs.deploy_ws }} INPUT_DEPLOY_WEBHOOK: ${{ github.event.inputs.deploy_webhook }} INPUT_DEPLOY_INBOUND_MAIL: ${{ github.event.inputs.deploy_inbound_mail }} INPUT_DEPLOY_THALAMUS_OBSERVER: ${{ github.event.inputs.deploy_thalamus_observer }} run: | envs=() services=() deploy_matrix=() nr=() has_ecs_services=false deploy_thalamus_observer=false thalamus_wrangler_env="" thalamus_gh_environment="" # Collect selected environments if [ "$INPUT_ENVIRONMENT" == "staging" ]; then envs+=("\"staging-eu\"") fi if [ "$INPUT_ENVIRONMENT" == "staging-sg" ]; then envs+=("\"staging-apse1\"") fi if [ "$INPUT_ENVIRONMENT" == "production-us" ]; then envs+=("\"prod-us\"") fi if [ "$INPUT_ENVIRONMENT" == "production-eu" ]; then envs+=("\"prod-eu\"") fi if [ "$INPUT_ENVIRONMENT" == "production-sg" ]; then envs+=("\"prod-apse1\"") fi if [ "$INPUT_ENVIRONMENT" == "production-au" ]; then envs+=("\"prod-apse2\"") fi if [ "$INPUT_ENVIRONMENT" == "production-uk" ]; then envs+=("\"prod-ew2\"") fi if [ "$INPUT_ENVIRONMENT" == "production-jp" ]; then envs+=("\"prod-apne1\"") fi if [ "$INPUT_ENVIRONMENT" == "production-kr" ]; then envs+=("\"prod-apne2\"") fi if [ "$INPUT_ENVIRONMENT" == "production-us-and-eu" ]; then envs+=("\"prod-us\"") envs+=("\"prod-eu\"") fi if [ "$INPUT_ENVIRONMENT" == "production-sg-au-uk-jp-kr" ]; then envs+=("\"prod-apse1\"") envs+=("\"prod-apse2\"") envs+=("\"prod-ew2\"") envs+=("\"prod-apne1\"") envs+=("\"prod-apne2\"") fi # Collect selected services if [ "$INPUT_DEPLOY_API" == "true" ]; then services+=("\"api\"") nr+=("\"api\"") fi if [ "$INPUT_DEPLOY_WORKER" == "true" ]; then services+=("\"worker\"") nr+=("\"worker\"") fi if [ "$INPUT_DEPLOY_WS" == "true" ]; then services+=("\"ws\"") fi if [ "$INPUT_DEPLOY_WEBHOOK" == "true" ]; then services+=("\"webhook\"") fi if [ "$INPUT_DEPLOY_INBOUND_MAIL" == "true" ]; then services+=("\"inbound-mail\"") fi if [ ${#services[@]} -gt 0 ]; then has_ecs_services=true fi if [ "$INPUT_DEPLOY_THALAMUS_OBSERVER" == "true" ]; then deploy_thalamus_observer=true if [ "$INPUT_ENVIRONMENT" == "staging" ]; then thalamus_wrangler_env="staging" thalamus_gh_environment="staging-eu" else # production-us and production-us-and-eu share one CF production Worker thalamus_wrangler_env="production" thalamus_gh_environment="prod-us" fi fi # Parse service secrets and generate deploy_matrix for service in "${services[@]}"; do if [ "$service" == "\"worker\"" ]; then IFS=',' read -r -a worker_services <<< "$WORKER_SERVICE" for worker_service in $(echo "$WORKER_SERVICE" | jq -c '.[]'); do cluster_name=$(echo "$worker_service" | jq -r '.cluster_name') container_name=$(echo "$worker_service" | jq -r '.container_name') service_name=$(echo "$worker_service" | jq -r '.service') task_name=$(echo "$worker_service" | jq -r '.task_name') image=$(echo "$worker_service" | jq -r '.image') # Check if service has environments filter, otherwise deploy to all allowed_envs=$(echo "$worker_service" | jq -r '.environments // empty') should_deploy=false if [ -z "$allowed_envs" ]; then # No environment filter, deploy to all environments should_deploy=true else # Check if any of the selected environments match the allowed environments for env in "${envs[@]}"; do env_clean=$(echo "$env" | tr -d '"') if echo "$allowed_envs" | jq -e --arg env "$env_clean" 'index($env) != null' > /dev/null; then should_deploy=true break fi done fi if [ "$should_deploy" == "true" ]; then deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") fi done elif [ "$service" == "\"api\"" ]; then for api_service in $(echo "$API_SERVICE" | jq -c '.[]'); do cluster_name=$(echo "$api_service" | jq -r '.cluster_name') container_name=$(echo "$api_service" | jq -r '.container_name') service_name=$(echo "$api_service" | jq -r '.service') task_name=$(echo "$api_service" | jq -r '.task_name') image=$(echo "$api_service" | jq -r '.image') # Check if service has environments filter, otherwise deploy to all allowed_envs=$(echo "$api_service" | jq -r '.environments // empty') should_deploy=false if [ -z "$allowed_envs" ]; then # No environment filter, deploy to all environments should_deploy=true else # Check if any of the selected environments match the allowed environments for env in "${envs[@]}"; do env_clean=$(echo "$env" | tr -d '"') if echo "$allowed_envs" | jq -e --arg env "$env_clean" 'index($env) != null' > /dev/null; then should_deploy=true break fi done fi if [ "$should_deploy" == "true" ]; then deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") fi done elif [ "$service" == "\"ws\"" ]; then cluster_name=ws-cluster container_name=ws-container service_name=ws-service task_name=ws-task image=ws deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") elif [ "$service" == "\"webhook\"" ]; then cluster_name=webhook-cluster container_name=webhook-container service_name=webhook-service task_name=webhook-task image=webhook deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") elif [ "$service" == "\"inbound-mail\"" ]; then cluster_name=inbound-mail-cluster container_name=inbound-mail-container service_name=inbound-mail-service task_name=inbound-mail-task image=inbound-mail deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") fi done env_matrix="{\"environment\": [$( IFS=','; echo "${envs[*]}" )]}" service_matrix="{\"service\": [$( IFS=','; echo "${services[*]}" )]}" deploy_matrix="[$( IFS=','; echo "${deploy_matrix[*]}" )]" nr_matrix="[$( IFS=','; echo "${nr[*]}" )]" echo "env_matrix=$env_matrix" >> $GITHUB_OUTPUT echo "service_matrix=$service_matrix" >> $GITHUB_OUTPUT echo "deploy_matrix=$deploy_matrix" >> $GITHUB_OUTPUT echo "nr_matrix=$nr_matrix" >> $GITHUB_OUTPUT echo "has_ecs_services=$has_ecs_services" >> $GITHUB_OUTPUT echo "deploy_thalamus_observer=$deploy_thalamus_observer" >> $GITHUB_OUTPUT echo "thalamus_wrangler_env=$thalamus_wrangler_env" >> $GITHUB_OUTPUT echo "thalamus_gh_environment=$thalamus_gh_environment" >> $GITHUB_OUTPUT run-clickhouse-migrations: needs: prepare-matrix if: needs.prepare-matrix.outputs.has_ecs_services == 'true' runs-on: ubuntu-latest strategy: matrix: env: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment }} environment: ${{ matrix.env }} steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: submodules: true fetch-depth: 0 token: ${{ secrets.SUBMODULES_TOKEN }} - name: Whitelist Runner IP id: whitelist uses: novuhq/clickhouse-cloud-whitelist-ip-action@7594062e821f1f28af7673bf82e4df9ca2e78636 # v1.0.0 with: clickhouse-org-id: ${{ secrets.CLICKHOUSE_ORG_ID }} clickhouse-service-id: ${{ secrets.CLICKHOUSE_SERVICE_ID }} clickhouse-api-key-id: ${{ secrets.CLICKHOUSE_API_KEY_ID }} clickhouse-api-key-secret: ${{ secrets.CLICKHOUSE_API_KEY_SECRET }} - name: Install pnpm uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: version: 11.0.9 run_install: false - name: Setup Node Version uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22.23.0" cache: "pnpm" - name: Install Dependencies shell: bash run: pnpm ci - name: Run ClickHouse Migrations working-directory: apps/api env: CH_MIGRATIONS_HOST: ${{ secrets.CLICK_HOUSE_URL }} CH_MIGRATIONS_USER: ${{ secrets.CLICK_HOUSE_USER }} CH_MIGRATIONS_PASSWORD: ${{ secrets.CLICK_HOUSE_PASSWORD }} CH_MIGRATIONS_DB: ${{ secrets.CLICK_HOUSE_DATABASE }} run: pnpm run clickhouse:migrate:prod build: needs: [prepare-matrix, run-clickhouse-migrations] if: needs.prepare-matrix.outputs.has_ecs_services == 'true' timeout-minutes: 60 runs-on: ${{ github.event.inputs.runner == 'github' && 'ubuntu-latest' || 'blacksmith-8vcpu-ubuntu-2404' }} environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} permissions: contents: read id-token: write strategy: matrix: service: ${{ fromJson(needs.prepare-matrix.outputs.service_matrix).service }} steps: - name: Checkout uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: submodules: true fetch-depth: 0 token: ${{ secrets.SUBMODULES_TOKEN }} - name: Install pnpm uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: version: 11.0.9 run_install: false - name: Setup Node Version uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22.23.0" cache: "pnpm" - name: Install Dependencies shell: bash run: pnpm ci - name: Set Up Docker Buildx (Blacksmith) if: github.event.inputs.runner != 'github' uses: useblacksmith/setup-docker-builder@a592b831ebb20e68f7cf47329cf2c3c67b8a7655 # v1 - name: Set Up Docker Buildx (GitHub) if: github.event.inputs.runner == 'github' uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Prepare Variables run: echo "BULL_MQ_PRO_NPM_TOKEN=${{ secrets.BULL_MQ_PRO_NPM_TOKEN }}" >> $GITHUB_ENV - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 with: role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} aws-region: ${{ vars.AWS_REGION }} role-session-name: gha-build-${{ github.run_id }} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@fa648b43de3d4d023bcb3f89ed6940096949c419 # v2 - name: Build, tag, and push image to Amazon ECR id: build-image env: REGISTRY: ${{ steps.login-ecr.outputs.registry }} REPOSITORY: ${{ vars.ECR_PREFIX }} SERVICE: ${{ matrix.service }} IMAGE_TAG: ${{ github.sha }} DOCKER_BUILD_ARGUMENTS: > --platform=linux/amd64 --output=type=image,name=$REGISTRY/$REPOSITORY/$SERVICE,push-by-digest=true,name-canonical=true run: | cd apps/$SERVICE && pnpm run docker:build docker tag novu-$SERVICE $REGISTRY/$REPOSITORY/$SERVICE:latest docker tag novu-$SERVICE $REGISTRY/$REPOSITORY/$SERVICE:$IMAGE_TAG docker push $REGISTRY/$REPOSITORY/$SERVICE:latest docker push $REGISTRY/$REPOSITORY/$SERVICE:$IMAGE_TAG deploy: needs: [build, prepare-matrix, run-clickhouse-migrations] if: needs.prepare-matrix.outputs.has_ecs_services == 'true' runs-on: ubuntu-latest permissions: contents: read id-token: write strategy: matrix: env: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment }} service: ${{ fromJson(needs.prepare-matrix.outputs.deploy_matrix) }} environment: ${{ matrix.env }} steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4 with: role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} aws-region: ${{ vars.AWS_REGION }} role-session-name: gha-deploy-${{ github.run_id }} - name: Download task definition env: ECS_PREFIX: ${{ vars.ECS_PREFIX }} TASK_NAME: ${{ matrix.service.task_name }} run: | aws ecs describe-task-definition --task-definition ${ECS_PREFIX}-${TASK_NAME} \ --query taskDefinition > task-definition.json - name: Render Amazon ECS task definition id: render-web-container uses: aws-actions/amazon-ecs-render-task-definition@39c13cf530718ffeb524ec8ee0c15882bcb13842 with: task-definition: task-definition.json container-name: ${{ vars.ECS_PREFIX }}-${{ matrix.service.container_name }} image: ${{secrets.ECR_URI}}/${{ vars.ECR_PREFIX }}/${{ matrix.service.image }}:${{ github.sha }} - name: Deploy to Amazon ECS service uses: aws-actions/amazon-ecs-deploy-task-definition@3e7310352de91b71a906e60c22af629577546002 with: task-definition: ${{ steps.render-web-container.outputs.task-definition }} service: ${{ vars.ECS_PREFIX }}-${{ matrix.service.service_name }} cluster: ${{ vars.ECS_PREFIX }}-${{ matrix.service.cluster_name }} wait-for-service-stability: true deploy_thalamus_observer: needs: prepare-matrix if: needs.prepare-matrix.outputs.deploy_thalamus_observer == 'true' name: Deploy thalamus-observer (${{ needs.prepare-matrix.outputs.thalamus_wrangler_env }}) runs-on: ubuntu-latest timeout-minutes: 15 environment: ${{ needs.prepare-matrix.outputs.thalamus_gh_environment }} permissions: contents: read steps: - name: Checkout uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '20' - name: Install dependencies working-directory: enterprise/workers/thalamus-observer run: npm install - name: Deploy with Wrangler working-directory: enterprise/workers/thalamus-observer env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} run: | npx wrangler deploy \ --env "${{ needs.prepare-matrix.outputs.thalamus_wrangler_env }}" \ --message "GitHub Actions" sentry_release: needs: [deploy, prepare-matrix] runs-on: ubuntu-latest strategy: matrix: service: ${{ fromJson(needs.prepare-matrix.outputs.service_matrix).service }} environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} steps: - name: Checkout uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - name: Get NPM Version id: package-version uses: martinbeentjes/npm-get-version-action@3cf273023a0dda27efcd3164bdfb51908dd46a5b # main with: path: apps/${{ matrix.service }} - name: Create Sentry release uses: getsentry/action-release@5657c9e888b4e2cc85f4d29143ea4131fde4a73a # v3.6.0 env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} SENTRY_PROJECT: ${{ matrix.service }} with: version: "${{ github.sha }}" version_prefix: v environment: ${{vars.SENTRY_ENV}} ignore_empty: true ignore_missing: true new_relic_release: needs: [deploy, prepare-matrix] if: ${{ fromJson(needs.prepare-matrix.outputs.nr_matrix) != '[]' }} runs-on: ubuntu-latest strategy: matrix: env: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment }} nr: ${{ fromJson(needs.prepare-matrix.outputs.nr_matrix) }} environment: ${{ matrix.env }} steps: - name: New Relic Application Deployment Marker uses: newrelic/deployment-marker-action@8c1d1c009e7ee18ed2bda1e068d59867fdaa91c4 # v2.3.0 with: region: EU apiKey: ${{ secrets.NEW_RELIC_API_KEY }} guid: ${{ matrix.nr == 'api' && secrets.NEW_RELIC_API_GUID || matrix.nr == 'worker' && secrets.NEW_RELIC_Worker_GUID }} version: "${{ github.sha }}" user: "${{ github.actor }}" description: "Novu Cloud Deployment" sync_novu_state: needs: [deploy, prepare-matrix] runs-on: ubuntu-latest if: github.event.inputs.deploy_api == 'true' environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} steps: - name: Sync State to Novu uses: novuhq/actions-novu-sync@dceec32b0c93c5d7f741e38094204fd5ebd9f308 # v2 with: secret-key: ${{ secrets.NOVU_INTERNAL_SECRET_KEY }} bridge-url: ${{ vars.NOVU_BRIDGE_URL }} webhook_notification: needs: [deploy, prepare-matrix] runs-on: ubuntu-latest if: | always() && needs.deploy.result == 'success' && (contains(github.event.inputs.environment, 'production')) environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} steps: - name: Send webhook notification for US production if: | github.event.inputs.environment == 'production-us' || github.event.inputs.environment == 'production-us-and-eu' run: | curl -X POST https://webhooks.bug0.com/integrations/test/run \ -H "Content-Type: application/json" \ -H "x-api-key: ${{ secrets.BUG0_SECRET_KEY }}" \ -d '{"url": "https://dashboard.novu.co", "source": "novuhq-novu", "prod": "true"}' - name: Send webhook notification for EU production if: | github.event.inputs.environment == 'production-eu' || github.event.inputs.environment == 'production-us-and-eu' run: | curl -X POST https://webhooks.bug0.com/integrations/test/run \ -H "Content-Type: application/json" \ -H "x-api-key: ${{ secrets.BUG0_SECRET_KEY }}" \ -d '{"url": "https://eu.dashboard.novu.co", "source": "novuhq-novu", "prod": "true"}' linear_release_cloud: # Cloud uses two continuous Linear pipelines: staging and production. # Each successful deploy syncs a completed release to the matching pipeline. # # The release `name` is date-based (`YYYY-MM-DD HH:MM UTC · <environment>`), # so releases are easy to scan in the Linear UI. The commit SHA is preserved # on the release via the `version` field (the Linear Release Action has no # `description` input — see https://github.com/linear/linear-release-action). name: Linear Release (Cloud) needs: [deploy, prepare-matrix] runs-on: ubuntu-latest if: | always() && needs.deploy.result == 'success' && ( github.event.inputs.environment == 'staging' || github.event.inputs.environment == 'staging-sg' || contains(github.event.inputs.environment, 'production') ) permissions: contents: read steps: - name: Checkout uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - name: Compute date-based release name id: release_meta env: DEPLOY_ENVIRONMENT: ${{ github.event.inputs.environment }} run: | release_name="$(date -u +'%Y-%m-%d %H:%M UTC') · ${DEPLOY_ENVIRONMENT}" echo "name=${release_name}" >> "$GITHUB_OUTPUT" - name: Sync staging release to Linear if: | github.event.inputs.environment == 'staging' || github.event.inputs.environment == 'staging-sg' uses: linear/linear-release-action@0353b5fa8c00326913966f00557d68f8f30b8b6b # pinned v0 ref with: access_key: ${{ secrets.LINEAR_ACCESS_KEY_CLOUD_STAGING }} name: ${{ steps.release_meta.outputs.name }} version: ${{ github.sha }} - name: Sync production release to Linear if: contains(github.event.inputs.environment, 'production') uses: linear/linear-release-action@0353b5fa8c00326913966f00557d68f8f30b8b6b # pinned v0 ref with: access_key: ${{ secrets.LINEAR_ACCESS_KEY_CLOUD_PROD }} name: ${{ steps.release_meta.outputs.name }} version: ${{ github.sha }} testerarmy_staging_regression: name: TesterArmy Staging Regression needs: [deploy, prepare-matrix] runs-on: ubuntu-latest environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} if: | always() && needs.deploy.result == 'success' && ( github.event.inputs.environment == 'staging' || github.event.inputs.environment == 'staging-sg' ) steps: - name: Trigger TesterArmy staging regression tests env: TESTERARMY_STAGING_WEBHOOK_URL: ${{ secrets.TESTERARMY_STAGING_WEBHOOK_URL }} COMMIT_SHA: ${{ github.sha }} run: | set -euo pipefail if [ -z "$TESTERARMY_STAGING_WEBHOOK_URL" ]; then echo "TESTERARMY_STAGING_WEBHOOK_URL is not configured, skipping regression trigger." exit 0 fi payload=$(jq -n \ --arg commitSha "$COMMIT_SHA" \ --arg targetUrl "https://dashboard.novu-staging.co" \ --arg environment "staging" \ '{commitSha: $commitSha, targetUrl: $targetUrl, environment: $environment}') response=$(curl -sS -w "\n%{http_code}" -X POST "$TESTERARMY_STAGING_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$payload") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') echo "TesterArmy response (HTTP ${http_code}): ${body}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "Failed to trigger TesterArmy staging regression tests." exit 1 fi testerarmy_production_regression: name: TesterArmy Production Regression needs: [deploy, prepare-matrix] runs-on: ubuntu-latest environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} if: | always() && needs.deploy.result == 'success' && (contains(github.event.inputs.environment, 'production')) steps: - name: Trigger TesterArmy US production regression tests if: | github.event.inputs.environment == 'production-us' || github.event.inputs.environment == 'production-us-and-eu' env: TESTERARMY_PROD_WEBHOOK_URL: ${{ secrets.TESTERARMY_PROD_WEBHOOK_URL }} COMMIT_SHA: ${{ github.sha }} run: | set -euo pipefail if [ -z "$TESTERARMY_PROD_WEBHOOK_URL" ]; then echo "TESTERARMY_PROD_WEBHOOK_URL is not configured, skipping US production regression trigger." exit 0 fi payload=$(jq -n \ --arg commitSha "$COMMIT_SHA" \ --arg targetUrl "https://dashboard.novu.co" \ --arg environment "production" \ '{commitSha: $commitSha, targetUrl: $targetUrl, environment: $environment}') response=$(curl -sS -w "\n%{http_code}" -X POST "$TESTERARMY_PROD_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$payload") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') echo "TesterArmy US production response (HTTP ${http_code}): ${body}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "Failed to trigger TesterArmy US production regression tests." exit 1 fi - name: Trigger TesterArmy EU production regression tests if: | github.event.inputs.environment == 'production-eu' || github.event.inputs.environment == 'production-us-and-eu' env: TESTERARMY_PROD_WEBHOOK_URL: ${{ secrets.TESTERARMY_PROD_WEBHOOK_URL }} COMMIT_SHA: ${{ github.sha }} run: | set -euo pipefail if [ -z "$TESTERARMY_PROD_WEBHOOK_URL" ]; then echo "TESTERARMY_PROD_WEBHOOK_URL is not configured, skipping EU production regression trigger." exit 0 fi payload=$(jq -n \ --arg commitSha "$COMMIT_SHA" \ --arg targetUrl "https://eu.dashboard.novu.co" \ --arg environment "production" \ '{commitSha: $commitSha, targetUrl: $targetUrl, environment: $environment}') response=$(curl -sS -w "\n%{http_code}" -X POST "$TESTERARMY_PROD_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$payload") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') echo "TesterArmy EU production response (HTTP ${http_code}): ${body}" if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then echo "Failed to trigger TesterArmy EU production regression tests." exit 1 fi