Overview

GitHub Actions is the CI tool. It runs automatically on every push to main in the fintrack-backend and fintrack-frontend repositories.

The full pipeline for the backend:

Developer pushes code to fintrack-backend
         ↓
GitHub Actions triggers
         ↓
Assumes AWS IAM role via OIDC (no stored keys)
         ↓
Builds Docker image → pushes to ECR
         ↓
Creates prisma-migrate Job inside EKS cluster
(cluster has VPC access to RDS - CI runner does not)
         ↓
Waits for migration to complete → prints logs
         ↓
Updates image tag in fintrack-gitops/manifests/backend/deployment.yaml
         ↓
ArgoCD detects the change → rolling update deploy

Step 1 - How OIDC Works (No Stored Credentials)

Traditional CI stores AWS access keys as GitHub secrets - a security risk. We use OIDC instead.

When GitHub Actions runs, it:

  1. Requests a short-lived OIDC token from GitHub
  2. Sends that token to AWS STS
  3. STS validates the token against the trusted OIDC provider
  4. Returns temporary credentials (valid for the duration of the job only)
  5. CI uses those credentials - no keys stored anywhere

The only secrets stored in GitHub are:

Secret Value Purpose
AWS_ROLE_ARN arn:aws:iam::203637463799:role/fintrack-dev-github-actions-role Role to assume via OIDC
GITOPS_PAT GitHub Personal Access Token Push image tag updates to fintrack-gitops

Step 2 - The CI Workflow File

Located at .github/workflows/ci.yaml in fintrack-backend:

name: Backend CI

on:
  push:
    branches: [main]

env:
  AWS_REGION: us-east-1
  ECR_REPOSITORY: fintrack-backend
  GITOPS_REPO: qezman/fintrack-gitops

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      id-token: write    # required for OIDC
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and push Docker image
        id: build
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

      - name: Run Prisma migrations in cluster
        run: |
          aws eks update-kubeconfig --name fintrack-dev --region $AWS_REGION
          kubectl delete job prisma-migrate -n fintrack 2>/dev/null || true
          # applies migration Job with current image tag
          # Job runs inside cluster → has VPC access to RDS
          # waits 120s for completion → prints logs → Job self-destructs

      - name: Update image tag in gitops repo
        run: |
          # clones fintrack-gitops
          # replaces image tag in manifests/backend/deployment.yaml
          # commits and pushes → ArgoCD detects → deploys