Skip to content
Open
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
27 changes: 24 additions & 3 deletions .github/workflows/build-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,15 @@ jobs:
if echo "$PLATFORMS" | grep -q 'amd64'; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [
{"component":"backend","dockerfile_dir":"./backend","platform":"linux/amd64","platform_pair":"linux-amd64","runner":"ubuntu-latest"},
{"component":"frontend","dockerfile_dir":"./frontend","platform":"linux/amd64","platform_pair":"linux-amd64","runner":"ubuntu-latest"}
{"component":"frontend","dockerfile_dir":"./frontend","platform":"linux/amd64","platform_pair":"linux-amd64","runner":"ubuntu-latest"},
{"component":"temporal-worker","dockerfile_dir":"./backend","dockerfile":"Dockerfile.temporal-worker","platform":"linux/amd64","platform_pair":"linux-amd64","runner":"ubuntu-latest"}
]')
fi
if echo "$PLATFORMS" | grep -q 'arm64'; then
MATRIX=$(echo "$MATRIX" | jq -c '.include += [
{"component":"backend","dockerfile_dir":"./backend","platform":"linux/arm64","platform_pair":"linux-arm64","runner":"ubuntu-24.04-arm"},
{"component":"frontend","dockerfile_dir":"./frontend","platform":"linux/arm64","platform_pair":"linux-arm64","runner":"ubuntu-24.04-arm"}
{"component":"frontend","dockerfile_dir":"./frontend","platform":"linux/arm64","platform_pair":"linux-arm64","runner":"ubuntu-24.04-arm"},
{"component":"temporal-worker","dockerfile_dir":"./backend","dockerfile":"Dockerfile.temporal-worker","platform":"linux/arm64","platform_pair":"linux-arm64","runner":"ubuntu-24.04-arm"}
]')
fi
# Use delimiter so JSON is not mangled by GITHUB_OUTPUT parsing
Expand Down Expand Up @@ -194,7 +196,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile_dir }}/Dockerfile
file: ${{ matrix.dockerfile_dir }}/${{ matrix.dockerfile || 'Dockerfile' }}
platforms: ${{ matrix.platform }}
labels: |
org.opencontainers.image.revision=${{ github.sha }}
Expand Down Expand Up @@ -288,3 +290,22 @@ jobs:
--annotation "index:org.opencontainers.image.description=SOBA Next.js frontend for web form builder" \
-t ${IMAGE}:latest ${DIGESTS}
fi

- name: Merge temporal-worker
run: |
SHORT_SHA=$(git rev-parse --short HEAD)
IMAGE="ghcr.io/${{ github.repository }}/temporal-worker"
IMAGE_VERSION="${{ needs.prepare.outputs.image_version }}"
DIGESTS=""
for f in $(find digests -type f -name 'temporal-worker-*' 2>/dev/null); do
DIGESTS="${DIGESTS} ${IMAGE}@$(cat $f)"
done
[ -n "$DIGESTS" ] || { echo "No temporal-worker digests"; exit 1; }
docker buildx imagetools create \
--annotation "index:org.opencontainers.image.description=SOBA Temporal worker for workflow execution" \
-t ${IMAGE}:sha-${SHORT_SHA} -t ${IMAGE}:${IMAGE_VERSION} ${DIGESTS}
if [ "$IMAGE_VERSION" = "main" ]; then
docker buildx imagetools create \
--annotation "index:org.opencontainers.image.description=SOBA Temporal worker for workflow execution" \
-t ${IMAGE}:latest ${DIGESTS}
fi
11 changes: 11 additions & 0 deletions .github/workflows/pr_close.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ jobs:
insecure_skip_tls_verify: true
namespace: ${{ secrets.OC_NAMESPACE }}

- name: Delete Temporal PR namespace
env:
NAMESPACE: ${{ secrets.OC_NAMESPACE }}
PR_NUM: ${{ needs.set-vars.outputs.PR_NUM }}
run: |
TEMPORAL_NS="soba-pr-${PR_NUM}"
if oc get deployment temporal-admintools -n "${NAMESPACE}" &>/dev/null; then
oc exec deployment/temporal-admintools -n "${NAMESPACE}" -- \
temporal operator namespace delete -n "${TEMPORAL_NS}" --yes 2>/dev/null || true
fi

- name: Uninstall SOBA and delete orphaned resources
env:
RELEASE: ${{ needs.set-vars.outputs.RELEASE }}
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/pr_open.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,36 @@ jobs:
insecure_skip_tls_verify: true
namespace: ${{ secrets.OC_NAMESPACE }}

- name: Deploy Shared Temporal (idempotent)
env:
NAMESPACE: ${{ secrets.OC_NAMESPACE }}
TEMPORAL_CHART_VERSION: ${{ vars.TEMPORAL_CHART_VERSION || '0.74.0' }}
run: |
helm upgrade --install temporal temporal \
--repo https://go.temporal.io/helm-charts \
--version "${TEMPORAL_CHART_VERSION}" \
--namespace "${NAMESPACE}" \
--timeout 900s \
-f deployments/helm/temporal/values.yaml

echo "Waiting for schema jobs..."
oc wait --for=condition=complete job -l app.kubernetes.io/instance=temporal \
-n "${NAMESPACE}" --timeout=600s || true

echo "Waiting for Temporal frontend..."
oc rollout status deployment/temporal-frontend \
-n "${NAMESPACE}" --timeout=300s

- name: Ensure Temporal PR namespace exists
env:
NAMESPACE: ${{ secrets.OC_NAMESPACE }}
PR_NUM: ${{ needs.set-vars.outputs.PR_NUM }}
run: |
TEMPORAL_NS="soba-pr-${PR_NUM}"
oc exec deployment/temporal-admintools -n "${NAMESPACE}" -- \
sh -c "temporal operator namespace describe -n ${TEMPORAL_NS} 2>/dev/null || \
temporal operator namespace create -n ${TEMPORAL_NS} --retention 3d"

- name: Get DB config from Crunchy secret
id: db
env:
Expand All @@ -133,6 +163,7 @@ jobs:
TAG: sha-${{ needs.build.outputs.short_sha }}
DOMAIN: ${{ vars.OC_DOMAIN }}
DATABASE_URI: ${{ steps.db.outputs.uri }}
PR_NUM: ${{ needs.set-vars.outputs.PR_NUM }}
run: |
REPO_LC=$(echo "$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]')
# Escape single quotes for YAML: ' -> ''
Expand All @@ -147,6 +178,10 @@ jobs:
image:
repository: "ghcr.io/${REPO_LC}/frontend"
tag: "${TAG}"
temporalWorker:
image:
repository: "ghcr.io/${REPO_LC}/temporal-worker"
tag: "${TAG}"
global:
domain: "${DOMAIN}"
database:
Expand All @@ -155,6 +190,9 @@ jobs:
admin:
password: "${{ secrets.FORMIO_ADMIN_PASSWORD }}"
jwtSecret: "${{ secrets.FORMIO_JWT_SECRET }}"
temporal:
address: "temporal-frontend.${NAMESPACE}.svc.cluster.local:7233"
namespace: "soba-pr-${PR_NUM}"
OVERRIDE

helm upgrade --install "${RELEASE}" ./deployments/helm/soba \
Expand Down Expand Up @@ -186,6 +224,9 @@ jobs:
echo "Waiting for mongodb rollout..."
oc rollout status statefulset/${RELEASE}-mongodb -n "${NAMESPACE}" --timeout=300s

echo "Waiting for temporal-worker rollout..."
oc rollout status deployment/${RELEASE}-temporal-worker -n "${NAMESPACE}" --timeout=300s

- name: Release Comment on PR
uses: marocchino/sticky-pull-request-comment@v2
if: success()
Expand Down
25 changes: 25 additions & 0 deletions backend/Dockerfile.temporal-worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM node:lts-slim

# enable pnpm via corepack and ensure workspace installs work
RUN corepack enable pnpm

WORKDIR /app

# copy root workspace config + backend package file
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY backend/package.json backend/

# install only backend deps (hoisted into root node_modules)
RUN pnpm install --filter ./backend...

# copy entire repo and build backend
COPY . .
RUN pnpm --filter ./backend run build

# OpenShift / arbitrary UID: run non-root; g=u lets assigned user (often gid 0) read /app
RUN chgrp -R 0 /app && chmod -R g=u /app
ENV HOME=/tmp
ENV XDG_CACHE_HOME=/tmp/.cache
USER 1001

CMD ["node", "backend/dist/temporal-worker.js"]
5 changes: 5 additions & 0 deletions deployments/helm/action-crunchy/values.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ crunchy:
databases:
- '{{ .Values.global.config.dbName }}'
- 'postgres'
- name: 'temporal'
databases:
- 'temporal'
- 'temporal_visibility'
options: "SUPERUSER CREATEDB CREATEROLE"
11 changes: 11 additions & 0 deletions deployments/helm/soba/templates/temporal/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "soba.fullname" . }}-temporal
labels:
{{- include "soba.labels" (dict "root" . "component" "temporal") | nindent 4 }}
data:
TEMPORAL_ALLOWED: {{ .Values.temporal.allowed | default "false" | quote }}
TEMPORAL_ADDRESS: {{ .Values.temporal.address | default "" | quote }}
TEMPORAL_NAMESPACE: {{ .Values.temporal.namespace | default "default" | quote }}
TEMPORAL_TASK_QUEUE: {{ .Values.temporal.taskQueue | default "soba" | quote }}
50 changes: 50 additions & 0 deletions deployments/helm/soba/templates/temporal/deployment-worker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{{- $tw := .Values.temporalWorker | default dict }}
{{- $twEnabled := false }}
{{- if hasKey $tw "enabled" }}{{ $twEnabled = index $tw "enabled" }}{{ end }}
{{- if $twEnabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "soba.fullname" . }}-temporal-worker
labels:
{{- include "soba.labels" (dict "root" . "component" "temporal-worker") | nindent 4 }}
spec:
replicas: {{ .Values.temporalWorker.replicas }}
selector:
matchLabels:
{{- include "soba.selectorLabels" (dict "root" . "component" "temporal-worker") | nindent 6 }}
template:
metadata:
annotations:
checksum/config-temporal: {{ include (print $.Template.BasePath "/temporal/configmap.yaml") . | sha256sum }}
checksum/config-app: {{ include (print $.Template.BasePath "/backend/configmap-app.yaml") . | sha256sum }}
checksum/config-formio: {{ include (print $.Template.BasePath "/backend/configmap-formio.yaml") . | sha256sum }}
checksum/config-sso: {{ include (print $.Template.BasePath "/backend/configmap-sso.yaml") . | sha256sum }}
checksum/config-ratelimit: {{ include (print $.Template.BasePath "/backend/configmap-ratelimit.yaml") . | sha256sum }}
checksum/secret-db: {{ include (print $.Template.BasePath "/secrets/db-secret.yaml") . | sha256sum }}
checksum/secret-formio: {{ include (print $.Template.BasePath "/secrets/formio-secret.yaml") . | sha256sum }}
labels:
{{- include "soba.selectorLabels" (dict "root" . "component" "temporal-worker") | nindent 8 }}
spec:
containers:
- name: temporal-worker
image: "{{ .Values.temporalWorker.image.repository }}:{{ .Values.temporalWorker.image.tag }}"
imagePullPolicy: {{ .Values.temporalWorker.image.pullPolicy }}
envFrom:
- configMapRef:
name: {{ include "soba.fullname" . }}-temporal
- configMapRef:
name: {{ include "soba.fullname" . }}-backend-app
- configMapRef:
name: {{ include "soba.fullname" . }}-backend-formio
- configMapRef:
name: {{ include "soba.fullname" . }}-backend-sso
- configMapRef:
name: {{ include "soba.fullname" . }}-backend-ratelimit
- secretRef:
name: {{ include "soba.fullname" . }}-db
- secretRef:
name: {{ include "soba.fullname" . }}-formio
resources:
{{- toYaml .Values.temporalWorker.resources | nindent 12 }}
{{- end }}
14 changes: 14 additions & 0 deletions deployments/helm/soba/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,17 @@ outboxWorker:
limits:
cpu: 100m
memory: 256Mi

temporal:
allowed: "true"
address: ""
namespace: "soba-dev"
taskQueue: "soba"

temporalWorker:
enabled: true
image:
repository: ghcr.io/bcgov/soba/temporal-worker
tag: latest
pullPolicy: IfNotPresent
replicas: 1
21 changes: 21 additions & 0 deletions deployments/helm/soba/values-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,24 @@ outboxWorker:
limits:
cpu: 100m
memory: 256Mi

temporal:
allowed: "true"
address: ""
namespace: "default"
taskQueue: "soba"

temporalWorker:
enabled: true
image:
repository: ghcr.io/bcgov/soba/temporal-worker
tag: latest
pullPolicy: IfNotPresent
replicas: 1
resources:
requests:
cpu: 25m
memory: 128Mi
limits:
cpu: 100m
memory: 512Mi
23 changes: 23 additions & 0 deletions deployments/helm/soba/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,26 @@ mongodb:
persistence:
size: 1Gi
storageClassName: ""

# -- Temporal (shared server, env vars for backend + worker) ------------------
temporal:
allowed: "false"
address: ""
namespace: "default"
taskQueue: "soba"

# -- Temporal worker (dedicated image with glibc for @temporalio/core-bridge) --
temporalWorker:
enabled: false
image:
repository: ghcr.io/bcgov/soba/temporal-worker
tag: latest
pullPolicy: IfNotPresent
replicas: 1
resources:
requests:
cpu: 25m
memory: 128Mi
limits:
cpu: 100m
memory: 512Mi
85 changes: 85 additions & 0 deletions deployments/helm/temporal/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
server:
image:
repository: temporalio/server
tag: 1.30.3
configMapsToMount: "sprig"
setConfigFilePath: true
securityContext:
fsGroup: null
runAsUser: null
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
config:
persistence:
defaultStore: default
visibilityStore: visibility
default:
driver: "sql"
sql:
driver: "postgres12"
host: pg-soba-crunchy-primary
port: 5432
database: temporal
user: temporal
existingSecret: pg-soba-crunchy-pguser-temporal
maxConns: 20
maxIdleConns: 20
maxConnLifetime: "1h"
visibility:
driver: "sql"
sql:
driver: "postgres12"
host: pg-soba-crunchy-primary
port: 5432
database: temporal_visibility
user: temporal
existingSecret: pg-soba-crunchy-pguser-temporal
maxConns: 20
maxIdleConns: 20
maxConnLifetime: "1h"

cassandra:
enabled: false
mysql:
enabled: false
elasticsearch:
enabled: false
prometheus:
enabled: false
grafana:
enabled: false

schema:
createDatabase:
enabled: false
setup:
enabled: true
backoffLimit: 100
update:
enabled: true
backoffLimit: 100

web:
enabled: true
resources:
requests:
cpu: 15m
memory: 48Mi
limits:
cpu: 100m
memory: 128Mi

admintools:
enabled: true
resources:
requests:
cpu: 10m
memory: 48Mi
limits:
cpu: 50m
memory: 128Mi
Loading