The LifeOps CI/CD pipeline is a 6-stage GitHub Actions workflow (.github/workflows/build-push.yaml) that builds, signs, scans, and deploys container images to the homelab Kubernetes cluster via GitOps.
Push to main/PR
│
▼
┌─────────────────────────────────────────────┐
│ Stage 0: Detect Changes │
│ Diff-based detection (frontend/backend) │
│ Manual dispatch support │
└──────────┬──────────────────────────────────┘
│
┌──────▼──────┐
│ Stage 1: CI │ (GitHub-hosted runners, parallel)
├─────────────┤
│ Lint Backend (golangci-lint) │
│ Lint Frontend (ESLint + tsc) │
│ Test Backend (go test -race) │
│ Test Frontend (jest) │
│ Secret Scan (Gitleaks) │
│ Vuln Scan Backend (govulncheck) │
│ Vuln Scan Frontend (npm audit) │
│ License Check Backend (go-licenses) │
│ License Check Frontend (license-checker)│
└──────────┬──────────────────────────────┘
│
┌──────────▼──────────────────────────────┐
│ Stage 2: CI Gate │
│ Lint + Test failures = BLOCK │
│ Vuln + License = advisory only │
└──────────┬──────────────────────────────┘
│
┌──────────▼──────────────────────────────┐
│ Stage 3: Container Build │
│ Self-hosted runners (arc-rss) │
│ Kaniko pods in-cluster │
│ Push to Harbor registry │
└──────────┬──────────────────────────────┘
│
┌──────────▼──────────────────────────────┐
│ Stage 4: Sign Images (Cosign) │
│ Key-based signing by digest │
│ Annotations: repo, sha, ref, run_id │
└──────────┬──────────────────────────────┘
│
┌──────────▼──────────────────────────────┐
│ Stage 5: Security Scan + SBOM │
│ Trivy image scan (HIGH, CRITICAL) │
│ Harbor auto-generated SBOM (SPDX) │
└──────────┬──────────────────────────────┘
│
┌──────────▼──────────────────────────────┐
│ Stage 6: Update Manifests │
│ Push new image tags to │
│ k8s-cluster-config/applications/ │
│ lifeops/values.yaml │
│ ArgoCD auto-syncs the change │
└─────────────────────────────────────────┘
- Uses
git diff to determine which components changed (frontend/, backend/)
- Supports
workflow_dispatch with options: all, frontend, backend, scan-only, update-manifests-only
- Outputs:
frontend, backend, sha_short, branch
All CI checks run in parallel on GitHub-hosted runners (ubuntu-latest):
| Check |
Backend |
Frontend |
| Lint |
golangci-lint v2.8.0 |
ESLint + TypeScript check |
| Tests |
go test -race -coverprofile |
Jest with coverage |
| Secret Scan |
Gitleaks v8.21.2 |
— |
| Vuln Scan |
govulncheck |
npm audit --audit-level=high |
| License Check |
go-licenses |
license-checker |
- Blocking: Lint and test failures prevent builds
- Advisory: Vulnerability and license checks are logged but don't block
- Runs on self-hosted runners (
arc-rss — ARC Runner Scale Set in K8s)
- Uses Kaniko pods for in-cluster container builds (no Docker daemon needed)
- Pushes to Harbor:
harbor.homelab.vyanh.uk/applications/lifeops-{backend,frontend}
- Tags:
<sha-short>, <branch>, latest (main only)
- PR builds:
--no-push (build-only validation)
- Caching: Kaniko cache repo in Harbor (
cache/backend, cache/frontend), TTL 168h
- Cosign key-based signing (private key from GitHub Secrets)
- Signs by digest (covers all tags)
- Annotations include: repo, SHA, ref, run ID
- Transparency log upload disabled (
--tlog-upload=false)
- Trivy scans for HIGH and CRITICAL vulnerabilities
- Harbor auto-generates SBOM (SPDX format)
- SBOM downloaded and uploaded as workflow artifact
- Checks out
k8s-cluster-config repo
- Updates image tags in
applications/lifeops/values.yaml using yq
- Commits and pushes → ArgoCD auto-syncs the deployment
| Variable |
Value |
HARBOR_REGISTRY |
harbor.homelab.vyanh.uk |
HARBOR_PROJECT |
applications |
GO_VERSION |
1.25 |
NODE_VERSION |
20 |
| Secret |
Purpose |
HARBOR_USERNAME |
Harbor robot account for push |
HARBOR_PASSWORD |
Harbor robot account password |
COSIGN_KEY |
Cosign private key for signing |
COSIGN_PASSWORD |
Cosign key passphrase |
K8S_CONFIG_PAT |
GitHub PAT for k8s-cluster-config repo |