> ## Documentation Index
> Fetch the complete documentation index at: https://docs.equa.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# CI/CD Pipeline

> How code gets built, tested, and deployed across Equa repositories

# CI/CD Pipeline

> **Last Verified:** 2026-05-03

This guide documents the continuous integration and deployment pipelines for the Equa platform. Each repository has its own CI configuration with different validation jobs and runtime targets.

## Pipeline Overview

```mermaid theme={null}
graph LR
    Dev["Developer Push"]
    GHA["GitHub Actions"]
    Build["Build + Lint"]
    Test["Run Tests"]
    Docker["Docker Build"]
    
    Dev --> GHA
    GHA --> Build
    Build --> Test
    Test --> Docker

    subgraph deployTargets ["Deployment Targets"]
        Railway["Railway (Current Interim Runtime)"]
        CloudRun["Google Cloud Run (Legacy Path)"]
        PM2["PM2 (Server)"]
    end

    Docker --> CloudRun
    Docker --> Railway
    Docker --> PM2
```

## GitHub Actions Workflows

| Repository      | Workflow File                     | Triggers                                                          | Jobs                                                                 |
| --------------- | --------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------- |
| equa-web        | `.github/workflows/ci.yml`        | Push/PR to `main` (PR #516 removed stale `staging` trigger)       | lint-and-build, unit-tests, e2e-smoke, regression, visual-regression |
| equa-web        | `.github/workflows/e2e-tests.yml` | Push/PR to `main`, `master`, `develop`, `feat/**`                 | E2E tests, regression tests                                          |
| equa-server     | `.github/workflows/ci.yml`        | Push to `main`, `staging`, `prod-deploy`; PRs to `main`/`staging` | build, test, docker-build                                            |
| equabot-gateway | (Vitest in CI)                    | Push to `main`                                                    | lint, build, test (with coverage thresholds)                         |

Source: GitHub Actions workflow files in each repository

## equa-web CI

**File:** `.github/workflows/ci.yml`

CI runs a tiered pipeline on every push/PR:

### Lint & Build Job

* **Runner:** ubuntu-latest
* **Node version:** 18
* **Steps:** Install deps -> ESLint -> webpack production build
* **Note (PR #511):** `@babel/core` was upgraded from 7.11.6 → 7.29.0 to fix an `api.assumption is not a function` TypeError that broke every webpack build. The `@babel/plugin-proposal-class-properties` dead dep was removed and `core-js` entries deduped in the same PR. Webpack build failures referencing `api.assumption` should no longer occur.
* **Note (PR #516):** The stale `GH_PATTERNLIB_TOKEN: placeholder` env override was removed from this job; the repo-level secret-validation step handles the token properly. The `staging` branch trigger was also removed because the default branch is now `main`.

### Unit Tests Job

* **Steps:** Jest 29 + React Testing Library (52 tests)

### E2E Smoke Job

* **Timeout:** 30 minutes
* **Steps:** Start webpack dev server -> run Playwright E2E tests
* **On failure:** Uploads test results and screenshots as artifacts

### Regression Tests Job

* **Timeout:** 20 minutes
* **Runs:** Specific regression test files targeting previously fixed issues

### Visual Regression Tests Job (PR #509)

* **Steps:** Capture screenshots of key routes -> compare against baseline images
* **On failure:** Uploads diff images as artifacts for visual review

<Note>
  The legacy Azure Pipelines config (`azure-pipelines.yml`) was removed in PR #507. CI is now fully handled by GitHub Actions. The Azure DevOps GitHub integration should also be disconnected from the `equa-devops` project to stop the failing `EQUAStart.equa-web` check from appearing on PRs.
</Note>

Source: `equa-web/.github/workflows/ci.yml`

## equa-server CI

**File:** `.github/workflows/ci.yml`

Three jobs:

### Build Job

* **Runner:** ubuntu-latest
* **Node version:** 18
* **Steps:** Install deps -> compile TypeScript (`yarn tsc && yarn tsa`)
* **Note:** Tests continue on error (`continue-on-error: true`)

### Test Job

* **Steps:** Install deps -> run tests (`yarn test:api`)
* **Continues on error** so Docker build can proceed even if tests fail

### Docker Build Job

* **Steps:** Build Docker image -> verify compiled files exist
* **Does not push** -- only validates the image builds correctly

Source: `equa-server/.github/workflows/ci.yml`

## Deployment Targets

### Google Cloud Run (legacy equa-server deploy path)

**Config:** `equa-server/cloudbuild.yaml`

Cloud Build steps:

1. Build Docker image
2. Push to Container Registry
3. Deploy to Cloud Run

**Cloud Run configuration:**

| Setting           | Value       |
| ----------------- | ----------- |
| Region            | us-central1 |
| Port              | 3000        |
| Memory            | 1Gi         |
| CPU               | 1           |
| Min instances     | 1           |
| Max instances     | 10          |
| Request timeout   | 300s        |
| Startup CPU boost | Enabled     |

Source: `equa-server/cloudbuild.yaml`

### Railway (current interim runtime targets)

Both `equa-web` and `equa-server` have Railway deployment configs. Treat these as the current committed runtime targets for the interim public stack.

**equa-server (`railway.toml`):**

```toml theme={null}
[build]
builder = "nixpacks"

[deploy]
startCommand = "npm run start:api"
healthcheckPath = "/health"
healthcheckTimeout = 300
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 3
```

**equa-web (`railway.toml`):**

```toml theme={null}
[build]
builder = "nixpacks"

[deploy]
startCommand = "npm run start"
healthcheckPath = "/"
```

Source: `equa-server/railway.toml`, `equa-web/railway.toml`

### Browser API routing (same-origin)

The browser does not call a Cloud Run hostname directly in the default SPA build. `equa-web` defines `API_URL` as `/api/v1`, the development server proxies `/api` to `http://localhost:3000`, and the committed production nginx config proxies `/api/` to `https://equa-server-so-production.up.railway.app/`.

Treat `api.equa.cc` as a hostname that may exist for direct callers or future cutovers, not as the default browser proxy target documented in `nginx.conf`.

Sources:

* `equa-web/webpack.config.js`, Lines: 95-103, 262-266
* `equa-web/config/default.json`, Lines: 1-4
* `equa-web/nginx.conf`, Lines: 21-29

### PM2 (equa-server traditional hosting)

**Config:** `equa-server/ecosystem.config.js`

PM2 manages the production process with two apps:

* `api` -- the main API server
* `raven-addresses` -- the Raven address service

The production Docker image uses PM2 as its runtime (`pm2-runtime start ecosystem.config.js --only api`).

Source: `equa-server/ecosystem.config.js`

## Docker Configuration

**File:** `equa-server/Dockerfile`

Multi-stage build using Node 18 Alpine:

| Stage      | Purpose                                                         |
| ---------- | --------------------------------------------------------------- |
| Build      | Install all deps, compile TypeScript                            |
| Production | Copy compiled output, install production deps only, run via PM2 |

Key details:

* Base image: `node:18-alpine`
* Health check: HTTP GET to port 3000
* Entry point: `pm2-runtime start ecosystem.config.js --only api`

Source: `equa-server/Dockerfile`

## Environment Promotion

The general flow for deploying changes:

```
feature branch → PR → main/staging → production
```

### equa-server

1. Develop on feature branch
2. PR to `main` or `staging` -- GitHub Actions run build + test
3. Keep the Railway runtime story tied to the current service wiring, not to the legacy `prod-deploy` branch
4. Use `prod-deploy` only when intentionally exercising the legacy Cloud Build -> Cloud Run path

### equa-web

1. Develop on feature branch
2. PR to `main` -- CI runs lint, build, unit, smoke, regression, and visual jobs
3. Merge to `main` -- Railway serves the interim `app.equa.cc` runtime

<Warning>
  Secrets management is handled via environment variables in the deployment platform (Railway dashboard, Cloud Run service config, or GitHub Secrets for CI). There is no centralized secrets management tool. Secrets are configured per-environment and are not documented in code.
</Warning>

## Health Checks

| Service                 | Endpoint                | Configured In     |
| ----------------------- | ----------------------- | ----------------- |
| equa-server (Railway)   | `GET /health`           | `railway.toml`    |
| equa-server (Cloud Run) | Port 3000 startup probe | `cloudbuild.yaml` |
| equa-web (Railway)      | `GET /`                 | `railway.toml`    |

## Environments

| Environment | equa-web URL                                   | equa-server URL                                                                                         | Deploy Trigger                                                                                    |
| ----------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Local dev   | [http://localhost:8080](http://localhost:8080) | [http://localhost:3000](http://localhost:3000)                                                          | Manual                                                                                            |
| Staging     | staging.app.equa.cc                            | Same-origin `/api/v1` in the SPA; any direct staging API hostname must be verified separately           | Merge to `staging`                                                                                |
| Production  | app.equa.cc (interim host)                     | Same-origin `/api/v1` via `equa-web` nginx; direct `api.equa.cc` usage is a separate host-level concern | Merge to `main` for `equa-web`; `prod-deploy` remains the legacy Cloud Run path for `equa-server` |
