Skip to content

Commit

Permalink
build(Github): Refactors main workflow and adds Dry Run mode and envi…
Browse files Browse the repository at this point in the history
…ronment override
  • Loading branch information
KevSanchez committed May 15, 2024
1 parent 26a58ea commit 2af49a0
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 68 deletions.
80 changes: 80 additions & 0 deletions .github/actions/build-and-deploy-cloud-run/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Build And Deploy to Cloud Run
description: Build And Deploy to Cloud Run
inputs:
REPOSITORY:
description: "Repository"
required: true
SERVICE:
description: "Service"
required: true
PROJECT_ID:
description: "GCP Project Id"
required: true
GAR_LOCATION:
description: "GCP Artifact Registry url"
required: true
ENVIRONMENT_NAME:
description: "Environment name (PRODUCTION, STAGING)"
required: true
REGION:
description: "GCP Region"
required: true
COMPONENT_PATH:
description: "Component path of the component to be deployed (./cms, ./client ...)"
required: true
DRY_RUN:
description: "Makes the action work in Dry Run Mode"
required: false
type: boolean
default: false



outputs:
url:
description: url
value: ${{steps.deploy.output}}

runs:
using: "composite"
steps:
- name: Google Auth
id: auth
uses: 'google-github-actions/auth@v1'
with:
credentials_json: "${{ secrets[format('TF_{0}_GCP_SA_KEY', inputs.ENVIRONMENT_NAME)] }}"
token_format: 'access_token'

# Authenticate Docker to Google Cloud Artifact Registry via credentials json
- name: Docker Auth
id: docker-auth
uses: 'docker/login-action@v3'
with:
registry: ${{ inputs.GAR_LOCATION }}-docker.pkg.dev
username: _json_key
password: ${{ secrets[format('TF_{0}_GCP_SA_KEY', inputs.ENVIRONMENT_NAME)] }}

- name: Build Container
run: |-
docker build -f ${{ inputs.COMPONENT_PATH }}/Dockerfile.prod -t "${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:${{ github.sha }}" ${{ inputs.COMPONENT_PATH }}
- name: Push Container
if: ${{ ! inputs.DRY_RUN }}
run: |-
docker push "${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:${{ github.sha }}"
# tag as "latest"
docker tag "${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:${{ github.sha }}" "${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:latest"
docker push "${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:latest"
- name: Deploy to Cloud Run
if: ${{ ! inputs.DRY_RUN }}
id: deploy
uses: google-github-actions/deploy-cloudrun@v1
with:
service: ${{ inputs.SERVICE }}
region: ${{ inputs.REGION }}
image: ${{ inputs.GAR_LOCATION }}-docker.pkg.dev/${{ inputs.PROJECT_ID }}/${{ inputs.REPOSITORY }}/${{ inputs.SERVICE }}:${{ github.sha }}
# NOTE: You can also set env variables here:
# env_vars: |
# NODE_ENV=production
# TOKEN_EXPIRE=6400
136 changes: 68 additions & 68 deletions .github/workflows/deploy.yml → .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ name: Run deploy to GCP

on:
workflow_dispatch:
inputs:
ENVIRONMENT_NAME_OVERRIDE:
description: "Environment name to override the environment used in the workflow"
required: false
type: string
dry_run:
description: "Makes the workflow trigger in dry run mode (no components will be published or deployed to the actual environment infrastructure)"
required: false
default: false
type: boolean

push:
branches:
- main
Expand Down Expand Up @@ -47,8 +58,16 @@ jobs:
client:
- 'client/**'
- '.github/workflows/**'
- name: Applicable check
id: applicable_check
run: |
{
echo "flag=${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}"
} >> $GITHUB_OUTPUT
- name: Extract branch name
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
run: |
{
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}
Expand All @@ -58,16 +77,15 @@ jobs:
id: extract_branch

- name: Set environment name
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
id: environment_name
run: |
{
echo "ENVIRONMENT=${{ steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}"
echo "ENVIRONMENT=${{ inputs.ENVIRONMENT_NAME_OVERRIDE || steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}"
} >> $GITHUB_ENV
- name: Output secrets and vars as JSON
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail)
run: |
{
Expand All @@ -81,7 +99,7 @@ jobs:
id: env_json

- name: Output secrets and vars as key=value entries
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use jq to convert JSON to key=value entries
# 1. to_entries converts JSON to array of key/value pairs
# 2. map(.key + "=" + .value) converts each key/value pair to key=value
Expand All @@ -95,7 +113,7 @@ jobs:
id: env_entries_all

- name: Filter secrets and vars for inclusion in .env file by environment and application prefixes
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_
run: |
{
Expand All @@ -106,7 +124,7 @@ jobs:
id: env_entries_filtered

- name: Strip environment and application prefixes from secret and var names
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use sed to strip environment and application prefixes from secret and var names
run: |
{
Expand All @@ -117,44 +135,51 @@ jobs:
id: env_entries_stripped

- name: Save .env file
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
run: |
echo '${{ steps.env_entries_stripped.outputs.entries_stripped }}' >> $APP_ENV_PATH
cat $APP_ENV_PATH
#- name: Google Auth authentication via credentials json
- name: Google Auth
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
id: auth
uses: 'google-github-actions/auth@v1'
uses: 'google-github-actions/auth@v2'
with:
credentials_json: "${{ secrets[format('TF_{0}_GCP_SA_KEY', steps.environment_name.outputs.ENVIRONMENT)] }}"
token_format: 'access_token'

# Authenticate Docker to Google Cloud Artifact Registry via credentials json
- name: Docker Auth
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
id: docker-auth
uses: 'docker/login-action@v3'
with:
registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev
username: _json_key
password: ${{ secrets[format('TF_{0}_GCP_SA_KEY', steps.environment_name.outputs.ENVIRONMENT)] }}

- name: Build and Push Container
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
- name: Build Container
if: ${{ steps.applicable_check.outputs.flag }}
env:
REPOSITORY: ${{ secrets[format('TF_{0}_CLIENT_REPOSITORY', steps.environment_name.outputs.ENVIRONMENT)] }}
SERVICE: ${{ secrets[format('TF_{0}_CLIENT_SERVICE', steps.environment_name.outputs.ENVIRONMENT)] }}
run: |-
docker build -f client/Dockerfile.prod -t "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}" ./client
- name: Push Container
if: ${{ steps.applicable_check.outputs.flag && !inputs.dry_run }}
env:
REPOSITORY: ${{ secrets[format('TF_{0}_CLIENT_REPOSITORY', steps.environment_name.outputs.ENVIRONMENT)] }}
SERVICE: ${{ secrets[format('TF_{0}_CLIENT_SERVICE', steps.environment_name.outputs.ENVIRONMENT)] }}
run: |-
docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}"
# tag as "latest"
docker tag "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}" "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:latest"
docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:latest"
- name: Deploy to Cloud Run
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }}
if: ${{ steps.applicable_check.outputs.flag && !inputs.dry_run }}
env:
REPOSITORY: ${{ secrets[format('TF_{0}_CLIENT_REPOSITORY', steps.environment_name.outputs.ENVIRONMENT)] }}
SERVICE: ${{ secrets[format('TF_{0}_CLIENT_SERVICE', steps.environment_name.outputs.ENVIRONMENT)] }}
Expand All @@ -171,7 +196,7 @@ jobs:

# If required, use the Cloud Run url output in later steps
- name: Show Output
run: echo ${{ steps.deploy.outputs.url }}
run: echo ${{ steps.deploy.outputs.url || 'No URL generated' }}

deploy_cms:
# Add 'id-token' with the intended permissions for workload identity federation
Expand Down Expand Up @@ -206,16 +231,23 @@ jobs:
} >> $GITHUB_OUTPUT
id: extract_branch

- name: Applicable check
id: applicable_check
run: |
{
echo "flag=${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}"
} >> $GITHUB_OUTPUT
- name: Set environment name
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
id: environment_name
run: |
{
echo "ENVIRONMENT=${{ steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}"
echo "ENVIRONMENT=${{ inputs.ENVIRONMENT_NAME_OVERRIDE ||steps.extract_branch.outputs.branch == 'main' && 'PRODUCTION' || steps.extract_branch.outputs.branch_upper }}"
} >> $GITHUB_ENV
- name: Output secrets and vars as JSON
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail)
run: |
{
Expand All @@ -229,7 +261,7 @@ jobs:
id: env_json

- name: Output secrets and vars as key=value entries
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use jq to convert JSON to key=value entries
# 1. to_entries converts JSON to array of key/value pairs
# 2. map(.key + "=" + .value) converts each key/value pair to key=value
Expand All @@ -243,7 +275,7 @@ jobs:
id: env_entries_all

- name: Filter secrets and vars for inclusion in .env file by environment and application prefixes
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_
run: |
{
Expand All @@ -254,69 +286,37 @@ jobs:
id: env_entries_filtered

- name: Strip environment and application prefixes from secret and var names
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
# Use sed to strip environment and application prefixes from secret and var names
run: |
{
echo 'entries_stripped<<EOF'
echo '${{ steps.env_entries_filtered.outputs.entries_filtered }}' | sed -E "s/^(TF_)?("$ENVIRONMENT"_)?"$APP_ENV_PREFIX"_//"
echo '${{ steps.env_entries_filtered.outputs.entries_filtered }}' | sed -E "s/^(TF_)?("$ENVIRONMENT"_)?"$APP_ENV_PREFIX"_//g"
echo 'EOF'
} >> $GITHUB_OUTPUT
id: env_entries_stripped

- name: Save .env file
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
if: ${{ steps.applicable_check.outputs.flag }}
run: |
echo '${{ steps.env_entries_stripped.outputs.entries_stripped }}' >> $APP_ENV_PATH
cat $APP_ENV_PATH
- name: Google Auth
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
id: auth
uses: 'google-github-actions/auth@v1'
with:
credentials_json: "${{ secrets[format('TF_{0}_GCP_SA_KEY', steps.environment_name.outputs.ENVIRONMENT)] }}"
token_format: 'access_token'

# Authenticate Docker to Google Cloud Artifact Registry via credentials json
- name: Docker Auth
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
id: docker-auth
uses: 'docker/login-action@v3'
- name: Deploy and Push to Docker
if: ${{ steps.applicable_check.outputs.flag }}
id: deploy_and_push
uses: ./.github/actions/build-and-deploy-to-cloud-run
with:
registry: ${{ env.GAR_LOCATION }}-docker.pkg.dev
username: _json_key
password: ${{ secrets[format('TF_{0}_GCP_SA_KEY', steps.environment_name.outputs.ENVIRONMENT)] }}

- name: Build and Push Container
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
env:
COMPONENT_PATH: "./cms"
ENVIRONMENT_NAME: ${{ steps.environment_name.outputs.ENVIRONMENT }}
GAR_LOCATION: ${{ env.GAR_LOCATION }}
PROJECT_ID: ${{ env.PROJECT_ID }}
REGION: ${{ env.REGION }}
REPOSITORY: ${{ secrets[format('TF_{0}_CMS_REPOSITORY', steps.environment_name.outputs.ENVIRONMENT)] }}
SERVICE: ${{ secrets[format('TF_{0}_CMS_SERVICE', steps.environment_name.outputs.ENVIRONMENT)] }}
run: |-
docker build -f cms/Dockerfile.prod -t "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}" ./cms
docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}"
# tag as "latest"
docker tag "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}" "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:latest"
docker push "${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:latest"
- name: Deploy to Cloud Run
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
env:
REPOSITORY: ${{ secrets[format('TF_{0}_CMS_REPOSITORY', steps.environment_name.outputs.ENVIRONMENT)] }}
SERVICE: ${{ secrets[format('TF_{0}_CMS_SERVICE', steps.environment_name.outputs.ENVIRONMENT)] }}
id: deploy
uses: google-github-actions/deploy-cloudrun@v1
with:
service: ${{ env.SERVICE }}
region: ${{ env.REGION }}
image: ${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE }}:${{ github.sha }}
# NOTE: You can also set env variables here:
# env_vars: |
# NODE_ENV=production
# TOKEN_EXPIRE=6400
DRY_RUN: ${{ inputs.dry_run }}

# If required, use the Cloud Run url output in later steps
- name: Show Output
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }}
run: echo ${{ steps.deploy.outputs.url }}
if: ${{ steps.applicable_check.outputs.flag }}
run: echo ${{ steps.deploy_and_push.outputs.url || 'No URL generated' }}

0 comments on commit 2af49a0

Please sign in to comment.