CI/CD Pipeline
GospeLib uses GitHub Actions for CI and ArgoCD for continuous deployment. Nx's affected commands ensure only changed projects are built and tested.
Pipeline Architecture
graph LR
PR["Push / PR"] --> Setup["setup<br/>(detect affected)"]
Setup --> Lint["lint<br/>(nx affected -t lint)"]
Setup --> TestJS["test-js<br/>(nx affected -t test)"]
Setup --> TestPy["test-python<br/>(pytest per service)"]
Setup --> TestGo["test-go<br/>(go test per service)"]
Lint & TestJS & TestPy & TestGo --> Build["build<br/>(Docker images)"]
Build --> Deploy["deploy<br/>(staging or prod)"]
CI Pipeline (All Branches + PRs)
Setup Job
Detects which projects were affected by the current change:
setup:
runs-on: ubuntu-latest
outputs:
affected: ${{ steps.affected.outputs.projects }}
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: nrwl/nx-set-shas@v4
- id: affected
run: echo "projects=$(pnpm nx show projects --affected --json)" >> $GITHUB_OUTPUT
Lint Job
lint:
needs: setup
steps:
- run: pnpm nx affected -t lint --parallel=4
Test Jobs
Tests run in parallel across three language ecosystems:
| Job | Framework | Infrastructure |
|---|---|---|
| test-js | Vitest (nx affected -t test) | None |
| test-python | pytest (per service) | FalkorDB + PostgreSQL containers |
| test-go | go test -race (per service) | None |
Python tests spin up real database containers in CI:
test-python:
services:
falkordb:
image: falkordb/falkordb:latest
ports: ['6379:6379']
postgres:
image: pgvector/pgvector:pg16
env: { POSTGRES_DB: gospelib_test, POSTGRES_USER: test, POSTGRES_PASSWORD: test }
ports: ['5432:5432']
Each test job checks the affected output and only runs if its service was changed:
- name: Test content service
if: contains(fromJson(needs.setup.outputs.affected), 'content')
run: cd services/content && uv run pytest tests/ --cov -v
Build Job
After all tests pass, Docker images are built for affected services:
build:
needs: [lint, test-js, test-python, test-go]
steps:
- name: Build Docker images (affected services only)
run: |
for svc in gateway content auth billing ai notifications; do
if echo '${{ needs.setup.outputs.affected }}' | grep -q "$svc"; then
docker build -t gospelib-$svc:${{ github.sha }} services/$svc/
fi
done
CD Pipeline — Staging
Merges to main automatically deploy to staging via ArgoCD:
sequenceDiagram
participant Dev as Developer
participant GH as GitHub
participant CI as CI Pipeline
participant ECR
participant ArgoCD
participant K8s as Staging Cluster
Dev->>GH: Merge PR to main
GH->>CI: Trigger cd-staging.yml
CI->>CI: Build Docker images
CI->>ECR: Push gospelib-*:$SHA
CI->>GH: Update Kustomize image tags in repo
ArgoCD->>GH: Detect git change (poll)
ArgoCD->>K8s: Apply updated manifests
ArgoCD->>ArgoCD: Report health status
Key steps:
- Build and push Docker images to ECR
- Update Kustomize overlay image tags in the repo
- ArgoCD detects the git change and applies the new manifests
- Wait for ArgoCD to report healthy sync
CD Pipeline — Production
Production deployments are triggered by release tags:
main branch → auto deploy to staging + EAS preview builds
release/v*.*.* → create release PR → full test suite + E2E
→ merge → tag → deploy to production
→ EAS submit to App Store + Play Store
Mobile CI/CD (EAS Build)
Mobile builds are triggered on main merges via Expo EAS:
eas-build:
needs: test-js
if: github.ref == 'refs/heads/main'
steps:
- uses: expo/expo-github-action@v8
with: { token: ${{ secrets.EXPO_TOKEN }} }
- run: cd apps/mobile && eas build --platform all --profile preview --non-interactive
CI Environment
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 22 | JS/TS ecosystem |
| pnpm | 9 | Package manager |
| Python | 3.12 | Python services |
| uv | latest | Python package manager |
| Go | 1.23 | Go services |
| Docker | latest | Container builds |
Nx Caching in CI
Nx caches build, test, lint, and typecheck results. With Nx Cloud, cache hits are shared across branches and CI runs:
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
Release Strategy
Version tagging uses semantic versioning via Release Please:
- 13 individually versioned components — all apps, services, and packages
- Tag format:
{component}/v{version}(e.g.,gateway/v1.2.0) - Changelog sections: Features, Bug Fixes, Performance Improvements, Reverts, Documentation
- Commit format: Conventional Commits enforced by commitlint
Related Pages
- Architecture Overview — Infrastructure as Code principle
- Scaling Strategy — How deployment changes across phases
- Secrets & Network Policy — CI secrets and OIDC configuration
- Monorepo & Tooling — Nx affected commands