-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build(Github): Adds main Github Action workflow file
- Loading branch information
1 parent
837c71b
commit 43895f9
Showing
1 changed file
with
322 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,322 @@ | ||
# The workflow processes GH secrets and variables managed by Terraform or manually, some of them for general usage in | ||
# the github jobs, and some which component-relevant (cms, client, etc), used to build the .env files for the containers. | ||
# These follow the naming convention: | ||
# - TF_[PRODUCTION|<UPPER CASE BRANCH NAME>]_[CLIENT_ENV|CMS_ENV|]_* - managed by Terraform | ||
# - [PRODUCTION|<UPPER CASE BRANCH NAME>]_[CLIENT_ENV|CMS_ENV|]_* - managed manually | ||
|
||
name: Run deploy to GCP | ||
|
||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: | ||
- main | ||
- staging | ||
|
||
paths: | ||
- 'client/**' | ||
- 'cms/**' | ||
- '.github/workflows/*' | ||
- 'infrastructure/**' | ||
|
||
env: | ||
PROJECT_ID: ${{ secrets.TF_GCP_PROJECT_ID }} | ||
GAR_LOCATION: ${{ secrets.TF_GCP_REGION }} | ||
REGION: ${{ secrets.TF_GCP_REGION }} | ||
|
||
jobs: | ||
deploy_client: | ||
# Add 'id-token' with the intended permissions for workload identity federation | ||
permissions: | ||
contents: 'read' | ||
id-token: 'write' | ||
|
||
runs-on: ubuntu-latest | ||
env: | ||
APP_ENV_PREFIX: CLIENT_ENV | ||
APP_ENV_PATH: client/.env.local | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- uses: dorny/paths-filter@v3 | ||
id: client-changes | ||
with: | ||
filters: | | ||
client: | ||
- 'client/**' | ||
- '.github/workflows/**' | ||
- name: Extract branch name | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | ||
run: | | ||
{ | ||
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} | ||
echo "branch=${branch}" | ||
echo "branch_upper=${branch^^}" | ||
} >> $GITHUB_OUTPUT | ||
id: extract_branch | ||
|
||
- name: Set environment name | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | ||
id: environment_name | ||
run: | | ||
{ | ||
echo "ENVIRONMENT=${{ 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' }} | ||
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail) | ||
run: | | ||
{ | ||
echo 'secrets<<EOF' | ||
echo '${{ secrets != null && toJSON(secrets) || null }}' | ||
echo 'EOF' | ||
echo 'vars<<EOF' | ||
echo '${{ vars != null && toJSON(vars) || null }}' | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
id: env_json | ||
|
||
- name: Output secrets and vars as key=value entries | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | ||
# 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 | ||
# 3. .[] flattens array to key=value entries | ||
run: | | ||
{ | ||
echo 'entries_all<<EOF' | ||
echo '${{ steps.env_json.outputs.secrets }}' '${{ steps.env_json.outputs.vars }}' | jq -r 'to_entries | map(.key + "=" + .value) | .[]' | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
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' }} | ||
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_ | ||
run: | | ||
{ | ||
echo 'entries_filtered<<EOF' | ||
echo '${{ steps.env_entries_all.outputs.entries_all }}' | grep -E "^(TF_)?(${ENVIRONMENT}_)?${APP_ENV_PREFIX}_" | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
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' }} | ||
# 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 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
id: env_entries_stripped | ||
|
||
- name: Save .env file | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.client-changes.outputs.client == 'true' }} | ||
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' }} | ||
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.client-changes.outputs.client == 'true' }} | ||
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' }} | ||
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 | ||
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' }} | ||
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)] }} | ||
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 | ||
|
||
# If required, use the Cloud Run url output in later steps | ||
- name: Show Output | ||
run: echo ${{ steps.deploy.outputs.url }} | ||
|
||
deploy_cms: | ||
# Add 'id-token' with the intended permissions for workload identity federation | ||
permissions: | ||
contents: 'read' | ||
id-token: 'write' | ||
|
||
runs-on: ubuntu-latest | ||
env: | ||
APP_ENV_PREFIX: CMS_ENV | ||
APP_ENV_PATH: cms/.env | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- uses: dorny/paths-filter@v3 | ||
id: cms-changes | ||
with: | ||
filters: | | ||
cms: | ||
- 'cms/**' | ||
- '.github/workflows/**' | ||
- name: Extract branch name | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | ||
run: | | ||
{ | ||
branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} | ||
echo "branch=${branch}" | ||
echo "branch_upper=${branch^^}" | ||
} >> $GITHUB_OUTPUT | ||
id: extract_branch | ||
|
||
- name: Set environment name | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | ||
id: environment_name | ||
run: | | ||
{ | ||
echo "ENVIRONMENT=${{ 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' }} | ||
# Use GH Actions toJSON function to convert secrets and vars to JSON; in case no values present, output null (otherwise jq will fail) | ||
run: | | ||
{ | ||
echo 'secrets<<EOF' | ||
echo '${{ secrets != null && toJSON(secrets) || null }}' | ||
echo 'EOF' | ||
echo 'vars<<EOF' | ||
echo '${{ vars != null && toJSON(vars) || null }}' | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
id: env_json | ||
|
||
- name: Output secrets and vars as key=value entries | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | ||
# 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 | ||
# 3. .[] flattens array to key=value entries | ||
run: | | ||
{ | ||
echo 'entries_all<<EOF' | ||
echo '${{ steps.env_json.outputs.secrets }}' '${{ steps.env_json.outputs.vars }}' | jq -r 'to_entries | map(.key + "=" + .value) | .[]' | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
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' }} | ||
# Use grep to filter client secrets & vars and save .env file (names starting with (TF_)((PRODUCTION|STAGING|SOMEBRANCH)_)[CLIENT_ENV|CMS_ENV]_ | ||
run: | | ||
{ | ||
echo 'entries_filtered<<EOF' | ||
echo '${{ steps.env_entries_all.outputs.entries_all }}' | grep -E "^(TF_)?(${ENVIRONMENT}_)?${APP_ENV_PREFIX}_" | ||
echo 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
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' }} | ||
# 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 'EOF' | ||
} >> $GITHUB_OUTPUT | ||
id: env_entries_stripped | ||
|
||
- name: Save .env file | ||
if: ${{ github.event_name == 'workflow_dispatch' || steps.cms-changes.outputs.cms == 'true' }} | ||
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' | ||
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: | ||
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 | ||
|
||
# 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 }} |