Skip to content
Merged
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
70 changes: 70 additions & 0 deletions .github/workflows/_build-roboshield.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Reusable Build | RoboShield

on:
workflow_call:
inputs:
tag:
required: true
type: string
description: "Image tag to build or push"
push:
required: true
type: boolean
description: "Whether to push the built image to the registry"
base_tag:
required: true
type: string
description: "Published base image tag to build from"
app_url:
required: true
type: string
description: "NEXT_PUBLIC_APP_URL baked into the client bundle (public, not a secret)"
sentry_environment:
required: true
type: string
description: "Sentry environment baked into the app"
set:
required: false
type: string
default: ""
description: "Additional bake --set overrides"
secrets:
DOCKER_HUB_USERNAME:
required: false
DOCKER_HUB_ACCESS_TOKEN:
required: false
DATABASE_URL:
required: false
PAYLOAD_SECRET:
required: false
SENTRY_AUTH_TOKEN:
required: false
SENTRY_ORG:
required: false
SENTRY_PROJECT:
required: false

jobs:
build:
permissions:
actions: read
contents: read
uses: ./.github/workflows/_bake-and-push.yml
with:
target: roboshield
target_os: '["ubuntu-24.04-arm"]'
base_tag: ${{ inputs.base_tag }}
tag: ${{ inputs.tag }}
push: ${{ inputs.push }}
set: |
roboshield.args.NEXT_PUBLIC_APP_URL=${{ inputs.app_url }}
roboshield.args.SENTRY_ENVIRONMENT=${{ inputs.sentry_environment }}
${{ inputs.set }}
secrets:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
PAYLOAD_SECRET: ${{ secrets.PAYLOAD_SECRET }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
29 changes: 29 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ on:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "apps/pesayetu/**"
- "apps/roboshield/**"
- "apps/techlabblog/**"
- "apps/trustlab/**"
- "docker/apps/pesayetu/**"
- "docker/apps/roboshield/**"
- "docker/apps/techlabblog/**"
- "docker/apps/trustlab/**"
- "docker-bake.hcl"
Expand All @@ -21,10 +23,12 @@ on:
- "scripts/pr-build-targets.test.mjs"
- ".github/workflows/_bake-and-push.yml"
- ".github/workflows/_build-pesayetu.yml"
- ".github/workflows/_build-roboshield.yml"
- ".github/workflows/_build-techlabblog.yml"
- ".github/workflows/_build-trustlab.yml"
- ".github/workflows/pesayetu.yml"
- ".github/workflows/pr-build.yml"
- ".github/workflows/roboshield.yml"
- ".github/workflows/techlabblog.yml"
- ".github/workflows/trustlab.yml"

Expand Down Expand Up @@ -120,6 +124,31 @@ jobs:
WORDPRESS_APPLICATION_PASSWORD: pr-build-unused
WORDPRESS_PREVIEW_SECRET: pr-build-unused

build-roboshield:
name: Build roboshield image
needs: affected
if: ${{ contains(fromJSON(needs.affected.outputs.targets), 'roboshield') }}
permissions:
actions: read
contents: read
uses: ./.github/workflows/_build-roboshield.yml
with:
base_tag: ${{ vars.UI_BASE_TAG }}
tag: pr-${{ github.event.pull_request.number }}-${{ github.sha }}
push: false
app_url: "http://localhost:3000"
sentry_environment: ci
secrets:
# Payload's mongooseAdapter is not queried during static generation, so
# PR builds don't need a reachable database — a well-formed but unused
# connection string is enough to satisfy the build. The remaining
# secrets are not required to compile either.
DATABASE_URL: "mongodb://pr-build-unused/pr-build-unused"
PAYLOAD_SECRET: pr-build-unused
SENTRY_AUTH_TOKEN: pr-build-unused
SENTRY_ORG: pr-build-unused
SENTRY_PROJECT: pr-build-unused

build-techlabblog:
name: Build techlabblog image
needs: affected
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/roboshield-deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
platforms: linux/arm64
push: true
secrets: |
"mongo_url=${{ secrets.ROBOSHIELD_MONGO_URL }}"
"database_url=${{ secrets.ROBOSHIELD_MONGO_URL }}"
"payload_secret=${{ secrets.ROBOSHIELD_PAYLOAD_SECRET }}"
"sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}"
"sentry_org=${{ secrets.SENTRY_ORG }}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/roboshield-deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
uses: docker/build-push-action@v6
with:
build-args: |
MONGO_URL=${{ secrets.ROBOSHIELD_MONGO_URL }}
DATABASE_URL=${{ secrets.ROBOSHIELD_MONGO_URL }}
NEXT_PUBLIC_APP_URL=${{ env.NEXT_PUBLIC_APP_URL }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.ROBOSHIELD_SENTRY_DSN }}
SENTRY_ENVIRONMENT=${{ env.SENTRY_ENVIRONMENT }}
Expand All @@ -83,7 +83,7 @@ jobs:
platforms: linux/arm64
push: true
secrets: |
"mongo_url=${{ secrets.ROBOSHIELD_MONGO_URL }}"
"database_url=${{ secrets.ROBOSHIELD_MONGO_URL }}"
"payload_secret=${{ secrets.ROBOSHIELD_PAYLOAD_SECRET }}"
"sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}"
"sentry_org=${{ secrets.SENTRY_ORG }}"
Expand Down
127 changes: 127 additions & 0 deletions .github/workflows/roboshield.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: RoboShield

on:
push:
branches:
- main
paths:
- "apps/roboshield/**"
- "docker/apps/roboshield/**"
- "docker-bake.hcl"
- ".github/workflows/_bake-and-push.yml"
- ".github/workflows/_build-roboshield.yml"
- ".github/workflows/roboshield.yml"

permissions:
actions: read
contents: read

concurrency:
group: "${{ github.workflow }} @ ${{ github.ref }}"
cancel-in-progress: true

jobs:
# Checks whether apps/roboshield/package.json has a version bump.
# The prod deploy is gated on this: every push triggers a dev build+deploy,
# only a version bump triggers a prod build+deploy.
version-check:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04]
permissions:
contents: read
outputs:
changed: ${{ steps.check.outputs.changed }}
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: actions/setup-node@v6
with:
node-version-file: "package.json"

- name: Check if version is bumped
id: check
uses: EndBug/version-check@v3
with:
diff-search: true
file-name: apps/roboshield/package.json

# Builds the DEV image and pushes it to DockerHub on every push. roboshield
# bakes its canonical URL into the client bundle (NEXT_PUBLIC_APP_URL), and
# DEV/PROD use different Dokku apps with different URLs, so — unlike
# techlabblog/trustlab which build once and promote — DEV and PROD each get
# their own build, matching the pre-bake two-workflow setup this replaces.
#
# Required GitHub Secrets:
# ROBOSHIELD_MONGO_URL, ROBOSHIELD_PAYLOAD_SECRET,
# ROBOSHIELD_SENTRY_PROJECT, SENTRY_AUTH_TOKEN, SENTRY_ORG, UI_BASE_TAG (var)
build-dev:
permissions:
actions: read
contents: read
uses: ./.github/workflows/_build-roboshield.yml
with:
base_tag: ${{ vars.UI_BASE_TAG }}
tag: ${{ github.sha }}
push: true
app_url: "https://roboshield-ui.dev.codeforafrica.org"
sentry_environment: development
secrets:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
DATABASE_URL: ${{ secrets.ROBOSHIELD_MONGO_URL }}
PAYLOAD_SECRET: ${{ secrets.ROBOSHIELD_PAYLOAD_SECRET }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.ROBOSHIELD_SENTRY_PROJECT }}

deploy-dev:
needs: build-dev
permissions: {}
uses: ./.github/workflows/push-to-dokku.yml
with:
git_remote_url: "ssh://azureuser@ui-1.dev.codeforafrica.org/roboshield-ui"
deploy_docker_image: "codeforafrica/roboshield:${{ github.sha }}"
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

# Builds the PROD image only when the package.json version is bumped, using
# the PROD canonical URL and the semver tag.
build-prod:
needs: version-check
if: needs.version-check.outputs.changed == 'true'
permissions:
actions: read
contents: read
uses: ./.github/workflows/_build-roboshield.yml
with:
base_tag: ${{ vars.UI_BASE_TAG }}
tag: ${{ needs.version-check.outputs.version }}
push: true
app_url: "https://roboshield.trustlab.africa"
sentry_environment: production
set: |
roboshield.tags+=codeforafrica/roboshield:latest
secrets:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
DATABASE_URL: ${{ secrets.ROBOSHIELD_MONGO_URL }}
PAYLOAD_SECRET: ${{ secrets.ROBOSHIELD_PAYLOAD_SECRET }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.ROBOSHIELD_SENTRY_PROJECT }}

deploy-prod:
needs: [version-check, build-prod]
if: needs.version-check.outputs.changed == 'true'
permissions: {}
uses: ./.github/workflows/push-to-dokku.yml
with:
git_remote_url: "ssh://dokku@ui-2.prod.codeforafrica.org/roboshield-ui"
deploy_docker_image: "codeforafrica/roboshield:${{ needs.version-check.outputs.version }}"
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ COPY apps/roboshield ./apps/roboshield/

# When building Next.js app, Next.js needs to connect to local Payload
ENV PAYLOAD_PUBLIC_APP_URL=http://localhost:3000
RUN --mount=type=secret,id=mongo_url,env=MONGO_URL \
RUN --mount=type=secret,id=database_url,env=DATABASE_URL \
--mount=type=secret,id=payload_secret,env=PAYLOAD_SECRET \
--mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN \
--mount=type=secret,id=sentry_org,env=SENTRY_ORG \
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pesayetu:
./scripts/bake-up.sh pesayetu

roboshield:
./scripts/dc.sh roboshield
./scripts/bake-up.sh roboshield

techlabblog:
./scripts/bake-up.sh techlabblog
Expand Down
4 changes: 2 additions & 2 deletions apps/roboshield/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ NEXT_PUBLIC_APP_URL
# Defaults to 3000.
PORT
# https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_DSN=
# Defaults to local
SENTRY_ENVIRONMENT=local|development|staging|production
# Defaults to code-for-africa
Expand All @@ -19,7 +19,7 @@ SENTRY_ORG=
SENTRY_PROJECT=

# Payload
MONGO_URL=
DATABASE_URL=
PAYLOAD_SECRET=
PAYLOAD_PUBLIC_APP_URL=

Expand Down
4 changes: 2 additions & 2 deletions apps/roboshield/instrumentation-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

import * as Sentry from "@sentry/nextjs";

const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN;
import site from "@/roboshield/utils/site";

Sentry.init({
dsn: SENTRY_DSN,
dsn: site.sentryDsn,

// Add optional integrations for additional features
integrations: [
Expand Down
2 changes: 1 addition & 1 deletion apps/roboshield/migrations/20250422_064614_migration.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MigrateDownArgs, MigrateUpArgs } from "@payloadcms/db-mongodb";
import mongoose from "mongoose";

const mongoURL = process.env.MONGO_URL || "";
const mongoURL = process.env.DATABASE_URL || "";
const { ObjectId } = mongoose.Types;

export async function up({ payload }: MigrateUpArgs): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions apps/roboshield/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"tsconfig-paths": "catalog:"
},
"devDependencies": {
"@babel/preset-react": "catalog:",
"@svgr/webpack": "catalog:",
"@types/express": "catalog:",
"@types/node": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion apps/roboshield/payload.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default buildConfig({
],
}),
db: mongooseAdapter({
url: process.env.MONGO_URL ?? false,
url: process.env.DATABASE_URL ?? false,
migrationDir: process.env.MIGRATIONS_DIR,
}),
collections: [Media, Pages, Users] as CollectionConfig[],
Expand Down
4 changes: 3 additions & 1 deletion apps/roboshield/sentry.edge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

import * as Sentry from "@sentry/nextjs";

import site from "@/roboshield/utils/site";

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
dsn: site.sentryDsn,

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
Expand Down
4 changes: 3 additions & 1 deletion apps/roboshield/sentry.server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

import * as Sentry from "@sentry/nextjs";

import site from "@/roboshield/utils/site";

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
dsn: site.sentryDsn,

// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
Expand Down
Loading