Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/actions/argocd-deployment/update-chart-values/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# ArgoCD Deployment GitHub Action

## Introduction
This GitHub Action automates the deployment of applications using ArgoCD by updating Helm chart values. It supports various deployment scenarios, including staging, production, and multi-environment deployments.

## Inputs
- **app_name**: The name of the application to be deployed. *(required)*
- **branch_name**: The name of the branch currently being deployed. *(required)*
- **deploy_on_sandbox**: Set to "true" to enable deployment to the sandbox environment during the ALL_ENV stage. Default is `true`.
- **env_to_deploy**: Specifies the target environment for deployment. Use 'ALL_ENV' to update all environments, or 'NO_SYNC' to only update common staging values. *(required)*
- **image_tag**: The specific image tag to deploy.
- **manual**: Set to true if the deployment is manual, to reflect this in the commit message.
- **rollout**: Set to true if the deployment is a rollout, to reflect this in the commit message.
- **synced_envs_as_outputs**: Set to true to output the list of environments that were synchronized. Default is `false`.
- **update_deployed_at**: Set to "true" to update the DEPLOYED_AT environment variable with the current date and time. Default is `false`.
- **release_version**: The version of the PROD release to be deployed.

## Outputs
- **synced_staging_envs**: Outputs the list of environments that were successfully synchronized.
- **old_image_tag**: Outputs the old image tag for a possible rollback.

## Usage Example
```yaml
name: Deploy Application

on:
push:
branches:
- main

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Deploy to ArgoCD
uses: ./.github/actions/argocd-deployment/update-chart-values
with:
app_name: 'my-app'
branch_name: 'main'
env_to_deploy: 'prod'
image_tag: 'v1.0.0'
manual: 'true'
```
23 changes: 11 additions & 12 deletions .github/actions/argocd-deployment/update-chart-values/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,39 @@ description: 'Update current_tag value locally and commit changes.'

inputs:
app_name:
description: 'The application name that will be deployed.'
description: 'The name of the application to be deployed.'
required: true
branch_name:
description: 'Current branch name.'
description: 'The name of the branch currently being deployed.'
required: true
deploy_on_sandbox:
description: 'If "true" the deployment in the ALL_ENV stage will be done on the sandbox environment.'
description: 'Set to "true" to enable deployment to the sandbox environment during the ALL_ENV stage.'
required: false
default: 'true'
env_to_deploy:
description: >
Environment where the image will be deployed.
If the value is 'ALL_ENV', then all environments will be updated.
If the value is 'NO_SYNC', then the action will only update the common staging values.
Specifies the target environment for deployment.
Use 'ALL_ENV' to update all environments, or 'NO_SYNC' to only update common staging values.
required: true
image_tag:
description: 'The image tag that will be deployed.'
description: 'The specific image tag to deploy.'
required: false
manual:
description: 'If true, the commit message will specify that is a Manual deployment.'
description: 'Set to true if the deployment is manual, to reflect this in the commit message.'
required: false
rollout:
description: 'If true, the commit message will specify that is a Rollout.'
description: 'Set to true if the deployment is a rollout, to reflect this in the commit message.'
required: false
synced_envs_as_outputs:
description: 'If true, the action will output the environments that were sycned.'
description: 'Set to true to output the list of environments that were synchronized.'
required: false
default: 'false'
update_deployed_at:
description: 'If "true" the env DEPLOYED_AT will be updated with the current deployment date.'
description: 'Set to "true" to update the DEPLOYED_AT environment variable with the current date and time.'
required: false
default: 'false'
release_version:
description: 'The release version that will be deployed.'
description: 'The version of the PROD release to be deployed.'
required: false

outputs:
Expand Down
208 changes: 139 additions & 69 deletions .github/actions/argocd-deployment/update-chart-values/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
#!/usr/bin/env bash

# ============================================================================
# ArgoCD Deployment Script for Helm Chart Values Update
#
# This script updates Helm chart values for ArgoCD deployments based on
# environment, branch name, and other parameters. It handles different
# deployment scenarios including staging, production, and multi-environment
# deployments.
# ============================================================================

# Exit immediately if a command exits with a non-zero status
# Treat unset variables as an error
# Exit if any command in a pipeline fails
set -euo pipefail

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

# Variable to track environments that have been synchronized
synced_staging_envs=""

# Adds a new staging environment to the list of synced environments
add_synced_staging_envs() {
local new_staging_env=$1
if [ -z "$synced_staging_envs" ]; then
Expand All @@ -14,135 +31,188 @@ add_synced_staging_envs() {
fi
}

# Updates the deployment timestamp if enabled
update_deployment_timestamp() {
if [[ "$UPDATE_DEPLOYED_AT" = true ]]; then
echo "Updating 'DEPLOYED_AT' env variable at runtime."
DEPLOYED_AT=$(date -u +"%FT%TZ")
fi
}

# Updates image tag in the specified values file
update_image_tag() {
local values_file=$1
if [ "$IMAGE_TAG" != "" ]; then
sed -i "{s/currentTag:.*/currentTag: $IMAGE_TAG/;}" "$values_file"
fi
}

# Updates DEPLOYED_AT timestamp in the specified values file
update_deployed_at() {
local values_file=$1
if [ ! -z ${DEPLOYED_AT+x} ]; then
sed -i "{s/DEPLOYED_AT:.*/DEPLOYED_AT: $DEPLOYED_AT/;}" "$values_file"
fi
}

# Commits and pushes changes to git repository
commit_and_push_changes() {
if [ -z "$(git diff --exit-code)" ]; then
echo "No changes in the working directory."
return 0
fi

# Configure git user
git config user.name github-actions
git config user.email github-actions@github.com
git pull
git add .

# The branch name can only start with 'master' or 'main' if the branch is MASTER/MAIN ref.
# Create appropriate commit message based on deployment type
if [[ $ROLLOUT == true ]]; then
git commit -m "ROLLOUT UNDO in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}]"
elif [[ $MANUAL == true ]] && [[ "$IMAGE_TAG" != "" ]]; then
git commit -m "MANUAL DEPLOYMENT in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}${RELEASE_VERSION:+-$RELEASE_VERSION}]"
elif [[ $MANUAL == true ]]; then
git commit -m "MANUAL DEPLOYMENT in ${APP_NAME^^} -> [${ENV_TO_DEPLOY^^}]"
else
git commit -m "DEPLOYMENT in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}]"
fi

git push
}

# ============================================================================
# VALIDATION CHECKS
# ============================================================================

# Validate production deployment requirements
if [[ "$ENV_TO_DEPLOY" == "prod" ]] &&
[[ "$BRANCH_NAME" != "master" && "$BRANCH_NAME" != "main" ]]; then

if [[ -z "$RELEASE_VERSION" ]]; then
echo "The Environment to Deploy cannot be 'prod' if the branches are not 'master' or 'main', and the RELEASE_VERSION is not set."
echo "ERROR: Production deployment requires either master/main branch or a RELEASE_VERSION."
exit 1
fi
fi

# The Environment name to Deploy cannot be 'master' or 'main'.
# Prevent deploying to master/main environments
if [[ "$ENV_TO_DEPLOY" == "master" || "$ENV_TO_DEPLOY" == "main" ]]; then
echo "The Environment to Deploy cannot be 'master' or 'main'."
echo "ERROR: Cannot deploy directly to 'master' or 'main' environments."
exit 1
fi

# The Environment name to Deploy cannot be 'ALL_ENV' if the branch is not 'master' or 'main'.
# Validate ALL_ENV deployment requirements
if [[ "$ENV_TO_DEPLOY" == "ALL_ENV" ]] && [[ "$BRANCH_NAME" != "master" && "$BRANCH_NAME" != "main" ]]; then
echo "It cannot be deployed in All Environments if the branch is not 'master' or 'main'."
echo "ERROR: ALL_ENV deployment requires master or main branch."
exit 1
fi

# Must be updated in each deploy if UPDATE_DEPLOYED_AT is true.
if [[ "$UPDATE_DEPLOYED_AT" = true ]]; then
echo "Updating 'DEPLOYED_AT' env variable at runtime."
DEPLOYED_AT=$(date -u +"%FT%TZ")
fi
# Update deployment timestamp if enabled
update_deployment_timestamp

# ============================================================================
# DEPLOYMENT LOGIC
# ============================================================================

# Iter over all values-*.yaml files in order to sync thier content with the local values.
# SCENARIO 1: Deploy to ALL staging environments from master/main branch
if [[ "$ENV_TO_DEPLOY" == "ALL_ENV" ]] && [[ "$BRANCH_NAME" == "master" || "$BRANCH_NAME" == "main" ]]; then
cd helm-chart-$APP_NAME-values-staging/

# Process each staging environment
for env_path in $(ls -d -- ./staging/*/ 2>/dev/null); do
# Get the source of the 'currentTag' environment
# Extract environment name and source file path
export CURRENT_ENV=$(basename "${env_path%/}")
export CURRENT_SOURCE_FILE=$(echo "./../kube/values/$APP_NAME/staging/$CURRENT_ENV/values-stg.yaml")
export CURRENT_SOURCE_FILE="./../kube/values/$APP_NAME/staging/$CURRENT_ENV/values-stg.yaml"

if [[ -e $CURRENT_SOURCE_FILE ]]; then
# Get current image tag information
export CURRENT_IMAGE_TAG=$(cat "./staging/$CURRENT_ENV/values-stg-tag.yaml" | grep currentTag: | cut -d ':' -f 2 | sed 's/ //g')
export CURRENT_IMAGE_TAG_ENV=$(cut -d '-' -f 1 <<< $( echo $CURRENT_IMAGE_TAG ))

# Check if the currentTag is an old 'master' image -> Then, sync the values.
if [[ "$CURRENT_IMAGE_TAG_ENV" == "master" || "$CURRENT_IMAGE_TAG_ENV" == "main" || "$CURRENT_IMAGE_TAG_ENV" == "latest" || "$CURRENT_ENV" == "sandbox" ]]; then
# Only sync environments with master/main/latest tags or sandbox
if [[ "$CURRENT_IMAGE_TAG_ENV" == "master" || "$CURRENT_IMAGE_TAG_ENV" == "main" ||
"$CURRENT_IMAGE_TAG_ENV" == "latest" || "$CURRENT_ENV" == "sandbox" ]]; then

# Skip the deployment on sandbox if the DEPLOY_ON_SANDBOX is false.
# Skip sandbox if disabled
if [[ $DEPLOY_ON_SANDBOX == false && "$CURRENT_ENV" == "sandbox" ]]; then
echo "Skipping sandbox deployment as DEPLOY_ON_SANDBOX is false"
continue
fi

# Update the currentTag to the new image tag if it is not empty.
if [ "$IMAGE_TAG" != "" ]; then
sed -i "{s/currentTag:.*/currentTag: $IMAGE_TAG/;}" "./staging/$CURRENT_ENV/values-stg-tag.yaml"
fi

# Update the DEPLOYED_AT env variable if it is not empty.
if [ ! -z ${DEPLOYED_AT+x} ]; then
sed -i "{s/DEPLOYED_AT:.*/DEPLOYED_AT: $DEPLOYED_AT/;}" $CURRENT_SOURCE_FILE
fi
# Update image tag and deployment timestamp
update_image_tag "./staging/$CURRENT_ENV/values-stg-tag.yaml"
update_deployed_at "$CURRENT_SOURCE_FILE"

# Add the current environment to the synced_staging_envs variable if SYNCED_ENVS_AS_OUTPUTS is true.
# Track synced environments if enabled
if [[ $SYNCED_ENVS_AS_OUTPUTS == true ]]; then
add_synced_staging_envs $CURRENT_ENV
fi

# Sync the values of the current environment from the local code repository to the helm-chart-$APP_NAME-values/staging repository.
# Sync values from local repository to helm chart repository
echo "Syncing values for environment: $CURRENT_ENV"
cp -f -r "./../kube/values/$APP_NAME/staging/$CURRENT_ENV/" "./staging/"
fi
else
echo "$CURRENT_ENV not found in local code repository, but existing in helm-chart-$APP_NAME-values/staging repository."
echo "WARNING: $CURRENT_ENV not found in local code repository, but exists in helm-chart-$APP_NAME-values/staging repository."
fi
done
# The values-stg.yaml will always be synced when a Pull Request is closed.

# Always sync the common staging values file
cp -f "./../kube/values/$APP_NAME/staging/values-stg.yaml" "./staging/values-stg.yaml"

# Output synced environments for GitHub Actions
if [ ! -z ${synced_staging_envs+x} ]; then
echo "synced_staging_envs=$( echo $synced_staging_envs )" >> $GITHUB_OUTPUT
fi

# SCENARIO 2: No sync operation, just update common staging values
elif [[ "$ENV_TO_DEPLOY" == "NO_SYNC" ]] && [[ "$BRANCH_NAME" == "master" || "$BRANCH_NAME" == "main" ]]; then
cd helm-chart-$APP_NAME-values-staging/
echo "Updating only common staging values file"
cp -f "./../kube/values/$APP_NAME/staging/values-stg.yaml" "./staging/values-stg.yaml"

# SCENARIO 3: Deploy to production from master/main branch
elif [[ "$ENV_TO_DEPLOY" == "prod" ]] && [[ "$BRANCH_NAME" == "master" || "$BRANCH_NAME" == "main" ]]; then
cd helm-chart-$APP_NAME-values-prod/
# Store the currentTag value before the deployment for rollout undo (just in case).

# Store current image tag for potential rollback
echo "old_image_tag=$(cat "./prod/values-prod-tag.yaml" | grep currentTag: | cut -d ':' -f 2 | sed 's/ //g')" >> $GITHUB_OUTPUT
if [ "$IMAGE_TAG" != "" ]; then
sed -i "{s/currentTag:.*/currentTag: $IMAGE_TAG/;}" "./prod/values-prod-tag.yaml"
fi
if [ ! -z ${DEPLOYED_AT} ]; then
sed -i "{s/DEPLOYED_AT:.*/DEPLOYED_AT: $DEPLOYED_AT/;}" "./../kube/values/$APP_NAME/prod/values-prod.yaml"
fi

# Update image tag and deployment timestamp
update_image_tag "./prod/values-prod-tag.yaml"
update_deployed_at "./../kube/values/$APP_NAME/prod/values-prod.yaml"

# Sync production values
echo "Syncing production values"
cp -f -r "./../kube/values/$APP_NAME/prod/" "./"

# SCENARIO 4: Deploy to production with specific release version
elif [[ "$ENV_TO_DEPLOY" == "prod" ]] && [[ ! -z "$RELEASE_VERSION" ]]; then
cd helm-chart-$APP_NAME-values-prod/
if [ "$IMAGE_TAG" != "" ]; then
sed -i "{s/currentTag:.*/currentTag: $IMAGE_TAG/;}" "./prod-$RELEASE_VERSION/values-prod-tag.yaml"
fi
if [ ! -z ${DEPLOYED_AT} ]; then
sed -i "{s/DEPLOYED_AT:.*/DEPLOYED_AT: $DEPLOYED_AT/;}" "./../kube/values/$APP_NAME/prod-$RELEASE_VERSION/values-prod.yaml"
fi

# Update image tag and deployment timestamp for specific release
update_image_tag "./prod-$RELEASE_VERSION/values-prod-tag.yaml"
update_deployed_at "./../kube/values/$APP_NAME/prod-$RELEASE_VERSION/values-prod.yaml"

# Sync production values for specific release
echo "Syncing production values for release: $RELEASE_VERSION"
cp -f -r "./../kube/values/$APP_NAME/prod-$RELEASE_VERSION/" "./"

# SCENARIO 5: Deploy to specific staging environment
else
cd helm-chart-$APP_NAME-values-staging/
# Store the currentTag value before the deployment for rollout undo (just in case).

# Store current image tag for potential rollback
echo "old_image_tag=$(cat "./staging/$ENV_TO_DEPLOY/values-stg-tag.yaml" | grep currentTag: | cut -d ':' -f 2 | sed 's/ //g')" >> $GITHUB_OUTPUT
if [ "$IMAGE_TAG" != "" ]; then
sed -i "{s/currentTag:.*/currentTag: $IMAGE_TAG/;}" "./staging/$ENV_TO_DEPLOY/values-stg-tag.yaml"
fi
if [ ! -z ${DEPLOYED_AT} ]; then
sed -i "{s/DEPLOYED_AT:.*/DEPLOYED_AT: $DEPLOYED_AT/;}" "./../kube/values/$APP_NAME/staging/$ENV_TO_DEPLOY/values-stg.yaml"
fi

# Update image tag and deployment timestamp
update_image_tag "./staging/$ENV_TO_DEPLOY/values-stg-tag.yaml"
update_deployed_at "./../kube/values/$APP_NAME/staging/$ENV_TO_DEPLOY/values-stg.yaml"

# Sync values for specific staging environment
echo "Syncing values for specific staging environment: $ENV_TO_DEPLOY"
cp -f -r "./../kube/values/$APP_NAME/staging/$ENV_TO_DEPLOY/" "./staging/"
fi

if [ -z "$(git diff --exit-code)" ]; then
echo "No changes in the working directory."
else
git config user.name github-actions
git config user.email github-actions@github.com
git pull
git add .
if [[ $ROLLOUT == true ]]; then
git commit -m "ROLLOUT UNDO in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}]"
elif [[ $MANUAL == true ]] && [[ "$IMAGE_TAG" != "" ]]; then
git commit -m "MANUAL DEPLOYMENT in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}${RELEASE_VERSION:+-$RELEASE_VERSION}]"
elif [[ $MANUAL == true ]]; then
git commit -m "MANUAL DEPLOYMENT in ${APP_NAME^^} -> [${ENV_TO_DEPLOY^^}]"
else
git commit -m "DEPLOYMENT in ${APP_NAME^^} - $IMAGE_TAG -> [${ENV_TO_DEPLOY^^}]"
fi
git push
fi
# Commit and push changes to git repository
commit_and_push_changes