diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml
index f57f9ac..f5f7106 100644
--- a/.github/workflows/pr-open.yml
+++ b/.github/workflows/pr-open.yml
@@ -76,11 +76,50 @@ jobs:
with:
target: ${{ github.event.number }}
+ compose:
+ name: Docker Compose
+ runs-on: ubuntu-26.04
+ timeout-minutes: 15
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-node@v7
+ with:
+ node-version: "24.x"
+
+ # docker-compose.yml serves frontend/dist from the host
+ - name: Build frontend
+ working-directory: frontend
+ run: npm ci --ignore-scripts && npm run build
+
+ - name: Start stack
+ run: docker compose up --build --detach --wait --wait-timeout 300
+
+ - name: Smoke test
+ run: |
+ set -euo pipefail
+ curl -fsS --retry 5 --retry-all-errors --retry-delay 3 http://localhost:3005/api/pub-code/health
+ curl -fsS --retry 5 --retry-all-errors --retry-delay 3 http://localhost:3002/ | grep -q '
'
+ curl -fsS --retry 5 --retry-all-errors --retry-delay 3 http://localhost:3002/api/pub-code/health
+ # mongo-express may answer 401 (basic auth); anything below 500 means it's up
+ code=$(curl -sS --retry 5 --retry-all-errors --retry-delay 3 -o /dev/null -w '%{http_code}' http://localhost:8001/)
+ echo "mongo-express: HTTP ${code}"
+ [ "${code}" -lt 500 ]
+
+ - name: Logs
+ if: failure()
+ run: docker compose logs
+
+ - name: Stop stack
+ if: always()
+ run: docker compose down -v
+
# This job is the merge gate for this workflow. Every job in this
# workflow must be listed in 'needs', or its failure won't block the PR.
results:
name: PR Results
- needs: [builds, deploys, tests]
+ needs: [builds, compose, deploys, tests]
if: always()
runs-on: ubuntu-26.04
timeout-minutes: 1
diff --git a/docker-compose.yml b/docker-compose.yml
index 8dcf8e9..6ee92a4 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,6 +11,11 @@ services:
image: ghcr.io/bcgov/nr-containers/mongo:7.0.43
ports: ["27017:27017"]
restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand({ ping: 1 })"]
+ interval: 5s
+ timeout: 5s
+ retries: 20
mongo-express: # The admin UI container for mongoDB
image: mongo-express
@@ -27,7 +32,7 @@ services:
ME_CONFIG_MONGODB_URL: mongodb://admin:DPEPGHbjUluRjetU@database:27017/
depends_on:
database:
- condition: service_started
+ condition: service_healthy
api:
image: pub-code-api:latest
@@ -38,23 +43,43 @@ services:
DB_USER: admin
DB_PWD: DPEPGHbjUluRjetU
DB_NAME: pubcode
+ # The API won't start without CHES settings. These placeholders let it
+ # start; set real values in your shell to send email locally.
+ CHES_TOKEN_URL: ${CHES_TOKEN_URL:-http://localhost/ches/token}
+ CHES_CLIENT_ID: ${CHES_CLIENT_ID:-placeholder}
+ CHES_CLIENT_SECRET: ${CHES_CLIENT_SECRET:-placeholder}
+ CHES_API_URL: ${CHES_API_URL:-http://localhost/ches}
ports:
- "3005:3000"
restart: unless-stopped
+ # The image is distroless (no curl), so check with Node instead
+ healthcheck:
+ test: ["CMD", "/nodejs/bin/node", "-e", "fetch('http://localhost:3000/').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
+ interval: 5s
+ timeout: 5s
+ retries: 20
depends_on:
database:
- condition: service_started
+ condition: service_healthy
#Run npm run build-watch in local before this.
frontend:
image: caddy:2.11.7-alpine
working_dir: /app
+ environment:
+ BACKEND_URL: http://api:3000
+ LOG_LEVEL: info
volumes:
- ./frontend/Caddyfile:/etc/caddy/Caddyfile
- ./frontend/dist:/app/dist
+ healthcheck:
+ test: ["CMD", "wget", "-qO-", "http://localhost:3001/health"]
+ interval: 5s
+ timeout: 5s
+ retries: 20
depends_on:
api:
- condition: service_started
+ condition: service_healthy
ports:
- "3002:3000"
restart: unless-stopped