Skip to content
Merged
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
60 changes: 31 additions & 29 deletions .github/workflows/deploy-to-dev.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
name: Deploy Survey to Dev (GitOps)
# Post AWS->DO migration: main builds/pushes to GHCR and deploys straight to
# do-prod (the only live cluster — EKS dev/prod are retired). Filename kept to
# preserve any required-status-check config; see the job below for the real target.
name: Build & Deploy Survey to Prod (GitOps)

on:
push:
Expand Down Expand Up @@ -29,7 +32,11 @@ jobs:
cache-dependency-path: web/package-lock.json

- name: Install templ
run: go install github.com/a-h/templ/cmd/templ@latest
# Pin the CLI to the go.mod runtime version (github.com/a-h/templ) so
# `templ generate` emits code the pinned runtime can compile. `@latest`
# drifts ahead of the runtime (e.g. templ.ResolveAttributeValue) and
# breaks the build. Bump this in lockstep with go.mod.
run: go install github.com/a-h/templ/cmd/templ@v0.3.960

- name: Install dependencies
run: go mod download
Expand All @@ -52,46 +59,42 @@ jobs:
build-and-deploy:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write # push to ghcr.io/openmeet-team/survey via GITHUB_TOKEN
outputs:
image_tag: ${{ steps.set-outputs.outputs.image_tag }}

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
# Auth to GHCR. GITHUB_TOKEN works once the package is linked to this repo
# (Package settings -> Manage Actions access -> add survey = Write).
# To use a PAT instead: set password to ${{ secrets.GHCR_PAT }} and drop the
# packages: write permission above.
- name: Log in to GHCR
uses: docker/login-action@v3
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: true
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set image tag
id: set-outputs
run: |
echo "IMAGE_TAG=${{ github.sha }}" >> $GITHUB_ENV
echo "image_tag=${{ github.sha }}" >> $GITHUB_OUTPUT

- name: Build, tag, and push image to Amazon ECR
- name: Build and push image to GHCR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: openmeet-ecr/survey
GIT_REVISION: ${{ github.sha }}
GIT_BRANCH: ${{ github.ref_name }}
IMAGE: ghcr.io/openmeet-team/survey
run: |
echo "Building image with tag: $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

# Tag as latest for main branch
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
set -euo pipefail
echo "Building $IMAGE:$IMAGE_TAG"
docker build -t "$IMAGE:$IMAGE_TAG" -t "$IMAGE:latest" .
docker push "$IMAGE:$IMAGE_TAG"
docker push "$IMAGE:latest"

gitops-update:
needs: [build-and-deploy]
Expand All @@ -100,9 +103,8 @@ jobs:
with:
service_name: survey
image_tag: ${{ needs.build-and-deploy.outputs.image_tag }}
environment: dev
ecr_registry: 433321780850.dkr.ecr.us-east-1.amazonaws.com
ecr_repository: openmeet-ecr/survey
commit_message: "GitOps: Update Survey to ${{ needs.build-and-deploy.outputs.image_tag }} from main branch"
image_name: ghcr.io/openmeet-team/survey
environment: do-prod
commit_message: "GitOps: deploy survey ${{ needs.build-and-deploy.outputs.image_tag }} to do-prod (main)"
secrets:
GH_PAT: ${{ secrets.GH_PAT_INFRASTRUCTURE }}
102 changes: 54 additions & 48 deletions .github/workflows/update-image-gitops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,30 @@ on:
workflow_call:
inputs:
service_name:
description: 'Service name (api, platform, survey, etc.)'
description: 'Service name (api, platform, biz, ...) — used for log/commit context'
required: true
type: string
image_tag:
description: 'Docker image tag to deploy'
description: 'Image tag (git SHA) to deploy'
required: true
type: string
environment:
description: 'Target environment (dev, prod, pr-123, etc.)'
required: true
type: string
ecr_registry:
description: 'ECR registry URL'
image_name:
description: 'Full GHCR image name to pin (matched against images[].newName), e.g. ghcr.io/openmeet-team/openmeet-api'
required: true
type: string
ecr_repository:
description: 'ECR repository name'
required: true
environment:
description: 'Target overlay under k8s/environments/ (do-prod is the only live cluster post-migration)'
required: false
type: string
default: do-prod
commit_message:
description: 'Git commit message'
required: false
type: string
default: 'Update image tag'
secrets:
GH_PAT:
description: 'GitHub PAT with repo access'
description: 'GitHub PAT with write access to the openmeet-infrastructure repo'
required: true

jobs:
Expand All @@ -44,67 +41,76 @@ jobs:
token: ${{ secrets.GH_PAT }}
ref: main

- name: Update kustomization.yaml with new image tag
- name: Pin ${{ inputs.service_name }} image to ${{ inputs.image_tag }}
env:
IMAGE_NAME: ${{ inputs.image_name }}
IMAGE_TAG: ${{ inputs.image_tag }}
ENVIRONMENT: ${{ inputs.environment }}
run: |
KUSTOMIZATION_PATH="k8s/environments/${{ inputs.environment }}/kustomization.yaml"
IMAGE_FULL="${{ inputs.ecr_registry }}/${{ inputs.ecr_repository }}:${{ inputs.image_tag }}"

echo "Updating $KUSTOMIZATION_PATH"
echo "Service: ${{ inputs.service_name }}"
echo "New image: $IMAGE_FULL"

# Update the image tag in the kustomization file
# This uses sed to find the line with the service image and update it
sed -i "s|image: .*${{ inputs.ecr_repository }}:.*|image: $IMAGE_FULL|" "$KUSTOMIZATION_PATH"
set -euo pipefail
KUSTOMIZATION_PATH="k8s/environments/${ENVIRONMENT}/kustomization.yaml"
echo "Pinning $IMAGE_NAME -> $IMAGE_TAG in $KUSTOMIZATION_PATH"

# Single source of truth: the overlay's `images:` transformer. Set newTag
# on EVERY entry whose newName is this service's GHCR image. Keying on
# newName (not the ECR lookup key) updates all references at once —
# e.g. survey has two entries (bare `survey` + the ECR name) that share
# one newName. yq (mikefarah) is preinstalled on ubuntu-latest runners.
yq -i '(.images[] | select(.newName == strenv(IMAGE_NAME))).newTag = strenv(IMAGE_TAG)' "$KUSTOMIZATION_PATH"

# Guard: fail loudly if nothing matched (typo in image_name, or the
# overlay has no images[] entry for this service) — never push a no-op
# that silently fails to deploy.
matched="$(yq '[.images[] | select(.newName == strenv(IMAGE_NAME) and .newTag == strenv(IMAGE_TAG))] | length' "$KUSTOMIZATION_PATH")"
if [ "$matched" -eq 0 ]; then
echo "::error::No images[] entry with newName=$IMAGE_NAME in $KUSTOMIZATION_PATH — nothing to deploy."
exit 1
fi
echo "Updated $matched image reference(s)."

# Show the diff
git diff "$KUSTOMIZATION_PATH"

- name: Commit and push changes
env:
ENVIRONMENT: ${{ inputs.environment }}
COMMIT_MESSAGE: ${{ inputs.commit_message }}
run: |
set -euo pipefail
git config --global user.email "github-actions@github.com"
git config --global user.name "GitHub Actions Bot"

KUSTOMIZATION_PATH="k8s/environments/${{ inputs.environment }}/kustomization.yaml"

KUSTOMIZATION_PATH="k8s/environments/${ENVIRONMENT}/kustomization.yaml"
git add "$KUSTOMIZATION_PATH"

# Check if there are changes to commit
if git diff --staged --quiet; then
echo "No changes to commit - image tag may already be up to date"
echo "No changes to commit image tag already up to date."
exit 0
fi

git commit -m "${{ inputs.commit_message }}"
git commit -m "$COMMIT_MESSAGE"

# Push with retry logic to handle concurrent updates
# Push with retry to handle concurrent gitops updates from sibling repos.
MAX_RETRIES=5
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
echo "Attempting to push (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)..."

if git push origin main; then
echo "Successfully pushed changes"
echo "Successfully pushed changes"
exit 0
else
echo "Push failed, pulling and retrying..."
RETRY_COUNT=$((RETRY_COUNT + 1))

if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
# Pull with rebase to get latest changes
git pull --rebase origin main
sleep $((RETRY_COUNT * 2)) # Exponential backoff
fi
fi
echo "⚠️ Push failed, pulling and retrying..."
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
git pull --rebase origin main
sleep $((RETRY_COUNT * 2))
fi
done

echo "Failed to push after $MAX_RETRIES attempts"
echo "Failed to push after $MAX_RETRIES attempts"
exit 1

- name: Trigger ArgoCD sync (optional)
if: inputs.environment == 'dev'
continue-on-error: true
- name: Deployment note
run: |
echo "ArgoCD will auto-detect the change within 3 minutes"
echo "Or manually sync via UI: https://argocd-dev.openmeet.net"
echo "Committed new tag to k8s/environments/${{ inputs.environment }}."
echo "ArgoCD (do-prod app, automated sync) will roll this out within ~3 minutes."
echo "Watch: kubectl -n prod rollout status deployment/${{ inputs.service_name }}"
Loading