Deploying to api, worker, ws, webhook on staging #137
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 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 ' || '' | |
}}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. | |
Additionally, it creates Sentry releases and New Relic deployment markers. | |
on: | |
workflow_dispatch: | |
inputs: | |
environment: | |
description: 'Environment to deploy to' | |
required: true | |
type: choice | |
default: staging | |
options: | |
- staging | |
- production-us | |
- production-eu | |
- production-both | |
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' | |
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 }} | |
steps: | |
- name: Validate Selected Services | |
run: | | |
if [ "${{ github.event.inputs.deploy_api }}" != "true" ] && \ | |
[ "${{ github.event.inputs.deploy_worker }}" != "true" ] && \ | |
[ "${{ github.event.inputs.deploy_ws }}" != "true" ] && \ | |
[ "${{ github.event.inputs.deploy_webhook }}" != "true" ]; then | |
echo "Error: At least one service must be selected for deployment." | |
exit 1 | |
fi | |
- name: Generate Environment, Service, and Deploy Matrices | |
id: set-matrix | |
env: | |
WORKER_SERVICE: ${{ vars.WORKER_SERVICE }} | |
run: | | |
envs=() | |
services=() | |
deploy_matrix=() | |
nr=() | |
# Collect selected environments | |
if [ "${{ github.event.inputs.environment }}" == "staging" ]; then | |
envs+=("\"staging-eu\"") | |
fi | |
if [ "${{ github.event.inputs.environment }}" == "production-us" ]; then | |
envs+=("\"prod-us\"") | |
fi | |
if [ "${{ github.event.inputs.environment }}" == "production-eu" ]; then | |
envs+=("\"prod-eu\"") | |
fi | |
if [ "${{ github.event.inputs.environment }}" == "production-both" ]; then | |
envs+=("\"prod-us\"") | |
envs+=("\"prod-eu\"") | |
fi | |
# Collect selected services | |
if [ "${{ github.event.inputs.deploy_api }}" == "true" ]; then | |
services+=("\"api\"") | |
nr+=("\"api\"") | |
fi | |
if [ "${{ github.event.inputs.deploy_worker }}" == "true" ]; then | |
services+=("\"worker\"") | |
nr+=("\"worker\"") | |
fi | |
if [ "${{ github.event.inputs.deploy_ws }}" == "true" ]; then | |
services+=("\"ws\"") | |
fi | |
if [ "${{ github.event.inputs.deploy_webhook }}" == "true" ]; then | |
services+=("\"webhook\"") | |
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') | |
deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") | |
done | |
elif [ "$service" == "\"api\"" ]; then | |
cluster_name=api-cluster | |
container_name=api-container | |
service_name=api-service | |
task_name=api-task | |
image=api | |
deploy_matrix+=("{\"cluster_name\": \"$cluster_name\", \"container_name\": \"$container_name\", \"service_name\": \"$service_name\", \"task_name\": \"$task_name\", \"image\": \"$image\"}") | |
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\"}") | |
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 | |
build: | |
needs: prepare-matrix | |
timeout-minutes: 60 | |
runs-on: ubuntu-latest | |
environment: ${{ fromJson(needs.prepare-matrix.outputs.env_matrix).environment[0] }} | |
strategy: | |
matrix: | |
service: ${{ fromJson(needs.prepare-matrix.outputs.service_matrix).service }} | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
with: | |
submodules: true | |
fetch-depth: 0 | |
token: ${{ secrets.SUBMODULES_TOKEN }} | |
- name: Install pnpm | |
uses: pnpm/action-setup@v4 | |
with: | |
version: 9.11.0 | |
run_install: false | |
- name: Setup Node Version | |
uses: actions/setup-node@v4 | |
with: | |
node-version: '20.8.1' | |
cache: 'pnpm' | |
- name: Install Dependencies | |
shell: bash | |
run: pnpm install --frozen-lockfile | |
- name: Set Up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
with: | |
driver-opts: 'image=moby/buildkit:v0.13.1' | |
- 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@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID}} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
aws-region: ${{ vars.AWS_REGION }} | |
- name: Login to Amazon ECR | |
id: login-ecr | |
uses: aws-actions/amazon-ecr-login@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: | | |
cp scripts/dotenvcreate.mjs apps/$SERVICE/src/dotenvcreate.mjs | |
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] | |
runs-on: ubuntu-latest | |
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@v4 | |
with: | |
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
aws-region: ${{ vars.AWS_REGION }} | |
- 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 | |
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@v4 | |
- name: Get NPM Version | |
id: package-version | |
uses: martinbeentjes/npm-get-version-action@main | |
with: | |
path: apps/${{ matrix.service }} | |
- name: Create Sentry release | |
uses: getsentry/action-release@v1 | |
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/[email protected] | |
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@v2 | |
with: | |
secret-key: ${{ secrets.NOVU_INTERNAL_SECRET_KEY }} | |
bridge-url: ${{ vars.NOVU_BRIDGE_URL }} |