# Deployment Runbook

This document explains how deployments work for the EDR platform using Docker, GitHub Actions, and self-hosted runners.

## Overview

- Monorepo contains 6 deployable services:
  - `freight-api`
  - `freight-portal`
  - `freight-backoffice`
  - `passenger-api`
  - `passenger-portal`
  - `passenger-backoffice`
- Deployments run through one workflow: `.github/workflows/deploy.yml`
- Each service is built/deployed independently in parallel (matrix jobs).
- Docker Compose project names are branch-aware to avoid environment collisions on the same host.

## Prerequisites

- Docker Engine with Compose plugin on the self-hosted runner.
- GitHub self-hosted runner registered for this repository.
- Repository secret configured:
  - `NPM_TOKEN` (for private `@tria-plc/*` package install during Docker build)
- Server-side env files created for each branch/environment.

## Server Environment Files

`sync-env-from-server.sh` reads env files from:

`/home/<DEPLOY_USER>/environment/edr/<branch-slug>/<project>/`

Where:

- `<DEPLOY_USER>` defaults to `tria` (overridable by `DEPLOY_USER`)
- `<branch-slug>` is derived from Git branch (lowercase, non-alphanumeric replaced with `-`)
- `<project>` is `edr-freight` or `edr-passenger`

### Required files per project

For `edr-freight`:

- `freight-api.env`
- `freight-portal.env`
- `freight-backoffice.env`
- optional: `freight-web.build.env`

For `edr-passenger`:

- `passenger-api.env`
- `passenger-portal.env`
- `passenger-backoffice.env`
- optional: `passenger-web.build.env`

### Required env key

Each service env file must contain:

- `PORT=<number>`

The sync script validates this and fails if missing.

### Build env files (optional)

Used for additional build-time variables (example: Vite API URL for freight web), with `export` syntax:

```bash
export FREIGHT_VITE_API_URL=https://freight-api.example.com/api
```

These are injected into `GITHUB_ENV` during workflow execution.

> **Passenger web:** `NEXT_PUBLIC_API_URL` does **not** need a separate build env file. Place it directly in the service runtime env file (`passenger-portal.env` / `passenger-backoffice.env`) and the sync script will forward it to the build automatically.

## Docker Compose Port Mapping

`docker-compose.yaml` uses per-service env variables for host/container port mappings:

- `FREIGHT_API_PORT`
- `PASSENGER_API_PORT`
- `FREIGHT_PORTAL_PORT`
- `FREIGHT_BACKOFFICE_PORT`
- `PASSENGER_PORTAL_PORT`
- `PASSENGER_BACKOFFICE_PORT`

`scripts/deploy/sync-env-from-server.sh` extracts `PORT` from each synced `.env` and exports the corresponding `*_PORT` variable to `GITHUB_ENV`.

## Passenger Web Docker Configuration

The passenger web apps (portal and backoffice) are deployed as **Next.js applications** using a dedicated Dockerfile:

- Dockerfile: `infrastructure/docker/Dockerfile.passenger-web`
- Apps: `apps/edr-passenger-web/portal` and `apps/edr-passenger-web/backoffice`

### Key differences from freight-web

| Aspect | Freight Web | Passenger Web |
| --- | --- | --- |
| Framework | Vite (SPA) | Next.js (SSR/SSG) |
| Deployment | Static export + nginx | Node.js server |
| Dockerfile | `Dockerfile.web` | `Dockerfile.passenger-web` |
| Final port (container) | 80 (nginx) | driven by `PORT` in service `.env` |
| Build arg | `TURBO_FILTER` | `APP_PACKAGE` + `APP_PATH` + `NEXT_PUBLIC_API_URL` |

### Build arguments

The Dockerfile accepts the following build args:

- `APP_PACKAGE`: Turbo package filter (e.g., `@edr/passenger-portal`)
- `APP_PATH`: App directory path (e.g., `apps/edr-passenger-web/portal`)
- `NEXT_PUBLIC_API_URL`: API URL visible to browser — sourced from `NEXT_PUBLIC_API_URL` in the service `.env` file

### Port mapping

Both host and container ports are driven by `PORT` in the service env file. The sync script reads `PORT`, exports `PASSENGER_PORTAL_PORT` / `PASSENGER_BACKOFFICE_PORT` to `GITHUB_ENV`, and `docker-compose.yaml` uses those variables for both sides of the mapping:

```
${PASSENGER_PORTAL_PORT:-5174}:${PASSENGER_PORTAL_PORT:-5174}
```

This ensures `docker ps` shows `0.0.0.0:<port>-><port>/tcp` with matching ports.

### Runtime

The final image runs:

```bash
npx next start
```

Next.js reads `PORT` from the runtime environment (supplied via `env_file` in docker-compose) to determine which port to listen on.

## GitHub Actions Deployment Flow

Workflow file: `.github/workflows/deploy.yml`

### 1) `prepare` job

- Checks out repository once.
- Creates workspace artifact (`workspace.tgz`) and uploads it.

### 2) `deploy` matrix job (parallel)

For each service:

- Downloads and extracts workspace artifact.
- Syncs that service env file from server path.
- Computes branch slug and sets:
  - `COMPOSE_PROJECT_NAME=<project>-<branch-slug>`
- Creates `.npmrc`/`.npmrc_temp` from `NPM_TOKEN`.
- Runs:
  - `docker compose --project-name "$COMPOSE_PROJECT_NAME" build <service>`
  - `docker compose --project-name "$COMPOSE_PROJECT_NAME" up -d <service>`
- Cleans `.npmrc`/`.npmrc_temp`.

## Branch/Environment Isolation

Compose project name is generated as:

`<project>-<branch-slug>`

Examples:

- `edr-freight-main`
- `edr-freight-staging`
- `edr-passenger-dev`

This prevents container/network/volume name collisions between branches.

## Local Manual Deployment (Optional)

From repo root:

```bash
DOCKER_BUILDKIT=1 docker compose build <service>
docker compose up -d <service>
```

If private packages are required locally, create `.npmrc`:

```bash
cat <<EOF > .npmrc
@tria-plc:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<YOUR_TOKEN>
always-auth=true
EOF
```

## Passenger API Startup Behavior

Passenger container entrypoint runs on startup:

1. `npm run prisma:generate`
2. `npm run prisma:migrate` (deploy mode)
3. `npm run prisma:seed`
4. starts API process

## Troubleshooting

### Missing env file

Error:

- `Missing env file: ...`

Fix:

- Create the required file in the server env directory for that project/branch slug.

### Missing PORT in env file

Error:

- `Missing required PORT in env file: ...`

Fix:

- Add `PORT=<number>` to that service env file.

### Private package install fails

Check:

- `NPM_TOKEN` exists in repo secrets.
- Workflow created `.npmrc` successfully.

### Prisma seed/migrate failures (passenger)

Check:

- `DATABASE_URL` in `passenger-api.env`
- DB reachability from runner host/container network
- migration history consistency

