backend deployment by Karan0005 #15
This file contains 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: backend_pipeline | |
run-name: backend deployment by ${{ github.actor }} | |
on: | |
push: | |
branches: | |
- develop | |
- uat | |
- main | |
paths: | |
- 'apps/backend/**' | |
- 'libs/shared/**' | |
jobs: | |
build: | |
name: Build Backend | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v4 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Login to DockerHub | |
uses: docker/login-action@v3.3.0 | |
with: | |
username: ${{ secrets.DOCKER_USERNAME }} | |
password: ${{ secrets.DOCKER_PASSWORD }} | |
- name: Set Environment Variables | |
run: | | |
case $GITHUB_REF in | |
refs/heads/develop) | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "DOCKER_TAG=develop" >> $GITHUB_ENV | |
;; | |
refs/heads/uat) | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "DOCKER_TAG=uat" >> $GITHUB_ENV | |
;; | |
refs/heads/main) | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "DOCKER_TAG=prod" >> $GITHUB_ENV | |
;; | |
esac | |
- name: Backup existing image if exists | |
run: | | |
IMAGE_EXISTS=$(docker manifest inspect ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} > /dev/null 2>&1 && echo "YES" || echo "NO") | |
if [ "$IMAGE_EXISTS" = "YES" ]; then | |
echo "Backup existing image..." | |
docker pull ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} | |
docker tag ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }}_backup | |
docker push ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }}_backup | |
else | |
echo "No existing image to backup." | |
fi | |
- name: Build and Push Backend Docker Image | |
uses: docker/build-push-action@v6 | |
with: | |
context: . | |
file: devops/backend/Dockerfile | |
push: true | |
tags: ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} | |
build-args: | | |
PORT_BACKEND=${{ env.PORT_BACKEND }} | |
deploy: | |
name: Deploy Backend | |
runs-on: ubuntu-latest | |
needs: build | |
steps: | |
- name: Set Environment Variables | |
run: | | |
case $GITHUB_REF in | |
refs/heads/develop) | |
echo "APP_ENV=DEV" >> $GITHUB_ENV | |
echo "REDIS_HOST=redis" >> $GITHUB_ENV | |
echo "REDIS_PORT=6379" >> $GITHUB_ENV | |
echo "MIN_REDIS_VERSION=7.0.0" >> $GITHUB_ENV | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "PORT_FRONTEND=80" >> $GITHUB_ENV | |
echo "DOCKER_TAG=develop" >> $GITHUB_ENV | |
echo "ROUTE_PREFIX=api" >> $GITHUB_ENV | |
echo "KEY_VAULT_URI=${{ secrets.DEV_KEY_VAULT_URI }}" >> $GITHUB_ENV | |
echo "TENANT_ID=${{ secrets.DEV_TENANT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_ID=${{ secrets.DEV_CLIENT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_SECRET=${{ secrets.DEV_CLIENT_SECRET }}" >> $GITHUB_ENV | |
echo "SERVER_HOST=${{ secrets.DEV_SERVER_HOST }}" >> $GITHUB_ENV | |
echo "SERVER_PASS_PHRASE=${{ secrets.DEV_SERVER_PASS_PHRASE }}" >> $GITHUB_ENV | |
echo "SERVER_PRIVATE_SSH_KEY<<EOF" >> $GITHUB_ENV | |
echo "${{ secrets.DEV_SERVER_PRIVATE_SSH_KEY }}" >> $GITHUB_ENV | |
echo "EOF" >> $GITHUB_ENV | |
echo "SERVER_USER=${{ secrets.DEV_SERVER_USER }}" >> $GITHUB_ENV | |
;; | |
refs/heads/uat) | |
echo "APP_ENV=UAT" >> $GITHUB_ENV | |
echo "REDIS_HOST=redis" >> $GITHUB_ENV | |
echo "REDIS_PORT=6379" >> $GITHUB_ENV | |
echo "MIN_REDIS_VERSION=7.0.0" >> $GITHUB_ENV | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "PORT_FRONTEND=80" >> $GITHUB_ENV | |
echo "DOCKER_TAG=uat" >> $GITHUB_ENV | |
echo "ROUTE_PREFIX=api" >> $GITHUB_ENV | |
echo "KEY_VAULT_URI=${{ secrets.UAT_KEY_VAULT_URI }}" >> $GITHUB_ENV | |
echo "TENANT_ID=${{ secrets.UAT_TENANT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_ID=${{ secrets.UAT_CLIENT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_SECRET=${{ secrets.UAT_CLIENT_SECRET }}" >> $GITHUB_ENV | |
echo "SERVER_HOST=${{ secrets.UAT_SERVER_HOST }}" >> $GITHUB_ENV | |
echo "SERVER_PASS_PHRASE=${{ secrets.UAT_SERVER_PASS_PHRASE }}" >> $GITHUB_ENV | |
echo "SERVER_PRIVATE_SSH_KEY<<EOF" >> $GITHUB_ENV | |
echo "${{ secrets.UAT_SERVER_PRIVATE_SSH_KEY }}" >> $GITHUB_ENV | |
echo "EOF" >> $GITHUB_ENV | |
echo "SERVER_USER=${{ secrets.UAT_SERVER_USER }}" >> $GITHUB_ENV | |
;; | |
refs/heads/main) | |
echo "APP_ENV=PROD" >> $GITHUB_ENV | |
echo "REDIS_HOST=redis" >> $GITHUB_ENV | |
echo "REDIS_PORT=6379" >> $GITHUB_ENV | |
echo "MIN_REDIS_VERSION=7.0.0" >> $GITHUB_ENV | |
echo "PORT_BACKEND=8000" >> $GITHUB_ENV | |
echo "PORT_FRONTEND=80" >> $GITHUB_ENV | |
echo "DOCKER_TAG=prod" >> $GITHUB_ENV | |
echo "ROUTE_PREFIX=api" >> $GITHUB_ENV | |
echo "KEY_VAULT_URI=${{ secrets.PROD_KEY_VAULT_URI }}" >> $GITHUB_ENV | |
echo "TENANT_ID=${{ secrets.PROD_TENANT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_ID=${{ secrets.PROD_CLIENT_ID }}" >> $GITHUB_ENV | |
echo "CLIENT_SECRET=${{ secrets.PROD_CLIENT_SECRET }}" >> $GITHUB_ENV | |
echo "SERVER_HOST=${{ secrets.PROD_SERVER_HOST }}" >> $GITHUB_ENV | |
echo "SERVER_PASS_PHRASE=${{ secrets.PROD_SERVER_PASS_PHRASE }}" >> $GITHUB_ENV | |
echo "SERVER_PRIVATE_SSH_KEY<<EOF" >> $GITHUB_ENV | |
echo "${{ secrets.PROD_SERVER_PRIVATE_SSH_KEY }}" >> $GITHUB_ENV | |
echo "EOF" >> $GITHUB_ENV | |
echo "SERVER_USER=${{ secrets.PROD_SERVER_USER }}" >> $GITHUB_ENV | |
;; | |
esac | |
- name: Deploy to Digital Ocean Droplet via SSH | |
uses: appleboy/ssh-action@v1.0.3 | |
with: | |
host: ${{ env.SERVER_HOST }} | |
username: ${{ env.SERVER_USER }} | |
key: ${{ env.SERVER_PRIVATE_SSH_KEY }} | |
passphrase: ${{ env.SERVER_PASS_PHRASE }} | |
script: | | |
# Ensure Docker is installed | |
if ! command -v docker &> /dev/null; then | |
echo "Docker not found, installing..." | |
curl -fsSL https://get.docker.com -o get-docker.sh | |
sh get-docker.sh | |
else | |
echo "Docker is already installed." | |
fi | |
# Function to create Docker volume if it does not exist | |
create_volume_if_not_exists() { | |
local VOLUME_NAME=$1 | |
if [[ $(docker volume ls -q -f name=^${VOLUME_NAME}$) ]]; then | |
echo "Volume '${VOLUME_NAME}' already exists. Skipping creation." | |
else | |
echo "Creating volume '${VOLUME_NAME}'..." | |
docker volume create "${VOLUME_NAME}" || { | |
echo "Error: Failed to create volume '${VOLUME_NAME}'."; exit 1; | |
} | |
fi | |
} | |
# Function to create Docker network if it does not exist | |
create_network_if_not_exists() { | |
local NETWORK_NAME=$1 | |
docker network inspect ${NETWORK_NAME} >/dev/null 2>&1 || { | |
echo "Creating Docker network '${NETWORK_NAME}'..." | |
docker network create ${NETWORK_NAME} || { | |
echo "Error: Failed to create network '${NETWORK_NAME}'."; exit 1; | |
} | |
} | |
} | |
# Function to set up Redis container if not running | |
setup_redis() { | |
if [ ! "$(docker ps -q -f name=redis)" ]; then | |
echo "Setting up Redis container..." | |
docker run -d --name redis --network backend_network --restart always \ | |
-p ${{ env.REDIS_PORT }}:${{ env.REDIS_PORT }} \ | |
-v redis_data:/app/redis-data redis:7 || { | |
echo "Error: Failed to start Redis container."; exit 1; | |
} | |
echo "Redis container started." | |
else | |
echo "Redis container already running." | |
fi | |
} | |
# Function to deploy backend container | |
deploy_backend() { | |
echo "Pulling latest backend image..." | |
docker pull ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} || { | |
echo "Error: Failed to pull backend image."; exit 1; | |
} | |
if [ "$(docker ps -q -f name=backend)" ]; then | |
echo "Stopping and removing old backend container..." | |
docker stop backend && docker rm backend || { | |
echo "Error: Failed to stop/remove backend container."; exit 1; | |
} | |
fi | |
echo "Starting new backend container..." | |
docker run -d --restart always --name backend --network backend_network \ | |
-p ${{ env.PORT_BACKEND }}:${{ env.PORT_BACKEND }} \ | |
-e APP_ENV=${{ env.APP_ENV }} \ | |
-e PORT_BACKEND=${{ env.PORT_BACKEND }} \ | |
-e PORT_FRONTEND=${{ env.PORT_FRONTEND }} \ | |
-e ROUTE_PREFIX=${{ env.ROUTE_PREFIX }} \ | |
-e KEY_VAULT_URI=${{ env.KEY_VAULT_URI }} \ | |
-e TENANT_ID=${{ env.TENANT_ID }} \ | |
-e CLIENT_ID=${{ env.CLIENT_ID }} \ | |
-e CLIENT_SECRET=${{ env.CLIENT_SECRET }} \ | |
-e REDIS_HOST=${{ env.REDIS_HOST }} \ | |
-e REDIS_PORT=${{ env.REDIS_PORT }} \ | |
-e MIN_REDIS_VERSION=${{ env.MIN_REDIS_VERSION }} \ | |
-v /var/run/docker.sock:/var/run/docker.sock \ | |
-v source_code_volume:/app/source-code \ | |
${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }} || { | |
echo "Error: Failed to start new backend container."; exit 1; | |
} | |
echo "Backend container started successfully." | |
} | |
# Function to roll back to backup backend container | |
rollback_backend() { | |
echo "Rolling back to backup backend container..." | |
docker pull ${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }}_backup || { | |
echo "Error: Failed to pull backup backend image."; exit 1; | |
} | |
# Stop and remove failed container (if it exists) | |
docker stop backend || true | |
docker rm backend || true | |
docker run -d --restart always --name backend --network backend_network \ | |
-p ${{ env.PORT_BACKEND }}:${{ env.PORT_BACKEND }} \ | |
-e APP_ENV=${{ env.APP_ENV }} \ | |
-e PORT_BACKEND=${{ env.PORT_BACKEND }} \ | |
-e PORT_FRONTEND=${{ env.PORT_FRONTEND }} \ | |
-e ROUTE_PREFIX=${{ env.ROUTE_PREFIX }} \ | |
-e KEY_VAULT_URI=${{ env.KEY_VAULT_URI }} \ | |
-e TENANT_ID=${{ env.TENANT_ID }} \ | |
-e CLIENT_ID=${{ env.CLIENT_ID }} \ | |
-e CLIENT_SECRET=${{ env.CLIENT_SECRET }} \ | |
-e REDIS_HOST=${{ env.REDIS_HOST }} \ | |
-e REDIS_PORT=${{ env.REDIS_PORT }} \ | |
-e MIN_REDIS_VERSION=${{ env.MIN_REDIS_VERSION }} \ | |
-v /var/run/docker.sock:/var/run/docker.sock \ | |
-v source_code_volume:/app/source-code \ | |
${{ secrets.DOCKER_USERNAME }}/backend:${{ env.DOCKER_TAG }}_backup || { | |
echo "Error: Failed to roll back to backup backend container."; exit 1; | |
} | |
echo "Rolled back to backup backend container." | |
} | |
# Create necessary volumes | |
create_volume_if_not_exists "source_code_volume" | |
create_volume_if_not_exists "redis_data" | |
# Create necessary network | |
create_network_if_not_exists "backend_network" | |
# Set up Redis container | |
setup_redis | |
# Deploy backend container | |
deploy_backend || rollback_backend | |
# Remove old backend images (dangling images) | |
docker images --filter "dangling=true" --format "{{.ID}}" | xargs -r docker rmi || true |