From 5d487654c6622560d50a1c7323e7284531657238 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Thu, 11 Jun 2026 22:20:26 -0700 Subject: [PATCH 1/4] ci(e2e): add wait for frontend health check before running tests --- .github/workflows/reusable-tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/reusable-tests.yml b/.github/workflows/reusable-tests.yml index 1b4723489..bcccb9a89 100644 --- a/.github/workflows/reusable-tests.yml +++ b/.github/workflows/reusable-tests.yml @@ -86,6 +86,23 @@ jobs: npm ci npx playwright install --with-deps + - name: Wait for frontend health check + env: + BASE_URL: https://${{ env.PREFIX }}.${{ env.DOMAIN }} + run: | + echo "Waiting for frontend health check at ${BASE_URL}..." + for i in {1..30}; do + status=$(curl -k --connect-timeout 5 --max-time 10 -s -o /dev/null -w "%{http_code}" "${BASE_URL}" || true) + if [ "$status" -eq 200 ]; then + echo "Frontend is healthy!" + exit 0 + fi + echo "Waiting for health check (HTTP $status), retrying in 10s... ($i/30)" + sleep 10 + done + echo "ERROR: Frontend did not become healthy in time." + exit 1 + - name: Run Tests env: E2E_BASE_URL: https://${{ env.PREFIX }}.${{ env.DOMAIN }}/ From e839771191bcf7a0eadf2e546634211f9aefe836 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 12 Jun 2026 09:09:44 -0700 Subject: [PATCH 2/4] feat(frontend): block deployment on backend readiness and optimize probes --- .github/workflows/reusable-tests.yml | 17 ----------- frontend/openshift.deploy.yml | 45 ++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/.github/workflows/reusable-tests.yml b/.github/workflows/reusable-tests.yml index bcccb9a89..1b4723489 100644 --- a/.github/workflows/reusable-tests.yml +++ b/.github/workflows/reusable-tests.yml @@ -86,23 +86,6 @@ jobs: npm ci npx playwright install --with-deps - - name: Wait for frontend health check - env: - BASE_URL: https://${{ env.PREFIX }}.${{ env.DOMAIN }} - run: | - echo "Waiting for frontend health check at ${BASE_URL}..." - for i in {1..30}; do - status=$(curl -k --connect-timeout 5 --max-time 10 -s -o /dev/null -w "%{http_code}" "${BASE_URL}" || true) - if [ "$status" -eq 200 ]; then - echo "Frontend is healthy!" - exit 0 - fi - echo "Waiting for health check (HTTP $status), retrying in 10s... ($i/30)" - sleep 10 - done - echo "ERROR: Frontend did not become healthy in time." - exit 1 - - name: Run Tests env: E2E_BASE_URL: https://${{ env.PREFIX }}.${{ env.DOMAIN }}/ diff --git a/frontend/openshift.deploy.yml b/frontend/openshift.deploy.yml index 0392916ab..6d293ee19 100644 --- a/frontend/openshift.deploy.yml +++ b/frontend/openshift.deploy.yml @@ -66,6 +66,37 @@ objects: matchLabels: deployment: ${NAME}-${ZONE}-${COMPONENT} topologyKey: kubernetes.io/hostname + initContainers: + - name: wait-for-backend + image: ${REGISTRY}/${ORG_NAME}/${NAME}/${COMPONENT}:${IMAGE_TAG} + command: + - /bin/sh + - -c + - | + echo "Waiting for backend to be healthy..." + max_retries=30 + attempt=1 + until wget -q --spider http://${NAME}-${ZONE}-backend/api/health; do + if [ "$attempt" -ge "$max_retries" ]; then + echo "ERROR: Backend ${NAME}-${ZONE}-backend is not reachable/healthy after ${max_retries} attempts." >&2 + exit 1 + fi + echo "Backend not ready (attempt ${attempt}/${max_retries}), sleeping..." + attempt=$((attempt + 1)) + sleep 2 + done + echo "Backend is UP and healthy!" + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + seccompProfile: + type: RuntimeDefault + volumeMounts: + - name: caddy-data + mountPath: /tmp/caddy containers: - name: ${NAME} image: ${REGISTRY}/${ORG_NAME}/${NAME}/${COMPONENT}:${IMAGE_TAG} @@ -73,6 +104,8 @@ objects: ports: - name: http containerPort: 3000 + - name: health + containerPort: 3001 env: - name: LOG_LEVEL value: ${LOG_LEVEL} @@ -109,20 +142,20 @@ objects: mountPath: /tmp/coraza startupProbe: httpGet: - path: / - port: http + path: /health + port: health periodSeconds: 5 failureThreshold: 30 readinessProbe: httpGet: - path: / - port: http + path: /health + port: health periodSeconds: 5 failureThreshold: 3 livenessProbe: httpGet: - path: / - port: http + path: /health + port: health periodSeconds: 10 failureThreshold: 3 lifecycle: From bc6d0b25c7af5f747bfe55fce9468a4e6d6dd8af Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 12 Jun 2026 09:14:19 -0700 Subject: [PATCH 3/4] fix(backend): configure PrismaPg driver adapter with custom schema --- backend/src/prisma.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/prisma.service.ts b/backend/src/prisma.service.ts index 93fe61016..87b64d603 100644 --- a/backend/src/prisma.service.ts +++ b/backend/src/prisma.service.ts @@ -29,7 +29,7 @@ class PrismaService extends PrismaClientWithLogs implements OnModuleInit, OnModu return PrismaService.instance } const pool = new Pool({ connectionString: dataSourceURL }) - const adapter = new PrismaPg(pool) + const adapter = new PrismaPg(pool, { schema: DB_SCHEMA }) super({ adapter, errorFormat: 'pretty', From fe94547ede94622236cfb1c556aea05b2d3e4ca4 Mon Sep 17 00:00:00 2001 From: Derek Roberts Date: Fri, 12 Jun 2026 09:23:14 -0700 Subject: [PATCH 4/4] feat(deploy): decouple backend liveness probe and optimize DB checks --- backend/openshift.deploy.yml | 2 +- backend/src/app.controller.spec.ts | 4 ++-- backend/src/app.service.ts | 2 +- backend/test/app.e2e-spec.ts | 2 +- common/openshift.database.yml | 18 ++++++++++-------- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/backend/openshift.deploy.yml b/backend/openshift.deploy.yml index c794bf73d..cf8098b6d 100644 --- a/backend/openshift.deploy.yml +++ b/backend/openshift.deploy.yml @@ -199,7 +199,7 @@ objects: failureThreshold: 3 livenessProbe: httpGet: - path: /api/health + path: /api port: http periodSeconds: 10 failureThreshold: 3 diff --git a/backend/src/app.controller.spec.ts b/backend/src/app.controller.spec.ts index 3dc29ca1d..16e6503ff 100644 --- a/backend/src/app.controller.spec.ts +++ b/backend/src/app.controller.spec.ts @@ -16,8 +16,8 @@ describe('AppController', () => { }) describe('root', () => { - it('should return "Hello Backend!"', () => { - expect(appController.getHello()).toBe('Hello Backend!') + it('should return "Backend is live!"', () => { + expect(appController.getHello()).toBe('Backend is live!') }) }) }) diff --git a/backend/src/app.service.ts b/backend/src/app.service.ts index 578d75564..b2c5a5197 100644 --- a/backend/src/app.service.ts +++ b/backend/src/app.service.ts @@ -3,6 +3,6 @@ import { Injectable } from '@nestjs/common' @Injectable() export class AppService { getHello(): string { - return 'Hello Backend!' + return 'Backend is live!' } } diff --git a/backend/test/app.e2e-spec.ts b/backend/test/app.e2e-spec.ts index bf8e645ec..c8107412a 100644 --- a/backend/test/app.e2e-spec.ts +++ b/backend/test/app.e2e-spec.ts @@ -15,5 +15,5 @@ describe('AppController (e2e)', () => { await app.init() }) - it('/ (GET)', () => request(app.getHttpServer()).get('/').expect(200).expect('Hello Backend!')) + it('/ (GET)', () => request(app.getHttpServer()).get('/').expect(200).expect('Backend is live!')) }) diff --git a/common/openshift.database.yml b/common/openshift.database.yml index d92acf609..ae8270526 100644 --- a/common/openshift.database.yml +++ b/common/openshift.database.yml @@ -94,10 +94,11 @@ objects: readinessProbe: exec: command: - - /usr/bin/env - - bash - - "-c" - - psql -q -U $POSTGRES_USER -d $POSTGRES_DB -c 'SELECT 1' + - pg_isready + - -d + - $(POSTGRES_DB) + - -U + - $(POSTGRES_USER) successThreshold: 1 failureThreshold: 5 initialDelaySeconds: 15 @@ -106,10 +107,11 @@ objects: livenessProbe: exec: command: - - /usr/bin/env - - bash - - "-c" - - psql -q -U $POSTGRES_USER -d $POSTGRES_DB -c 'SELECT 1' + - pg_isready + - -d + - $(POSTGRES_DB) + - -U + - $(POSTGRES_USER) successThreshold: 1 failureThreshold: 5 initialDelaySeconds: 30