Pipeline
The CI/CD pipeline runs on GitHub Actions using a self-hosted macOS runner. Every push triggers the full pipeline. Deployment to minikube only runs on main.
Jobs
lint → type-check → test → build-and-scan → helm-lint → smoke-test → deploy (main only)
| Job | What it does |
|---|---|
lint | ESLint on all src/**/*.ts files |
type-check | tsc --noEmit — no emit, just type errors |
test | Jest with coverage thresholds (80% statements/functions/lines, 75% branches) |
build-and-scan | docker build, then Trivy image scan for CRITICAL/HIGH CVEs |
helm-lint | helm lint on the chart |
smoke-test | Loads the image into minikube, does helm upgrade --wait, port-forwards, runs curl health checks |
deploy | Builds a short-SHA-tagged image, loads it into minikube, runs helm upgrade --wait |
Trigger
on:
push:
branches: ['**']
All branches get CI. Only main gets deployment.
Self-hosted runner
The pipeline uses runs-on: self-hosted on all jobs. The runner is a macOS machine with Docker, minikube, kubectl, and Helm pre-installed.
Setup
- Go to Settings → Actions → Runners → New self-hosted runner in your GitHub repo.
- Follow the instructions to download and configure the runner.
- Start the runner:
cd actions-runner
./run.sh
Or register it as a launchd service to start on boot:
./svc.sh install
./svc.sh start
Deployment
The deploy job:
- Builds
timeserver:<short-sha>(first 7 chars of commit SHA) - Loads the image into minikube with
minikube image load - Runs
helm upgrade --install --wait timeserver helm/timeserverwith the new image tag - The
--waitflag blocks until all pods are healthy before the job succeeds
Secrets
No secrets are required for the current pipeline. If you add a values.secret.yaml, store it as a GitHub Actions secret and write it to disk in the deploy job before running helm.
Node.js deprecation fix
All jobs set:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
This suppresses the Node.js 20 deprecation warning from actions/checkout and actions/setup-node.