GitHub Actions Deploy
September 12, 2024
Introduction
Today, I'd like to delve into the primary CICD pipeline I use to deploy to multiple environments: GitHub Actions. As an independent developer, this will be one of the most common platforms to utilize when working on multiple projects due to its high visibility, versatility, and acceptance.
My background was primarily Gitlab CICD-focused; while there are similarities between GitHub Actions and Gitlab CICD, the differences are enough to where one simply cannot drag-and-drop a pre-existing YAML file and pray that it works. While you can always manually deploy from your V.S. Code terminal and check on the deployment at the destination, this paradigm will inevitably fail. One of the core tenants of CICD is the ability to always deploy tested code to a production environment without fear of failure.
TL;DR - GCP
name: Build, Deploy to GCP Cloud Run / Lambda
on:
workflow_run:
workflows: [CodeQL]
types:
- completed
jobs:
build:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t ${{ vars.SERVICE }}:${{ github.sha }} ./
- name: Save Docker image to tar file
run: |
docker save ${{ vars.SERVICE }}:${{ github.sha }} -o ${{ vars.SERVICE }}_${{ github.sha }}.tar
- name: Upload image artifact
uses: actions/upload-artifact@v4
with:
name: ${{ vars.SERVICE }}_${{ github.sha }}
path: ${{ vars.SERVICE }}_${{ github.sha }}.tar
retention-days: 1
deploy-gcp:
# Add 'id-token' with the intended permissions for workload identity federation
needs:
[build]
permissions:
contents: 'read'
id-token: 'write'
runs-on: ubuntu-latest
steps:
- name: Download image artifact
uses: actions/download-artifact@v4
with:
name: ${{ vars.SERVICE }}_${{ github.sha }}
path: /tmp
- name: Google Auth
id: auth
uses: 'google-github-actions/auth@v2'
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- id: 'access-secret'
run: |-
curl https://secretmanager.googleapis.com/v1/projects/my-project/secrets/my-secret/versions/1:access \
--header "Authorization: Bearer ${{ steps.auth.outputs.access_token }}"
# BEGIN - Docker auth and build (NOTE: If you already have a container image, these Docker steps can be omitted)
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
version: 'latest'
- name: Authenticate Docker with GCR
run: |
gcloud auth configure-docker ${{ vars.GAR_LOCATION }}-docker.pkg.dev --quiet
gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://gcr.io
- name: Load, Build and Push Container
run: |-
docker load -i /tmp/${{ vars.SERVICE }}_${{ github.sha }}.tar
docker image tag ${{ vars.SERVICE }}:${{ github.sha }} ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ vars.PROJECT_ID }}/${{ vars.SERVICE }}-0/${{ vars.SERVICE }}:${{ github.sha }}
docker push ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ vars.PROJECT_ID }}/${{ vars.SERVICE }}-0/${{ vars.SERVICE }}:${{ github.sha }}
# END - Docker auth and build
- name: Deploy to Cloud Run
id: deploy
uses: google-github-actions/deploy-cloudrun@v2
with:
service: ${{ vars.SERVICE }}
region: ${{ vars.REGION }}
image: ${{ vars.GAR_LOCATION }}-docker.pkg.dev/${{ vars.PROJECT_ID }}/${{ vars.SERVICE }}-0/${{ vars.SERVICE }}:${{ github.sha }}
# If required, use the Cloud Run url output in later steps
- name: Show Output
run: echo ${{ steps.deploy.outputs.url }}
Why
In my search for hosting a portfolio website as thrifty as possible, I settled on deploying to Google Cloud Platform (GCP), specifically utilizing Google's Cloud Run. Cloud Run has a generous free-tier that allows one to host serverless applications, which is perfect for a static website. Although you can host a static website for free via GitHub Pages, I decided to add the additional challenge of the following: custom domain (Presentation matters) and the beginning of a multi-cloud deployment strategy.