Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ci): Instant rollback process #5460

Merged
merged 52 commits into from May 8, 2024
Merged
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6084205
fix(ci): Get back after workflow creation in the list
AliaksandrRyzhou Apr 26, 2024
1d85036
fix(ci): Condition fix
AliaksandrRyzhou Apr 26, 2024
82c7d76
fix(ci): working dir for one step has been deleted
AliaksandrRyzhou Apr 26, 2024
5a4467d
fix(ci): Token update
AliaksandrRyzhou Apr 26, 2024
1c48f19
fix(ci): Token update
AliaksandrRyzhou Apr 26, 2024
98a07e5
fix(ci): Token update
AliaksandrRyzhou Apr 26, 2024
bd0884e
fix(ci): Token update
AliaksandrRyzhou Apr 26, 2024
8a639a1
fix(ci): + environment
AliaksandrRyzhou Apr 26, 2024
d54d487
fix(ci): fix environment
AliaksandrRyzhou Apr 26, 2024
a0454df
fix(ci): fix environment
AliaksandrRyzhou Apr 26, 2024
9a238d2
fix(ci): Environment name
AliaksandrRyzhou Apr 26, 2024
0e05f33
fix(ci): Environment name fix
AliaksandrRyzhou Apr 26, 2024
b81f723
fix(ci): Environment Development
AliaksandrRyzhou Apr 26, 2024
31d33ac
fix(ci): Inbound_mail service name has been changed
AliaksandrRyzhou Apr 26, 2024
c439ee0
fix(ci): Inbound_mail service name has been changed
AliaksandrRyzhou Apr 26, 2024
91a8e5e
fix(ci): Variables have been updated
AliaksandrRyzhou Apr 26, 2024
3c7f83e
fix(ci): Variable previous_task_definition_arn to GITHUB_ENV
AliaksandrRyzhou Apr 26, 2024
c882e72
fix(ci): Variable update
AliaksandrRyzhou Apr 26, 2024
3c151ef
fix(ci): Variables restructure
AliaksandrRyzhou Apr 26, 2024
4ea1b1f
fix(ci): Debug mode
AliaksandrRyzhou Apr 26, 2024
8b70424
fix(ci): List fix
AliaksandrRyzhou Apr 26, 2024
36a64de
fix(ci): Refactor
AliaksandrRyzhou Apr 26, 2024
c45d802
fix(ci): Cut extra word
AliaksandrRyzhou Apr 26, 2024
9a76f3a
fix(ci): Cut extra word
AliaksandrRyzhou Apr 26, 2024
891e2d6
fix(ci): Variables fix
AliaksandrRyzhou Apr 26, 2024
6710b00
fix(ci): Indexing
AliaksandrRyzhou Apr 26, 2024
efec9a7
fix(ci): Indexing from 0
AliaksandrRyzhou Apr 26, 2024
6200af3
fix(ci): Indexing from 0
AliaksandrRyzhou Apr 26, 2024
357b670
fix(ci): Fix logic
AliaksandrRyzhou Apr 26, 2024
c13e194
fix(ci): Fix logic
AliaksandrRyzhou Apr 26, 2024
552c869
fix(ci): Error description
AliaksandrRyzhou Apr 29, 2024
d17feee
fix(ci): Syntax fix
AliaksandrRyzhou Apr 29, 2024
6e80520
fix(ci): Exclude |
AliaksandrRyzhou Apr 29, 2024
e944182
fix(ci): Workflow name
AliaksandrRyzhou Apr 29, 2024
fee192d
fix(ci): Workflow name
AliaksandrRyzhou Apr 29, 2024
1fef2ed
feat(ci): Matrix for both regions support
AliaksandrRyzhou Apr 29, 2024
5645e13
feat(ci): Matrix for both regions support
AliaksandrRyzhou Apr 29, 2024
dc2bf6b
feat(ci): New setting to use hash commit
AliaksandrRyzhou Apr 30, 2024
e0780b9
feat(ci): Fix ID of step
AliaksandrRyzhou Apr 30, 2024
a575b6e
feat(ci): Syntax fix
AliaksandrRyzhou Apr 30, 2024
0faa655
feat(ci): Netlify rollback job
AliaksandrRyzhou Apr 30, 2024
6caae85
feat(ci): Netlify rollback job by using a commit hash
AliaksandrRyzhou Apr 30, 2024
d93893c
feat(ci): Extra step has been deleted
AliaksandrRyzhou Apr 30, 2024
ada07e7
feat(ci): Condition fix
AliaksandrRyzhou Apr 30, 2024
b45421e
feat(ci): New id step
AliaksandrRyzhou Apr 30, 2024
7533afa
Merge branch 'next' into inf-376-instant-rollback-process
AliaksandrRyzhou May 2, 2024
566e046
feat(ci): Error message
AliaksandrRyzhou May 2, 2024
6359440
feat(ci): Exit 1 has been deleted
AliaksandrRyzhou May 2, 2024
ed73aa1
feat(ci): Comments have been deleted
AliaksandrRyzhou May 2, 2024
38cbb0c
feat(ci): Logic fix
AliaksandrRyzhou May 2, 2024
76a2521
feat(ci): The changes after comments
AliaksandrRyzhou May 6, 2024
918faa4
Merge branch 'next' into inf-376-instant-rollback-process
AliaksandrRyzhou May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
278 changes: 277 additions & 1 deletion .github/workflows/rollback.yml
@@ -1,11 +1,287 @@
name: Rollback
run-name: Rollback the ${{ inputs.service }} service in the ${{ inputs.environment }} environment

on:
workflow_dispatch:
inputs:
service:
type: choice
description: Select service to rollback.
options:
- inbound_mail
- api
- web
- webhook
- widget
- worker
- ws
environment:
type: choice
description: Select the environment
options:
- Production
- Development
region:
type: choice
description: Select the environment region. Required only in production.
options:
- [EU,US]
- [EU]
- [US]
mode:
type: choice
description: The Rollback mode. You can roll back to the previously deployed version or to the version that has the current commit hash of this branch in an image tag name or a deployment info.
options:
- Previous Version
- Commit Hash

jobs:
ecs:
if: contains(fromJson('["api", "inbound_mail", "webhook", "worker", "ws"]'), github.event.inputs.service)
runs-on: ubuntu-latest
strategy:
matrix:
region: ${{ fromJSON(github.event.inputs.region) }}
timeout-minutes: 60
environment: ${{ github.event.inputs.environment }}
permissions:
contents: read
packages: write
deployments: write
steps:
- run: echo "Rolling back"
- run: echo "Rolling back ${{ github.event.inputs.service }} in ${{ github.event.inputs.environment }}"

- id: commit
if: contains(fromJson('["Commit Hash"]'), github.event.inputs.mode)
uses: prompt/actions-commit-hash@v3

- name: Prepare variables
id: variables
run: |
if [[ "${{ matrix.region }}" == "EU" && "${{ github.event.inputs.environment }}" == "Production" ]]; then
AliaksandrRyzhou marked this conversation as resolved.
Show resolved Hide resolved
echo "Using Terraform Workspace: novu-prod-eu"
echo "TF_WORKSPACE=novu-prod-eu" >> $GITHUB_ENV
elif [[ "${{ matrix.region }}" == "US" && "${{ github.event.inputs.environment }}" == "Production" ]]; then
echo "Using Terraform Workspace: novu-prod"
echo "TF_WORKSPACE=novu-prod" >> $GITHUB_ENV
elif [[ "${{ matrix.region }}" == "EU" && "${{ github.event.inputs.environment }}" == "Development" ]]; then
echo "Using Terraform Workspace: novu-dev"
echo "TF_WORKSPACE=novu-dev" >> $GITHUB_ENV
elif [[ "${{ matrix.region }}" == "US" && "${{ github.event.inputs.environment }}" == "Development" ]]; then
echo "Using Terraform Workspace: novu-dev"
echo "TF_WORKSPACE=novu-dev" >> $GITHUB_ENV
echo "Error: Development environment doesn't exist in the US region." >&2
exit 1
else
echo "Using Terraform Workspace: novu-dev"
echo "TF_WORKSPACE=novu-dev" >> $GITHUB_ENV
fi

- name: Checkout cloud infra
uses: actions/checkout@master
with:
repository: novuhq/cloud-infra
token: ${{ secrets.GH_PACKAGES }}
path: cloud-infra

- name: Terraform setup
uses: hashicorp/setup-terraform@v1
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 1.5.5
terraform_wrapper: false

- name: Terraform Init
working-directory: cloud-infra/terraform/novu/aws
run: terraform init

- name: Terraform get output
working-directory: cloud-infra/terraform/novu/aws
id: terraform
env:
SERVICE_NAME: ${{ github.event.inputs.service }}
run: |
echo "ecs_container_name=$(terraform output -json ${{ env.SERVICE_NAME }}_ecs_container_name | jq -r .)" >> $GITHUB_ENV
echo "ecs_service=$(terraform output -json ${{ env.SERVICE_NAME }}_ecs_service | jq -r .)" >> $GITHUB_ENV
echo "ecs_cluster=$(terraform output -json ${{ env.SERVICE_NAME }}_ecs_cluster | jq -r .)" >> $GITHUB_ENV
echo "task_name=$(terraform output -json ${{ env.SERVICE_NAME }}_task_name | jq -r .)" >> $GITHUB_ENV
echo "aws_region=$(terraform output -json aws_region | jq -r .)" >> $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: ${{ env.aws_region }}

- name: ECS get output
if: contains(fromJson('["Previous Version"]'), github.event.inputs.mode)
id: ecs-output
run: |
echo "Retrieving current_task_definition_arn..."
AliaksandrRyzhou marked this conversation as resolved.
Show resolved Hide resolved
current_task_definition_arn=$(aws ecs describe-services --cluster ${{ env.ecs_cluster }} --services ${{ env.ecs_service }} --query 'services[0].taskDefinition' --output text)
echo "current_task_definition_arn=$current_task_definition_arn" >> $GITHUB_ENV

echo "Retrieving task_definition_family..."
task_definition_family=$(aws ecs describe-task-definition --task-definition ${{ env.task_name }} --query 'taskDefinition.family' --output text)
echo "task_definition_family=$task_definition_family" >> $GITHUB_ENV

echo "Retrieving task_definition_list..."
task_definition_list=$(aws ecs list-task-definitions --family-prefix "${task_definition_family}" --output text --sort DESC | grep 'TASKDEFINITIONARNS' | cut -f 2)
task_definition_list_formatted=$(echo "$task_definition_list" | tr '\n' '|') # Replace newline with '|'
echo "task_definition_list=$task_definition_list_formatted" >> $GITHUB_ENV

if [ -n "$task_definition_list" ]; then
echo "Retrieving previous_task_definition_arn..."
index=$(echo "$task_definition_list" | grep -n "$current_task_definition_arn" | cut -d ':' -f 1)
if [ -n "$index" ]; then
if [ "$index" -ge 1 ]; then # Greater than or equal to 1
previous_index=$((index + 1))
previous_task_definition_arn=$(echo "$task_definition_list" | sed -n "${previous_index}p")
echo "previous_task_definition_arn=$previous_task_definition_arn" >> $GITHUB_ENV
else
echo "Invalid index value: $index"
fi
else
echo "Previous task definition not found. It seems to me someone deleted the current task from the list and that is why I can't find the previous task."
exit 1
fi
else
echo "No task definitions found."
exit 1
fi

- name: ECS get output by using commit hash
if: contains(fromJson('["Commit Hash"]'), github.event.inputs.mode)
id: ecs-output-commit-hash
env:
IMAGE_TAG: ${{ steps.commit.outputs.hash }}
run: |
task_definition_family=$(aws ecs describe-task-definition --task-definition ${{ env.task_name }} --query 'taskDefinition.family' --output text)
task_definition_arns=$(aws ecs list-task-definitions --family-prefix "${task_definition_family}" --query 'taskDefinitionArns' --output text --sort DESC)
found=false
for arn in $(echo "$task_definition_arns" | tr '\t' '\n' | head -n 20); do
task_definition=$(aws ecs describe-task-definition --task-definition $arn)
if echo "$task_definition" | grep -q "$IMAGE_TAG"; then
echo "Found task definition with image tag $IMAGE_TAG: $arn"
found=true
needed_arn=$arn
break
fi
done
if [ "$found" = false ]; then
echo "Error: Task definition with image tag $IMAGE_TAG not found within the last 20 tasks."
exit 1
fi
current_task_definition_arn=$(aws ecs describe-services --cluster ${{ env.ecs_cluster }} --services ${{ env.ecs_service }} --query 'services[0].taskDefinition' --output text)
echo "current_task_definition_arn=$current_task_definition_arn" >> $GITHUB_ENV
echo "previous_task_definition_arn=$needed_arn" >> $GITHUB_ENV
echo "Your task definition ARN is $needed_arn"


- name: Rollback a service to the previous task definition
id: rollback
env:
PREVIOUS_TASK: ${{ env.previous_task_definition_arn }}
CURRENT_TASK: ${{ env.current_task_definition_arn }}
run: |
aws ecs update-service --cluster ${{ env.ecs_cluster }} --service ${{ env.ecs_service }} --task-definition ${{ env.PREVIOUS_TASK }}
aws ecs wait services-stable --cluster ${{ env.ecs_cluster }} --service ${{ env.ecs_service }}
echo "After Rollback:"
echo "The previous task definition: $(echo $CURRENT_TASK | awk -F'task-definition/' '{print $2}')"
echo "The current task definition: $(echo $PREVIOUS_TASK | awk -F'task-definition/' '{print $2}')"


netlify:
if: contains(fromJson('["web", "widget"]'), github.event.inputs.service)
AliaksandrRyzhou marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-latest
strategy:
matrix:
region: ${{ fromJSON(github.event.inputs.region) }}
timeout-minutes: 60
environment: ${{ github.event.inputs.environment }}
permissions:
contents: read
packages: write
deployments: write
env:
NETLIFY_ACCESS_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
steps:
- run: echo "Rolling back ${{ github.event.inputs.service }} in ${{ github.event.inputs.environment }}"

- id: commit-netlify
if: contains(fromJson('["Commit Hash"]'), github.event.inputs.mode)
uses: prompt/actions-commit-hash@v3

- name: Prepare variables
id: variables
run: |
if [[ "${{ github.event.inputs.service }}" == "widget" && "${{ github.event.inputs.environment }}" == "Development" && "${{ matrix.region }}" == "EU" ]]; then
echo "Using netlify_site_id: b9147448-b835-4eb1-a2f0-11102f611f5f"
echo "netlify_site_id=b9147448-b835-4eb1-a2f0-11102f611f5f" >> $GITHUB_ENV
elif [[ "${{ github.event.inputs.service }}" == "web" && "${{ github.event.inputs.environment }}" == "Development" && "${{ matrix.region }}" == "EU" ]]; then
echo "Using netlify_site_id: 45396446-dc86-4ad6-81e4-86d3eb78d06f"
echo "netlify_site_id=45396446-dc86-4ad6-81e4-86d3eb78d06f" >> $GITHUB_ENV
elif [[ "${{ github.event.inputs.environment }}" == "Development" && "${{ matrix.region }}" == "US" ]]; then
echo "Error: Development environment doesn't exist in the US region." >&2
exit 1
elif [[ "${{ github.event.inputs.service }}" == "web" && "${{ github.event.inputs.environment }}" == "Production" && "${{ matrix.region }}" == "EU" ]]; then
echo "Using netlify_site_id: d2e8b860-7016-4202-9256-ebca0f13259a"
echo "netlify_site_id=d2e8b860-7016-4202-9256-ebca0f13259a" >> $GITHUB_ENV
elif [[ "${{ github.event.inputs.service }}" == "web" && "${{ github.event.inputs.environment }}" == "Production" && "${{ matrix.region }}" == "US" ]]; then
echo "Using netlify_site_id: 8639d8b9-81f9-44c3-b885-585a7fd2b5ff"
echo "netlify_site_id=8639d8b9-81f9-44c3-b885-585a7fd2b5ff" >> $GITHUB_ENV
elif [[ "${{ github.event.inputs.service }}" == "widget" && "${{ github.event.inputs.environment }}" == "Production" && "${{ matrix.region }}" == "EU" ]]; then
echo "Using netlify_site_id: 20a64bdd-1934-4284-875f-862410c69a3b"
echo "netlify_site_id=20a64bdd-1934-4284-875f-862410c69a3b" >> $GITHUB_ENV
elif [[ "${{ github.event.inputs.service }}" == "widget" && "${{ github.event.inputs.environment }}" == "Production" && "${{ matrix.region }}" == "US" ]]; then
echo "Using netlify_site_id: 6f927fd4-dcb0-4cf3-8c0b-8c5539d0d034"
echo "netlify_site_id=6f927fd4-dcb0-4cf3-8c0b-8c5539d0d034" >> $GITHUB_ENV
fi

- name: Get Current Deploy ID
if: contains(fromJson('["Previous Version"]'), github.event.inputs.mode)
id: get_current_deploy
env:
NETLIFY_SITE_ID: ${{ env.netlify_site_id }}
run: |
response=$(curl -s -H "Authorization: Bearer $NETLIFY_ACCESS_TOKEN" "https://api.netlify.com/api/v1/sites/${NETLIFY_SITE_ID}")
current_deploy_id=$(echo "$response" | jq -r '.published_deploy.id')
echo "current_deploy_id=$current_deploy_id" >> $GITHUB_ENV

- name: Find Previous Production Deployments and Determine Previous Deploy ID
if: contains(fromJson('["Previous Version"]'), github.event.inputs.mode)
id: previous_deploy_id
env:
NETLIFY_SITE_ID: ${{ env.netlify_site_id }}
run: |
response=$(curl -s -H "Authorization: Bearer $NETLIFY_ACCESS_TOKEN" "https://api.netlify.com/api/v1/sites/${NETLIFY_SITE_ID}/deploys?per_page=100")
deploy_ids=$(echo "$response" | jq -r '.[] | select(.context == "production" and .state == "ready" and .published_at != null) | .id' | sort)
current_index=$(echo "$deploy_ids" | grep -n "$current_deploy_id" | cut -d ":" -f 1)
previous_index=$((current_index - 1))
previous_deploy_id=$(echo "$deploy_ids" | sed "${previous_index}q;d")
echo "previous_deploy_id=$previous_deploy_id" >> $GITHUB_ENV

- name: Determine Previous Deploy ID
if: contains(fromJson('["Commit Hash"]'), github.event.inputs.mode)
env:
NETLIFY_SITE_ID: ${{ env.netlify_site_id }}
COMMIT_REF: ${{ steps.commit-netlify.outputs.hash }}
run: |
response=$(curl -s -H "Authorization: Bearer $NETLIFY_ACCESS_TOKEN" "https://api.netlify.com/api/v1/sites/$NETLIFY_SITE_ID/deploys")
deploy_id=$(echo "$response" | jq -r ".[] | select(.commit_ref == \"$COMMIT_REF\") | .id")
if [ -n "$deploy_id" ]; then
echo "Deploy ID for commit $COMMIT_REF: $deploy_id"
echo "previous_deploy_id=$deploy_id" >> $GITHUB_ENV
else
echo "Deploy not found for commit $COMMIT_REF"
exit 1
fi

- name: Rollback to Previous Deploy
if: env.previous_deploy_id != null
env:
NETLIFY_SITE_ID: ${{ env.netlify_site_id }}
run: |
echo "Restoring previous deploy..."
curl -X POST -H "Authorization: Bearer $NETLIFY_ACCESS_TOKEN" "https://api.netlify.com/api/v1/sites/${{ env.netlify_site_id }}/deploys/${{ env.previous_deploy_id }}/restore"