feat: add CI/CD pipeline with OIDC, ECR, Docker Hub, lint and tests

This commit is contained in:
ikechukwu Elizabeth
2026-04-19 18:45:47 +01:00
parent 0a92e6838d
commit ee42969cca
11 changed files with 6073 additions and 35 deletions
+135
View File
@@ -0,0 +1,135 @@
# Week 4 - GitHub Actions CI/CD Pipeline
## Overview
Week 4 introduces a production-grade CI/CD pipeline to the Scalable Task Platform. Every push and pull request targeting `main` runs a full four-stage pipeline: lint, unit tests, integration test, and image publish. AWS authentication uses OIDC - no static credentials stored anywhere. Images are pushed to both Amazon ECR and Docker Hub on every successful merge to `main`.
---
## Pipeline Architecture
```
Push / PR to main
|
v
[ lint ]
|
v
[ unit-test ]
needs: lint
|
v
[ build-and-integration-test ]
needs: unit-test
|
v
[ publish ]
needs: build-and-integration-test
if: push to main only, skipped on PRs
|
+---------------------------+
| |
v v
Amazon ECR Docker Hub
task-backend:latest lizzycloudlab/task-backend:latest
task-backend:sha-<sha> lizzycloudlab/task-backend:sha-<sha>
task-frontend:latest lizzycloudlab/task-frontend:latest
task-frontend:sha-<sha> lizzycloudlab/task-frontend:sha-<sha>
```
---
## Files Added / Modified
| File | Change |
|---|---|
| `.github/workflows/ci-cd.yml` | Single workflow replacing two separate files |
| `docker-compose.yml` | Added healthcheck blocks, upgraded depends_on to condition: service_healthy |
| `app/backend/server.js` | Prefixed all routes with /api/, updated health endpoint response |
| `iam/github-actions-trust-policy.json` | OIDC trust policy scoped to this repo only |
| `iam/github-actions-ecr-policy.json` | Least-privilege ECR push permissions |
---
## AWS Authentication: OIDC
This pipeline uses OpenID Connect (OIDC) for AWS authentication. No AWS access keys are stored in GitHub secrets.
Each workflow run, GitHub generates a short-lived signed JWT token. AWS STS verifies the token against the registered GitHub OIDC provider and issues temporary credentials scoped to that job only. Credentials expire the moment the job finishes. Nothing to rotate, nothing to leak.
The IAM role `github-actions-scalable-task-platform` is scoped tightly:
- **Trust policy** - only tokens from `repo:elizabeth-ikechukwu/scalable-task-platform:*` can assume the role
- **Permissions policy** - ECR push to `task-backend` and `task-frontend` only, nothing else in the AWS account
---
## Image Tagging Strategy
| Tag | Purpose |
|---|---|
| `latest` | Always points to the most recent successful build |
| `sha-abc1234` | Immutable reference to the exact commit, used for rollback |
Every running container can be traced back to the exact line of code that produced it using the SHA tag.
---
## ECR Repositories
| Repository | URI |
|---|---|
| task-backend | `424522917561.dkr.ecr.us-east-1.amazonaws.com/task-backend` |
| task-frontend | `424522917561.dkr.ecr.us-east-1.amazonaws.com/task-frontend` |
Both repositories configured with: private visibility, mutable tags, AES-256 encryption, scan on push enabled.
---
## GitHub Secrets
| Secret | Purpose |
|---|---|
| `DOCKERHUB_USERNAME` | Docker Hub login username |
| `DOCKERHUB_TOKEN` | Docker Hub access token (not password) |
No AWS secrets needed. OIDC handles AWS authentication entirely.
---
## Docker Compose Health Checks
Both services now have health checks defined. Docker probes the backend every 10 seconds via `GET /api/health`. The frontend `depends_on` uses `condition: service_healthy` meaning Nginx will not start until the backend passes its health probe. This prevents the frontend from coming up pointing at a backend that is not ready.
---
## Branch Protection
`main` is protected with the following rules enforced:
- No direct pushes - all changes must go through a pull request
- Force pushes blocked
- Branch deletion blocked
All development happens on feature branches and merges to `main` via PR.
---
## Testing and Linting
The backend includes a full test and lint setup before the pipeline was built.
**ESLint** - configured via `eslint.config.js` using the new flat config format
required by ESLint v9+. Lints all `.js` files with a separate config block for
test files that includes Jest globals.
**Jest + Supertest** - four unit and integration tests covering:
- `GET /api/health` returns 200 with status ok
- `GET /api/tasks` returns empty array initially
- `POST /api/tasks` creates a task correctly
- `POST /api/tasks` returns 400 when title is missing
Both pass cleanly locally before the pipeline runs them in CI.
## What Week 5 Adds
Week 5 introduces Terraform to provision AWS infrastructure - VPC, subnets, security groups, and EC2 instance. The EC2 instance will pull images directly from ECR at deploy time using the same OIDC authentication pattern established this week.