Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .cursorignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.env
.env.*
.env.local
.env.*.local
!.env.example
!.env.local.example
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ RUN set -e; \
*) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
esac; \
curl -fsSL "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable/openshift-client-${OC_SUFFIX}.tar.gz" \
| tar xz -C /usr/local/bin oc
| tar xz -C /usr/local/bin oc
160 changes: 160 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# SOBA Dev Container

This directory contains the configuration for the SOBA development container and sidecar services.

## Build Process

### 1. Initialize (before container starts)

Runs on the host before the container is built or started:

- **Environment files**: Copies example files to active env files only if targets don't exist (`cp -n`):
- `backend/.env.example` → `backend/.env`, `backend/.env.local.example` → `backend/.env.local`
- `frontend/.env.example` → `frontend/.env`
- Existing files (e.g. `backend/.env.local` with secrets) are never overwritten.

### 2. Container build

The `Dockerfile` builds a Node.js 24 image with:

- **Package managers**: npm, pnpm (via corepack)
- **Database clients**: PostgreSQL 17 client, `mongosh` (MongoDB shell)
- **CLI tools**: `jq`, `curl`, `k6` (load testing), `oc` (OpenShift CLI)
- **Features**: Docker-in-Docker, GitHub CLI, kubectl, Helm

### 3. Container start

- **Environment**: `runArgs` loads `backend/.env` and `backend/.env.local` into the container environment.
- **Forwarded ports**: 3000 (Frontend), 3001 (Form.io), 4000 (Backend)

### 4. Post-create (first run only)

Runs once when the devcontainer is first created:

- Installs backend dependencies (`npm ci` in `backend/`)
- Installs frontend dependencies (`pnpm install` in `frontend/`)
- Creates `backend/.env`, `backend/.env.local`, and `frontend/.env` from examples if missing (safety net)

### 5. Post-start (every container start)

- Refreshes `backend/.env` and `frontend/.env` from their `.env.example` files on each start.
- `backend/.env.local` is never touched and keeps your secrets.

---

## What's Available After Build

| Tool | Location | Purpose |
| ----------------- | ---------------------- | -------------------------- |
| Node.js | 24.x | Runtime |
| npm | 10.x | Backend package manager |
| pnpm | 10.x | Frontend package manager |
| PostgreSQL client | `psql` | Connect to Postgres |
| MongoDB shell | `mongosh` | Connect to MongoDB |
| k6 | `/usr/local/bin/k6` | Load testing |
| oc | `/usr/local/bin/oc` | OpenShift CLI |
| Docker | (via Docker-in-Docker) | Run sidecars, build images |

**VS Code extensions**: ESLint, Prettier, Docker, PostgreSQL, Kubernetes Tools

---

## Starting Services

### Sidecar services (databases, Form.io)

Start MongoDB, PostgreSQL, and Form.io from inside the devcontainer:

```bash
docker compose -f .devcontainer/docker-compose.yml up -d
```

Stop services:

```bash
docker compose -f .devcontainer/docker-compose.yml down
```

### Connection strings (from inside devcontainer)

| Service | Connection |
| ---------- | ------------------------------------------------------------------- |
| MongoDB | `mongodb://host.docker.internal:27017` |
| PostgreSQL | `postgresql://postgres:postgres@host.docker.internal:5432/postgres` |
| Form.io | `http://host.docker.internal:3001` |

**Form.io login**: `formio@localhost.com` / `formio`

---

## Starting the App

### Option 1: VS Code launch (recommended)

- **SOBA Backend**: `http://localhost:4000`
- **SOBA Frontend**: `http://localhost:3000`
- **SOBA (Backend + Frontend)**: Starts both

### Option 2: Terminal

```bash
# Backend
npm run dev --prefix backend

# Frontend (in another terminal)
cd frontend && pnpm dev
```

### Endpoints

| App | URL |
| ----------- | --------------------- |
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:4000 |
| Form.io | http://localhost:3001 |

---

## Environment Files

### Backend

The backend uses `.env` and `.env.local`. Values are loaded in order: `.env` first, then `.env.local` (which overrides matching keys). The backend applies them at runtime via `dotenv`; the devcontainer also injects them into the container environment via `runArgs`.

| File | Purpose | Committed |
| ---------------------------- | ---------------------------------------------------------------- | --------- |
| `backend/.env.example` | Base config (Form.io URL, JWT issuer/audience, role mapping) | Yes |
| `backend/.env.local.example` | Template for credentials (Form.io admin/manager, session secret) | Yes |
| `backend/.env` | Active base config | No |
| `backend/.env.local` | Active credentials and secrets | No |

### Frontend

The frontend uses `.env`. Next.js loads it at build/runtime. Values in `.env.example` are for the localhost environment (local Form.io, BC Gov dev Keycloak).

| File | Purpose | Committed |
| ----------------------- | ------------------------------------- | --------- |
| `frontend/.env.example` | Template for localhost / local dev | Yes |
| `frontend/.env` | Active config (Form.io URL, Keycloak) | No |

### How they are created

| When | Action |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **Initialize** (before container starts) | `cp -n` for backend and frontend env files — copies only if the target does not exist. Existing files are never overwritten. |
| **Post-create** (first run only) | Same as above, as a safety net. |
| **Post-start** (every start) | `cp .env.example .env` — refreshes `backend/.env` and `frontend/.env` from their examples. `backend/.env.local` is never touched. |

### How they are applied

| Context | Mechanism |
| -------------------- | ----------------------------------------------------------------------------------------- |
| **Devcontainer** | `runArgs` passes `--env-file backend/.env` and `--env-file backend/.env.local` to Docker. |
| **Backend process** | `dotenv.config('.env')` then `dotenv.config('.env.local', override: true)`. |
| **Frontend process** | Next.js loads `frontend/.env` automatically at build and runtime. |

### Summary

- `.env` is never committed; it is created from `.env.example` when the devcontainer starts.
- Backend: put credentials in `.env.local`; it is never overwritten.
- Frontend: `.env.example` values target localhost; edit `.env` for other environments.
28 changes: 23 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,38 @@
"dockerfile": "Dockerfile",
"context": "."
},
"runArgs": [
"--env-file",
"${localWorkspaceFolder}/backend/.env",
"--env-file",
"${localWorkspaceFolder}/backend/.env.local"
],
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {
"minikube": "none"
}
},
"forwardPorts": [3000, 4000],
"forwardPorts": [3000, 3001, 4000],
"portsAttributes": {
"3000": { "label": "Frontend (Next.js)" },
"4000": { "label": "Backend (Express)" }
"3000": {
"label": "Frontend (Next.js)"
},
"3001": {
"label": "Form.io"
},
"4000": {
"label": "Backend (Express)"
}
},
"postCreateCommand": "bash .devcontainer/post-create.sh",
"postStartCommand": "bash .devcontainer/post-start.sh",
"customizations": {
"vscode": {
"settings": {
"remote.autoForwardPorts": false
},
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
Expand All @@ -27,5 +44,6 @@
"ms-kubernetes-tools.vscode-kubernetes-tools"
]
}
}
},
"initializeCommand": "cp -n ${localWorkspaceFolder}/backend/.env.example ${localWorkspaceFolder}/backend/.env 2>/dev/null || true; cp -n ${localWorkspaceFolder}/backend/.env.local.example ${localWorkspaceFolder}/backend/.env.local 2>/dev/null || true; cp -n ${localWorkspaceFolder}/frontend/.env.example ${localWorkspaceFolder}/frontend/.env 2>/dev/null || true"
}
21 changes: 21 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
# Connection strings (from inside devcontainer):
# MongoDB: mongodb://host.docker.internal:27017
# PostgreSQL: postgresql://postgres:postgres@host.docker.internal:5432/postgres
# Form.io: http://host.docker.internal:3001
#
# Local Form.io credentials (for backend .env):
# FORMIO_BASE_URL=http://host.docker.internal:3001
# FORMIO_ADMIN_USERNAME=formio@localhost.com
# FORMIO_ADMIN_PASSWORD=formio
# FORMIO_MANAGER_USERNAME=formio@localhost.com
# FORMIO_MANAGER_PASSWORD=formio

name: soba-dev-services
services:
mongodb:
image: mongo:7
Expand All @@ -14,6 +23,18 @@ services:
- mongodb_data:/data/db
restart: unless-stopped

formio:
image: formio/formio:latest
ports:
- "3001:3001"
environment:
ROOT_EMAIL: "formio@localhost.com"
ROOT_PASSWORD: "formio"
NODE_CONFIG: '{"mongo":"mongodb://mongodb:27017/formio-ce","port":3001,"jwt":{"secret":"formio-dev-secret-change-in-production"}}'
depends_on:
- mongodb
restart: unless-stopped

postgres:
image: postgres:17-alpine
environment:
Expand Down
24 changes: 23 additions & 1 deletion .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ echo " SOBA — Dev Container Setup"
echo "══════════════════════════════════════════════════════════════"
echo ""

# ── Install root dependencies ─────────────────────────────────────────────
echo "==> Installing root dependencies..."
npm ci

# ── Install backend dependencies ────────────────────────────────────────────
echo "==> Installing backend dependencies..."
cd backend
Expand All @@ -27,6 +31,24 @@ else
echo "==> backend/.env already exists or no .env.example, skipping..."
fi

# ── Set up local overrides file (create once, never overwrite) ──────────────
if [ ! -f backend/.env.local ]; then
echo "==> Creating backend/.env.local from .env.local.example..."
cp backend/.env.local.example backend/.env.local
echo " Add credentials and overrides to backend/.env.local (never committed)"
else
echo "==> backend/.env.local already exists, skipping..."
fi

# ── Set up frontend environment file ───────────────────────────────────────
if [ -f frontend/.env.example ] && [ ! -f frontend/.env ]; then
echo "==> Creating frontend/.env from .env.example..."
cp frontend/.env.example frontend/.env
echo " Values are for localhost; edit frontend/.env for other environments"
else
echo "==> frontend/.env already exists or no .env.example, skipping..."
fi

# ── Print summary ───────────────────────────────────────────────────────────
echo ""
echo "══════════════════════════════════════════════════════════════"
Expand Down Expand Up @@ -57,4 +79,4 @@ echo " kubectl $(kubectl version --client --short 2>/dev/null || kubectl vers
echo " oc $(oc version --client 2>/dev/null | head -1 || echo 'n/a')"
echo " k6 $(k6 version 2>/dev/null || echo 'n/a')"
echo " gh $(gh --version 2>/dev/null | head -1 || echo 'n/a')"
echo ""
echo ""
23 changes: 23 additions & 0 deletions .devcontainer/post-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
set -e

echo "══════════════════════════════════════════════════════════════"
echo " SOBA — Dev Container Post-Start"
echo "══════════════════════════════════════════════════════════════"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/.."

# Refresh backend/.env from .env.example on each devcontainer start
if [ -f backend/.env.example ]; then
cp backend/.env.example backend/.env
echo " Refreshed backend/.env from .env.example"
fi

# Refresh frontend/.env from .env.example on each devcontainer start
if [ -f frontend/.env.example ]; then
cp frontend/.env.example frontend/.env.local
echo " Refreshed frontend/.env from .env.example"
fi

echo "══════════════════════════════════════════════════════════════"
31 changes: 31 additions & 0 deletions .github/actions/build-component/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Build Component
description: Builds a specific component (frontend, backend, or monorepo at root)

inputs:
component:
description: Component to build (root, frontend, or backend)
required: true
node_version:
description: Node.js version to use
required: false
default: "24"

runs:
using: composite
steps:
- name: Setup Node Workspace
uses: ./.github/actions/setup-node-workspace
with:
node_version: ${{ inputs.node_version }}
component: ${{ inputs.component }}

- name: Build component
if: inputs.component != 'root'
working-directory: ${{ inputs.component }}
shell: bash
run: npm run build

- name: Build component (root)
if: inputs.component == 'root'
shell: bash
run: npm run build
Loading
Loading