Skip to main content

Overview

The CI/CD pipeline automates container builds and deployments via GitHub Actions:
  • Change detection: Only builds applications that have changed, keeping CI fast in a monorepo
  • Consistent builds: All containers are built in CI with the same environment and tooling
  • Rendered manifests: Kubernetes manifests are rendered and committed to Git, so what’s in the repository is exactly what ArgoCD deploys
  • Automatic staging deployment: Merges to main trigger builds and deploy to staging automatically

What’s Included

Workflow Structure

.github/workflows/
├── ci-build-push-containers.yml # Build and push container images
├── ci-test.yml                  # Run tests (placeholder)
├── gitops-update-manifests.yml  # Render and commit K8s manifests
└── terramate-*.yml              # Terraform orchestration
Terraform CI/CD workflows (preview, deploy, drift detection) are covered in Terraform Orchestration.

Change Detection

The workflow uses dorny/paths-filter with a separate filter configuration file:
# .github/utils/file-filters.yaml
services/go-backend:
  - "services/go-backend/**"
  - "kubernetes/src/services/go-backend/**"

services/another-service:
  - "services/another-service/**"
  - "kubernetes/src/services/another-service/**"
The workflow matrix builds only the services that match changed paths. Changes to shared dependencies (like the workflow itself) trigger rebuilds of all dependent services.

Build and Deploy Process

1

Detect changes [ci-build-push]

Determines which services have modifications based on path filters.
2

Build containers [ci-build-push]

Builds and pushes a container image to ECR for each changed service.
3

Trigger deploy [ci-build-push]

Triggers gitops-update-manifests for each built service.
4

Render manifests [gitops-update-manifests]

Generates Kubernetes manifests with the new image tags.
5

Commit manifests [gitops-update-manifests]

Pushes the updated manifests to the repository.
6

ArgoCD syncs

Detects the manifest changes via webhook or polling and deploys automatically.

Container Registry

Images are pushed to Amazon ECR with version tags derived from git describe:
<account>.dkr.ecr.<region>.amazonaws.com/go-backend:1.2.3-0001-g4b5d2e7
The tag format <version>-<commits>-g<hash> shows the base version from the last release tag, how many commits since that release, and the commit hash. This provides traceability, immutability (tags are never overwritten), and easy rollback.

Manifest Rendering

The pipeline renders Helm charts or Kustomize overlays into plain Kubernetes manifests:
kubernetes/
├── src/
│   └── services/
│       └── go-backend/
│           ├── Chart.yaml
│           └── values.yaml
└── rendered/
    └── staging/
        └── services/
            └── go-backend/
                └── manifests.yaml  # Generated by CI
Rendered manifests ensure what’s in Git is exactly what deploys, PRs show actual Kubernetes changes (not just Helm values), and Git history provides a full audit trail.

Key Design Decisions

DecisionRationale
Change detection with path filtersFast CI, minimal rebuilds in a monorepo
Versioned image tagsImmutable, traceable, rollback-friendly
Rendered manifests in GitArgoCD deploys exactly what’s committed
Staging auto-deploy on mergeMerged code is always running in staging