Skip to content

Commit a5ec22e

Browse files
committed
Merge remote-tracking branch 'bcgov/main' into docs/flyway-test-fixture-strategy
2 parents 26fe705 + 70270ca commit a5ec22e

257 files changed

Lines changed: 27193 additions & 566 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/analysis.yml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,14 @@ jobs:
4040
java-cache: maven
4141
java-distribution: temurin
4242
java-version: "21"
43-
# cpd.exclusions: RoadGroupLookup is a verbatim port of the legacy RMG tables
44-
# (RoadGroupUtil.setRmgByTfaTsbNumberCode/setRmgByTflNumberCode), where ~18 of the
45-
# TSA cases are token-identical apart from their literals. CPD reads that as 174
46-
# duplicated lines; the table is the business rule and must not be de-duplicated.
43+
# cpd.exclusions: both RoadGroup lookups are verbatim ports of the legacy RMG tables,
44+
# where ~18 of the TSA cases are token-identical apart from their literals. CPD reads
45+
# that as ~174 duplicated lines each; the tables ARE the business rule and must not be
46+
# de-duplicated. schedule6 ports setRmgByTfaTsbNumberCode/setRmgByTflNumberCode;
47+
# schedule10 ports setRG10ByTsaTsbNumberCode/setRG10ByTflNumberCode.
48+
# They are NOT interchangeable: the same inputs map to DIFFERENT road groups (TSA 01 ->
49+
# 15 vs 11, TSA 08 -> 10 vs 7, TFL 08 -> 7 vs 10), so "resolving" the duplication by
50+
# merging them would silently corrupt one schedule's derived, user-visible Road Group.
4751
# xmlReportPaths: with the ITs running, the pom's jacoco `merge`/`report-merged` executions
4852
# produce the union of surefire and failsafe coverage. Sonar would otherwise keep reading
4953
# the unit-only report and still count IT-covered lines as uncovered.
@@ -52,7 +56,7 @@ jobs:
5256
-Dsonar.projectKey=bcgov-sonarcloud_nr-ilcr_backend
5357
-Dsonar.host.url=https://sonarcloud.io
5458
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco-merged/jacoco.xml
55-
-Dsonar.cpd.exclusions=**/schedule6/RoadGroupLookup.java
59+
-Dsonar.cpd.exclusions=**/schedule6/RoadGroupLookup.java,**/schedule10/RoadGroup10Lookup.java
5660
sonar_token: ${{ secrets.sonar_token_backend }}
5761
triggers: ('backend/')
5862

@@ -64,7 +68,12 @@ jobs:
6468
checks: write
6569
security-events: write
6670
runs-on: ubuntu-24.04
67-
timeout-minutes: 5
71+
# Covers all four commands below — npm ci, lint, format:check AND a coverage run — not the tests
72+
# alone. Raised from 5 after PR #317 was cancelled at 4m58s with zero test failures: the same job
73+
# had passed at 3m22s one commit earlier, so the 5-minute budget was already two thirds spent and
74+
# a growing component suite left no room for runner variance. 10 restores headroom without
75+
# hiding a genuine hang.
76+
timeout-minutes: 10
6877
steps:
6978
- uses: bcgov/action-test-and-analyse@8f699e3fd3fadd9a6adf6f4b1f2638ef7ecfefb9 # v2.0.0
7079
env:

.github/workflows/merge.yml

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,9 @@ jobs:
2929
# Get PR number for squash merges to main
3030
- id: pr
3131
uses: bcgov/action-get-pr@28b0adf8e4d40720d41f9c87356ce24b0a4bd6af # v0.3.1
32-
# TEST/PROD deploys are held behind ENABLE_OPENSHIFT_DEPLOY until the code
33-
# is ready for those environments; set the repo variable to "true" to open
34-
# the pipeline. PR sandbox deploys (pr-open.yml) are not gated.
32+
# TEST deploys run on every merge and fail visibly if misconfigured.
3533
deploy-test:
3634
name: TEST Deploys (${{ needs.init.outputs.pr }})
37-
if: ${{ vars.ENABLE_OPENSHIFT_DEPLOY == 'true' }}
3835
needs: [init]
3936
uses: ./.github/workflows/reusable-deploy.yml
4037
secrets: inherit
@@ -51,60 +48,63 @@ jobs:
5148
with:
5249
target: test
5350

54-
deploy-prod:
55-
name: PROD Deploys (${{ needs.init.outputs.pr }})
56-
if: ${{ vars.ENABLE_OPENSHIFT_DEPLOY == 'true' }}
57-
needs: [tests, init]
58-
uses: ./.github/workflows/reusable-deploy.yml
59-
secrets: inherit
60-
with:
61-
environment: prod
62-
target: prod
63-
tag: ${{ needs.init.outputs.pr }}
64-
65-
# Sync Sysdig email alerts for PROD. No-ops cleanly if SYSDIG_API_TOKEN is
66-
# unset or monitoring/alerts/ is empty — adoption is gradual. Alert
67-
# templates live in monitoring/alerts/ in this repo; add or remove files
68-
# there to customize the alert set.
69-
monitor-prod:
70-
name: Sysdig Monitor (PROD)
71-
if: ${{ vars.ENABLE_OPENSHIFT_DEPLOY == 'true' }}
72-
needs: [deploy-prod]
73-
runs-on: ubuntu-24.04
74-
environment: prod
75-
permissions:
76-
contents: read
77-
timeout-minutes: 5
78-
steps:
79-
- uses: actions/checkout@v7
80-
- uses: bcgov/actions/sysdig-monitor@4ad61a784f1c17765b03d8d6de9737c1d3f4c0f2 # v0.5.0
81-
with:
82-
sysdig_api_token: ${{ secrets.SYSDIG_API_TOKEN }}
83-
oc_namespace: ${{ secrets.oc_namespace }}
84-
app: ${{ github.event.repository.name }}
85-
86-
promote:
87-
name: Promote Images
88-
if: ${{ vars.ENABLE_OPENSHIFT_DEPLOY == 'true' }}
89-
needs: [deploy-prod, init]
90-
runs-on: ubuntu-slim
91-
permissions:
92-
packages: write
93-
strategy:
94-
matrix:
95-
package: [backend, frontend]
96-
timeout-minutes: 1
97-
steps:
98-
- uses: shrink/actions-docker-registry-tag@e6aaef25c595b6e0edd18bf4c7dbfea3abd43299 # v5
99-
with:
100-
registry: ghcr.io
101-
repository: ${{ github.repository }}/${{ matrix.package }}
102-
target: ${{ needs.init.outputs.pr }}
103-
tags: prod
51+
# ── PROD pipeline: commented out until the prod environment is ready ──
52+
# It still lacks prod-scoped ORACLEDB_* secrets and would silently fall
53+
# back to the repo-level TEST database values. Once those are set, restore
54+
# the jobs below and add deploy-prod + promote back to the results job's
55+
# `needs` list.
56+
#
57+
# deploy-prod:
58+
# name: PROD Deploys (${{ needs.init.outputs.pr }})
59+
# needs: [tests, init]
60+
# uses: ./.github/workflows/reusable-deploy.yml
61+
# secrets: inherit
62+
# with:
63+
# environment: prod
64+
# target: prod
65+
# tag: ${{ needs.init.outputs.pr }}
66+
#
67+
# # Sync Sysdig email alerts for PROD. No-ops cleanly if SYSDIG_API_TOKEN is
68+
# # unset or monitoring/alerts/ is empty — adoption is gradual. Alert
69+
# # templates live in monitoring/alerts/ in this repo; add or remove files
70+
# # there to customize the alert set.
71+
# monitor-prod:
72+
# name: Sysdig Monitor (PROD)
73+
# needs: [deploy-prod]
74+
# runs-on: ubuntu-24.04
75+
# environment: prod
76+
# permissions:
77+
# contents: read
78+
# timeout-minutes: 5
79+
# steps:
80+
# - uses: actions/checkout@v7
81+
# - uses: bcgov/actions/sysdig-monitor@4ad61a784f1c17765b03d8d6de9737c1d3f4c0f2 # v0.5.0
82+
# with:
83+
# sysdig_api_token: ${{ secrets.SYSDIG_API_TOKEN }}
84+
# oc_namespace: ${{ secrets.oc_namespace }}
85+
# app: ${{ github.event.repository.name }}
86+
#
87+
# promote:
88+
# name: Promote Images
89+
# needs: [deploy-prod, init]
90+
# runs-on: ubuntu-slim
91+
# permissions:
92+
# packages: write
93+
# strategy:
94+
# matrix:
95+
# package: [backend, frontend]
96+
# timeout-minutes: 1
97+
# steps:
98+
# - uses: shrink/actions-docker-registry-tag@e6aaef25c595b6e0edd18bf4c7dbfea3abd43299 # v5
99+
# with:
100+
# registry: ghcr.io
101+
# repository: ${{ github.repository }}/${{ matrix.package }}
102+
# target: ${{ needs.init.outputs.pr }}
103+
# tags: prod
104104

105105
results:
106106
name: Merge Results
107-
needs: [init, deploy-test, tests, deploy-prod, promote]
107+
needs: [init, deploy-test, tests]
108108
if: always()
109109
runs-on: ubuntu-slim
110110
timeout-minutes: 1

.github/workflows/reusable-deploy.yml

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ jobs:
4545
env:
4646
DB_USER: ${{ secrets.ORACLEDB_USERNAME }}
4747
DB_PASS: ${{ secrets.ORACLEDB_PASSWORD }}
48+
DB_HOST: ${{ secrets.ORACLEDB_HOST }}
49+
DB_SERVICENAME: ${{ secrets.ORACLEDB_SERVICENAME }}
4850
COGNITO_POOL: ${{ secrets.USER_POOLS_ID }}
51+
COGNITO_CLIENT: ${{ secrets.USER_POOLS_WEB_CLIENT_ID }}
4952
steps:
5053
- uses: bcgov/action-deployer-openshift@27a85b7b157bfc9c3c9bf0aca53bcd288d4d2506 # v4.2.1
5154
id: deploy
@@ -55,12 +58,25 @@ jobs:
5558
oc_server: ${{ vars.OC_SERVER }}
5659
oc_token: ${{ secrets.OC_TOKEN }}
5760
overwrite: true
61+
# Cognito redirect fallbacks must match FAM's registration EXACTLY
62+
# (Cognito string-matches; see FAM repo oidc_clients_ilcr.tf):
63+
# sign-in has NO trailing slash, and dev PR hosts (nr-ilcr-0..49)
64+
# register only the plain <app>/logout. test/prod also register the
65+
# chain-prefixed logout — set env-scoped COGNITO_REDIRECT_SIGN_OUT
66+
# there for full federated (SiteMinder/Keycloak) logout.
5867
parameters:
5968
-p ZONE='${{ inputs.target }}'
6069
-p NAME='${{ github.event.repository.name }}'
6170
-p SPRING_DATASOURCE_USERNAME="$DB_USER"
6271
-p SPRING_DATASOURCE_PASSWORD="$DB_PASS"
72+
-p ORACLEDB_HOST="$DB_HOST"
73+
-p ORACLEDB_SERVICENAME="$DB_SERVICENAME"
6374
-p COGNITO_USER_POOL="$COGNITO_POOL"
75+
-p COGNITO_CLIENT_ID="$COGNITO_CLIENT"
76+
-p COGNITO_DOMAIN='${{ vars.COGNITO_DOMAIN || 'lza-prod-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com' }}'
77+
-p COGNITO_OAUTH_SCOPES='${{ vars.COGNITO_OAUTH_SCOPES || 'openid' }}'
78+
-p COGNITO_REDIRECT_SIGN_IN='${{ vars.COGNITO_REDIRECT_SIGN_IN || format('https://{0}-{1}.apps.gold.devops.gov.bc.ca', github.event.repository.name, inputs.target) }}'
79+
-p COGNITO_REDIRECT_SIGN_OUT='${{ vars.COGNITO_REDIRECT_SIGN_OUT || format('https://{0}-{1}.apps.gold.devops.gov.bc.ca/logout', github.event.repository.name, inputs.target) }}'
6480
triggers: ${{ inputs.triggers }}
6581

6682
deploy:
@@ -75,20 +91,18 @@ jobs:
7591
include:
7692
- name: backend
7793
file: backend/openshift.deploy.yml
78-
# Security and datasource flags come from GitHub vars, resolved
79-
# environment-first, and FAIL CLOSED: auth is enforced unless a
80-
# var explicitly opts a scope out. The backend refuses to start
81-
# with security off and the datasource on (DeployedSecurityGuard),
82-
# so a mock-auth deployment requires disabling both — mock auth
83-
# never fronts real data.
94+
# Oracle host/servicename are environment-scoped SECRETS, passed
95+
# through the init job above (env-scoped secrets don't resolve in
96+
# this matrix — GitHub evaluates it before the environment
97+
# attaches) into the backend Secret. ILCR_SECURITY_ENABLED is
98+
# hardcoded "true" in the backend template — deployed pods always
99+
# enforce auth.
84100
parameters: -p IMAGE_TAG="${{ inputs.tag }}"
85-
-p ORACLEDB_HOST="${{ vars.ORACLEDB_HOST }}"
86-
-p ORACLEDB_SERVICENAME="${{ vars.ORACLEDB_SERVICENAME }}"
87-
-p ILCR_SECURITY_ENABLED="${{ vars.ILCR_SECURITY_ENABLED || 'true' }}"
88101
-p ILCR_DATASOURCE_ENABLED="${{ vars.ILCR_DATASOURCE_ENABLED || 'true' }}"
89102
- name: frontend
90103
file: frontend/openshift.deploy.yml
91104
parameters: -p IMAGE_TAG="${{ inputs.tag }}"
105+
-p COGNITO_DOMAIN="${{ vars.COGNITO_DOMAIN || 'lza-prod-fam-user-pool-domain.auth.ca-central-1.amazoncognito.com' }}"
92106
outputs:
93107
triggered: ${{ steps.deploy.outputs.triggered }}
94108
steps:

.github/workflows/reusable-tests.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ jobs:
6868
# raw-Playwright frontend/e2e). Only the DATA-INDEPENDENT `@smoke` project runs here: it aborts /api,
6969
# so it needs neither the seeded delivery Oracle nor the pinned fixtures the deployed env lacks. The
7070
# full data-backed suite (setup + chromium — SCH1/SEC) is a LOCAL/manual gate; see frontend/e2e/README.md.
71+
#
72+
# The smoke runs against a LOCAL Vite server on localhost, NOT the deployed PR URL. Since Story 1.2
73+
# turned auth on, mock auth is (by design) disabled off-localhost — `isMockAuth()` double-gates on
74+
# `isLocalHost()` (src/env.ts) — so an anonymous visit to a deployed host correctly bounces to the
75+
# FAM/Cognito Hosted UI and never renders the shell. On localhost with the repo-default
76+
# `{ mockUser: true }` config the shell renders client-side, keeping this smoke auth- AND
77+
# backend-independent (the scenarios still abort every /api call).
7178
defaults:
7279
run:
7380
working-directory: frontend/e2e
@@ -82,17 +89,28 @@ jobs:
8289
node-version: 24
8390
cache: "npm"
8491
cache-dependency-path: frontend/e2e/package-lock.json
85-
- name: Install dependencies
92+
- name: Install e2e dependencies
8693
run: |
8794
npm ci
8895
npx playwright install --with-deps chromium
8996
90-
- name: Run smoke (data-independent)
97+
- name: Install frontend
98+
working-directory: frontend
99+
run: npm ci
100+
101+
- name: Run smoke (data-independent, localhost mock auth)
102+
working-directory: frontend
91103
env:
92-
E2E_BASE_URL: https://${{ env.PREFIX }}.${{ env.DOMAIN }}/
93104
CI: "true"
94105
run: |
95-
npm test -- --project=smoke --reporter=html
106+
# Serve the app on localhost so mock auth engages (isMockAuth() requires localhost); the
107+
# @smoke scenarios abort /api, so no backend is needed. No E2E_BASE_URL → the Playwright
108+
# config defaults baseURL to http://localhost:3000.
109+
npm run dev -- --port 3000 &
110+
DEV_PID=$!
111+
npx --yes wait-on -t 120000 http://localhost:3000
112+
( cd e2e && npm test -- --project=smoke --reporter=html )
113+
kill "$DEV_PID" 2>/dev/null || true
96114
97115
- uses: actions/upload-artifact@v7
98116
if: (! cancelled())

README.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,70 @@ mvn spring-boot:run
8686

8787
Do not commit real database passwords. Put local values in `.env`; the file is git-ignored.
8888

89+
### Corporate SSL/TLS Intercept & Certificate Issues (e.g., Zscaler / PKIX) — Local Workaround
90+
91+
*Note: This is a specific workaround for developers behind a corporate SSL-decryption/packet-inspection gateway (such as Zscaler) and is **not** required for all developers (e.g., if you are on a direct internet connection).*
92+
93+
If your corporate network performs SSL decryption/packet-inspection, Maven inside the isolated Docker container may fail to connect to Maven Central (all dependencies, including JasperReports 7, resolve from Central — the build declares no custom `<repositories>`) with a `PKIX path building failed` error.
94+
95+
The recommended, zero-import workaround is to leverage your Windows host's trusted certificate store by caching the dependencies on Windows once, and mounting your host's `.m2` repository into the container:
96+
97+
1. **Seed the cache on Windows**:
98+
Run this once inside your Windows terminal to download and cache the libraries (which automatically trusts your corporate certificate):
99+
```powershell
100+
cd backend
101+
mvn clean install -DskipTests
102+
```
103+
2. **Mount the cache in your local environment**:
104+
Set the `M2_HOME` variable inside your local, ignored `.env` file pointing to your host's `.m2` directory:
105+
```properties
106+
M2_HOME=/mnt/c/Users/<your-username>/.m2
107+
```
108+
Docker Compose will automatically detect this variable and mount your local Windows Maven cache into the container's `/root/.m2` path, bypassing the certificate handshake issues completely!
109+
110+
### Authentication (FAM/Cognito) — local testing
111+
112+
The SPA has two auth modes, selected at runtime by `public/amplify-config.js` (loaded before the
113+
bundle). The repo default is **mock**; deployed environments mount a per-env ConfigMap over it. See
114+
`src/context/auth/` (the `AuthProvider` seam) and `src/config/auth/amplify-initializer.ts`.
115+
116+
**Mock mode (default — no Cognito).** `npm run dev` with the backend running (security off by
117+
default) signs you in automatically. Use the **"Mock user"** dropdown in the header to switch
118+
`ILCR_ADMIN``ILCR_SUBMITTER` — it switches both the nav/route-guards **and** the backend mock
119+
principal (via the `X-Mock-Groups` header), so it exercises role gating end to end. This is the
120+
fastest path for manual testing.
121+
122+
**Real FAM/Cognito login (Hosted UI).**
123+
124+
1. Frontend — copy the example config over the default (do **not** commit it; the repo default must
125+
stay `mockUser: true`):
126+
```bash
127+
cd frontend
128+
cp amplify-config.local.example.js public/amplify-config.js
129+
npm run dev # then hard-refresh the browser (public/ files load at page load)
130+
```
131+
2. Backend — run with security on so `/api/v1/me` validates the real ID token:
132+
```bash
133+
cd backend
134+
ILCR_SECURITY_ENABLED=true COGNITO_REGION=ca-central-1 \
135+
COGNITO_USER_POOL=ca-central-1_UpeAqsYt4 COGNITO_CLIENT_ID=352pis0ark86dam7ht1jlp9uj5 \
136+
SPRING_PROFILES_ACTIVE=oracle,openshift ./mvnw spring-boot:run
137+
```
138+
3. Open `http://localhost:3000` → FAM Hosted UI → sign in (IDIR/BCeID) → back to the app with your
139+
real role. Confirm the exact `cognitoDomain` with the FAM admin if the Hosted UI does not load.
140+
141+
**Dev-only testing aids (real session, local dev only — `import.meta.env.DEV`, tree-shaken from every
142+
deployed build):**
143+
144+
- **"View as (dev)"** header dropdown — overrides the role the SPA uses (nav + route guards) so you
145+
can test both roles without re-logging-in. It is **frontend-only**: the backend still enforces your
146+
real token, so admin APIs still `403` if your account isn't really in that group.
147+
- A **"viewing as" warning banner** appears whenever an override is active, naming your real role.
148+
- A **Sign out** button (header, Logout icon) runs the Cognito/loginproxy logout chain on a real
149+
session; hidden in mock mode.
150+
151+
When done with real login: `git checkout -- frontend/public/amplify-config.js`.
152+
89153
## Frontend Shared Conventions
90154

91155
Reusable building blocks and global styles that new schedule/feature pages should adopt rather than
@@ -126,7 +190,7 @@ FAM authentication is tracked separately. The dashboard currently displays the s
126190

127191
## OpenShift Status
128192

129-
OpenShift Gold is the destination environment, but the Gold project is not required for this local-dev scaffold. Pull requests always deploy a sandbox environment (zone = PR number mod 50). Merges to `main` deploy to TEST and then, if tests pass, to PROD in the same workflow run — but only while the `ENABLE_OPENSHIFT_DEPLOY` repository variable is `true`; it is left unset until the code is ready for those environments.
193+
OpenShift Gold is the destination environment, but the Gold project is not required for this local-dev scaffold. Pull requests always deploy a sandbox environment (zone = PR number mod 50). Merges to `main` deploy to TEST on every merge. The PROD pipeline (deploy, Sysdig monitor, image promotion) is commented out in `.github/workflows/merge.yml` until the `prod` GitHub environment has its own `ORACLEDB_*` secrets; restore those jobs to open PROD.
130194

131195
Deployed pods fail closed on authentication: JWT enforcement (`ILCR_SECURITY_ENABLED`) and the Oracle datasource (`ILCR_DATASOURCE_ENABLED`) both default to `true` and can be overridden per scope with GitHub variables (environment-first, then repository). The backend refuses to start a deployed pod with security off while the datasource is on (`DeployedSecurityGuard`), so mock auth can never serve real data from a public route; setting both variables to `false` yields a data-less mock-auth smoke deployment.
132196

0 commit comments

Comments
 (0)