CI/CD Pipeline
Forja uses GitHub Actions for continuous integration. The pipeline runs on every push to main and on every pull request targeting main.
Pipeline Overview
The CI configuration lives at .github/workflows/ci.yml and defines two parallel jobs:
┌─────────────────────────────────────────┐
│ CI Pipeline │
├─────────────────┬───────────────────────┤
│ Backend (Rust) │ Admin (React) │
│ │ │
│ 1. Checkout │ 1. Checkout │
│ 2. Rust setup │ 2. Node.js 20 setup │
│ 3. Cache deps │ 3. npm install │
│ 4. Format check│ 4. Type check │
│ 5. Clippy lint │ 5. ESLint │
│ 6. Init DB │ 6. Tests │
│ 7. Unit tests │ │
│ 8. Integ tests │ │
└─────────────────┴───────────────────────┘
Both jobs run in parallel. The pipeline passes only when both jobs succeed.
Backend Job
The backend job runs on ubuntu-latest with a PostgreSQL 16 service container.
Service Container
A PostgreSQL 16 Alpine container starts automatically with health checks:
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: forja
POSTGRES_PASSWORD: forja
POSTGRES_DB: forja
options: >-
--health-cmd "pg_isready -U forja"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
Steps
-
Checkout -- Uses
actions/checkout@v4. -
Install Rust toolchain -- Uses
dtolnay/rust-toolchain@stablewithrustfmtandclippycomponents. -
Cache cargo registry and build -- Uses
actions/cache@v4to cache~/.cargo/registry,~/.cargo/git, andbackend/target. The cache key is based onCargo.lock. -
Check formatting --
cargo fmt --checkfails the build if any file is not properly formatted. -
Lint with Clippy --
cargo clippy -- -D warningstreats all warnings as errors. -
Initialize databases -- Creates the required PostgreSQL extensions on both the main and test databases:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "citext";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";Also creates a separate
forja_testdatabase for integration tests. -
Run unit tests --
cargo test --libruns all unit tests (no database required). -
Run integration tests --
cargo test --test integration_testsruns tests against the test database usingTEST_DATABASE_URL.
Environment Variables
env:
DATABASE_URL: postgres://forja:forja@localhost:5432/forja
TEST_DATABASE_URL: postgres://forja:forja@localhost:5432/forja_test
Admin Job
The admin job runs on ubuntu-latest with Node.js 20.
Steps
-
Checkout -- Uses
actions/checkout@v4. -
Setup Node.js -- Uses
actions/setup-node@v4with Node.js 20. -
Install dependencies --
npm installin theadmin/directory. -
Type check --
npm run typecheckruns the TypeScript compiler in check mode. -
Lint --
npm run lintruns ESLint. -
Run tests --
npm testruns the Vitest test suite.
Caching Strategy
The backend job caches three directories:
| Path | Purpose |
|---|---|
~/.cargo/registry | Downloaded crate sources |
~/.cargo/git | Git-based dependencies |
backend/target | Compiled artifacts |
The cache key is ${{ runner.os }}-cargo-${{ hashFiles('backend/Cargo.lock') }}, so the cache is invalidated whenever dependencies change. A restore key (${{ runner.os }}-cargo-) provides a fallback to the most recent cache.
Triggers
on:
push:
branches: [main]
pull_request:
branches: [main]
The pipeline runs on:
- Every push to
main(direct pushes and merged pull requests). - Every pull request targeting
main(opened, synchronized, reopened).
Adding New Checks
To add a new CI step, edit .github/workflows/ci.yml. For example, to add a security audit:
- name: Security audit
run: cargo audit
Or to add an admin build check:
- name: Build admin
run: npm run build
Running CI Checks Locally
Use the dev-test.sh script to run the same checks locally before pushing:
# Run all checks (matches CI)
./scripts/dev-test.sh
# Include integration tests (requires running database)
./scripts/dev-test.sh --integration