Skip to content

Deploy to GKE

Deploy to GKE #288

Workflow file for this run

name: Deploy to GKE
on:
workflow_dispatch:
inputs:
environment:
description: Environment to deploy to
required: true
default: development
type: choice
options:
- development
- production
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Read version
id: version
run: |
VERSION=$(tr -d '[:space:]' < .version)
[ -n "$VERSION" ] || { echo "Missing .version"; exit 1; }
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ vars.WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.SERVICE_ACCOUNT }}
- uses: google-github-actions/get-gke-credentials@v2
with:
cluster_name: ${{ vars.GKE_CLUSTER }}
location: ${{ vars.GKE_LOCATION }}
- name: Verify images exist
run: |
REGISTRY="${{ vars.REGISTRY }}/${{ vars.PROJECT_ID }}/${{ vars.REPOSITORY }}"
VERSION="${{ steps.version.outputs.version }}"
for IMAGE in whisp-app whisp-api whisp-db-migrate; do
IMAGE_REF="${REGISTRY}/${IMAGE}:${VERSION}"
echo "Checking ${IMAGE_REF}"
gcloud artifacts docker images describe "${IMAGE_REF}" >/dev/null || {
echo "Image not found: ${IMAGE_REF}"
exit 1
}
done
- name: Deploy
env:
GKE_NAMESPACE: ${{ vars.GKE_NAMESPACE }}
ENVIRONMENT: ${{ github.event.inputs.environment }}
VERSION: ${{ steps.version.outputs.version }}
IMAGE_REGISTRY: ${{ vars.REGISTRY }}/${{ vars.PROJECT_ID }}/${{ vars.REPOSITORY }}
CLOUDSQL_INSTANCE: ${{ vars.CLOUDSQL_INSTANCE }}
GCS_BUCKET: ${{ vars.GCS_BUCKET }}
ENV_SECRET: ${{ vars.ENV_SECRET }}
GEE_SECRET: ${{ vars.GEE_SECRET }}
GCP_SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }}
GKE_LB_STATIC_IP_NAME: ${{ vars.GKE_LB_STATIC_IP_NAME }}
HOST_URL: ${{ vars.HOST_URL }}
run: |
set -euo pipefail
VERSION_SLUG=$(echo "$VERSION" | tr '[:upper:]' '[:lower:]' | tr '.+' '-')
export VERSION_SLUG
INGRESS_HOST="${HOST_URL#https://}"
INGRESS_HOST="${INGRESS_HOST#http://}"
INGRESS_HOST="${INGRESS_HOST%%/*}"
export INGRESS_HOST
export DEPLOYED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
VARS='$GKE_NAMESPACE $ENVIRONMENT $VERSION $VERSION_SLUG $IMAGE_REGISTRY $CLOUDSQL_INSTANCE $GCS_BUCKET $ENV_SECRET $GEE_SECRET $GCP_SERVICE_ACCOUNT $GKE_LB_STATIC_IP_NAME $HOST_URL $INGRESS_HOST $DEPLOYED_AT'
OUT=/tmp/k8s
mkdir -p "$OUT"
for f in infra/k8s/[0-9]*.yaml; do
envsubst "$VARS" < "$f" > "$OUT/$(basename "$f")"
done
shopt -s nullglob
for tier in 00 01 02 03 04 05 06 07 08 09; do
if [ "$tier" = "02" ]; then
for f in "$OUT/${tier}."*.yaml; do
grep -q '^apiVersion:' "$f" || continue
kubectl delete -f "$f" --ignore-not-found
done
fi
for f in "$OUT/${tier}."*.yaml; do
grep -q '^apiVersion:' "$f" || continue
kubectl apply -f "$f"
done
case $tier in
01|03) kubectl wait --for=condition=available deployment --all -n "$GKE_NAMESPACE" --timeout=300s ;;
02)
kubectl wait --for=condition=complete job --all -n "$GKE_NAMESPACE" --timeout=300s &
wp=$!
while kill -0 $wp 2>/dev/null; do
kubectl get job -n "$GKE_NAMESPACE" -o jsonpath='{range .items[*]}{.status.conditions[?(@.type=="Failed")].status}{"\n"}{end}' | grep -qx True && {
kill $wp 2>/dev/null || true
wait $wp 2>/dev/null || true
kubectl get job -n "$GKE_NAMESPACE"
exit 1
}
sleep 2
done
wait $wp
;;
esac
done