Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 13 additions & 9 deletions .github/workflows/bake-and-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ permissions:
# Replaces build-docker-image.yml for apps migrated to docker-bake.hcl.
#
# Callers pass app-specific build args via the `set` input using GitHub Variables
# (vars.*) for NEXT_PUBLIC_* values — these are public by design and belong in
# vars, not secrets. Sentry secrets are declared explicitly below.
# (vars.*) — public config that is safe to expose in workflow logs. Secrets that
# are genuinely needed at build time (e.g. Sentry source map upload credentials,
# DB access if the framework initialises at build) are declared explicitly below.
# Everything else (APP_URL, SMTP_PASS, PAYLOAD_CORS, etc.) belongs in runtime
# config (e.g. Dokku config:set) and should never be passed here.

on:
workflow_call:
Expand Down Expand Up @@ -44,17 +47,19 @@ on:
description: >
Extra bake --set overrides (newline-separated target.field=value pairs).
Use this to inject app-specific build args, e.g.:
techlabblog.args.NEXT_PUBLIC_APP_URL=${{ vars.TECHLABBLOG_APP_URL }}
techlabblog.args.SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }}
Note: GitHub Variables (vars.*) are available here; secrets are not —
pass truly secret build args via the declared secrets inputs below.
# Mark required: false so apps without optional secrets can use this workflow.
secrets:
DATABASE_URL:
required: false
DOCKER_HUB_USERNAME:
required: true
DOCKER_HUB_ACCESS_TOKEN:
required: true
# Sentry secrets: sourced from env by BuildKit secret mounts in Dockerfiles
# (--mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN).
# Mark required: false so apps without Sentry can use this workflow too.
PAYLOAD_SECRET:
required: false
SENTRY_AUTH_TOKEN:
required: false
SENTRY_ORG:
Expand Down Expand Up @@ -93,9 +98,8 @@ jobs:
BASE_TAG: ${{ inputs.base_tag }}
GIT_REVISION: ${{ github.sha }}
BUILD_DATE: ${{ steps.meta.outputs.date }}
# Sentry secrets: exposed to BuildKit as secret mounts (not build args).
# See --mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN
# in app Dockerfiles.
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 }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/push-to-dokku.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ on:
required: true
type: string
description: "The name of the image to push"
secrets:
SSH_PRIVATE_KEY:
required: true

jobs:
push:
runs-on: ubuntu-latest
permissions: {}

steps:
- name: Push
Expand Down
18 changes: 8 additions & 10 deletions .github/workflows/techlabblog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- main
paths:
- "apps/techlabblog/**"
- "docker/apps/techlabblog.Dockerfile"
- "docker/apps/techlabblog/**"
- "docker/base.Dockerfile"
- "docker-bake.hcl"
- ".github/workflows/techlabblog.yml"
Expand Down Expand Up @@ -61,13 +61,8 @@ jobs:
# codeforafrica/techlabblog:<version> — version bump only (immutable, for releases)
# codeforafrica/techlabblog:latest — version bump only (mutable, for convenience)
#
# NEXT_PUBLIC_* vars are baked into the JS bundle at build time and cannot
# be changed by restarting the container. Configure them as GitHub Variables
# (Settings > Variables > Actions) rather than secrets since they are public
# by definition (they ship to the browser).
#
# Required GitHub Variables:
# TECHLABBLOG_SENTRY_DSN — public Sentry DSN (safe to use vars, not secrets)
# TECHLABBLOG_SENTRY_DSN
#
# Required GitHub Secrets (for Sentry source map upload during build):
# SENTRY_AUTH_TOKEN, SENTRY_ORG, TECHLABBLOG_SENTRY_PROJECT
Expand All @@ -85,7 +80,8 @@ jobs:
tag: ${{ github.sha }}
# base_tag: v3
set: |
techlabblog.args.NEXT_PUBLIC_SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }}
techlabblog.args.SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }}
techlabblog.args.SENTRY_ENVIRONMENT=production
${{ needs.version-check.outputs.changed == 'true' && format('techlabblog.tags[]=codeforafrica/techlabblog:{0}', needs.version-check.outputs.version) || '' }}
${{ needs.version-check.outputs.changed == 'true' && 'techlabblog.tags[]=codeforafrica/techlabblog:latest' || '' }}
secrets:
Expand All @@ -106,7 +102,8 @@ jobs:
with:
git_remote_url: "ssh://azureuser@ui-1.dev.codeforafrica.org/techlabblog-ui"
deploy_docker_image: "codeforafrica/techlabblog:${{ github.sha }}"
secrets: inherit
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

# Deploys to production when the package.json version is bumped.
# Both version-check and build must pass before this job runs.
Expand All @@ -118,4 +115,5 @@ jobs:
with:
git_remote_url: "ssh://dokku@ui-2.prod.codeforafrica.org/techlabblog-ui"
deploy_docker_image: "codeforafrica/techlabblog:${{ needs.version-check.outputs.version }}"
secrets: inherit
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
87 changes: 87 additions & 0 deletions .github/workflows/trustlab.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: TrustLab

on:
push:
branches:
- main
paths:
Comment thread
kilemensi marked this conversation as resolved.
Comment thread
kilemensi marked this conversation as resolved.
- "apps/trustlab/**"
- "docker/apps/trustlab/**"
- "docker/base.Dockerfile"
- "docker-bake.hcl"
- ".github/workflows/trustlab.yml"
- ".github/workflows/bake-and-push.yml"
Comment thread
kilemensi marked this conversation as resolved.

permissions:
contents: read

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

jobs:
version-check:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
changed: ${{ steps.check.outputs.changed }}
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v4
with:
node-version: lts/*

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

# Build and push image on every push.
build:
needs: version-check
permissions:
contents: read
uses: ./.github/workflows/bake-and-push.yml
with:
target: trustlab
tag: ${{ github.sha }}
set: |
${{ needs.version-check.outputs.changed == 'true' && format('trustlab.tags[]=codeforafrica/trustlab:{0}', needs.version-check.outputs.version) || '' }}
${{ needs.version-check.outputs.changed == 'true' && 'trustlab.tags[]=codeforafrica/trustlab:latest' || '' }}
secrets:
DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
DATABASE_URL: ${{ secrets.TRUSTLAB_MONGO_URL }}
PAYLOAD_SECRET: ${{ secrets.TRUSTLAB_PAYLOAD_SECRET }}
# We need these sentry vars at build time to send source maps.
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.TRUSTLAB_SENTRY_PROJECT }}

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

deploy-prod:
needs: [version-check, build]
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/trustlab-ui"
deploy_docker_image: "codeforafrica/trustlab:${{ needs.version-check.outputs.version }}"
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ techlabblog:
./scripts/bake-up.sh techlabblog

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

twoopstracker:
./scripts/dc.sh twoopstracker
Expand Down
23 changes: 15 additions & 8 deletions apps/trustlab/.env
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
# Args and Envs
MIGRATIONS_DIR=./migrations

SENTRY_ENVIRONMENT=local
APP_NAME=TrustLab
APP_URL=http://localhost:3000
IMAGE_UNOPTIMIZED="true"
SEO_DISABLED="true"

NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_IMAGE_UNOPTIMIZED="true"
NEXT_PUBLIC_SEO_DISABLED="true"
PROJECT_ROOT=../..

NEXT_PUBLIC_DEFAULT_LOCALE=en
NEXT_PUBLIC_LOCALES="en"
DEFAULT_LOCALE=en
LOCALES="en"

NEXT_TELEMETRY_DISABLED=1

# Secrets
## Optional - App will work fine without these but must be
# declared for docker image to build
SENTRY_AUTH_TOKEN=
SENTRY_ORG=
SENTRY_PROJECT=
SMTP_PASS=
14 changes: 9 additions & 5 deletions apps/trustlab/.env.template
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Next
NEXT_PUBLIC_APP_URL=
APP_NAME=
APP_URL=
SEO_DISABLED=
IMAGE_UNOPTIMIZED=
DEFAULT_LOCALE=
LOCALES=

# Payload
MONGO_URL=
DATABASE_URL=
PAYLOAD_SECRET=
PREVIEW_SECRET=

Expand All @@ -16,7 +21,8 @@ S3_MAX_SOCKETS=1000
S3_CONNECTION_TIMEOUT=5000

# Sentry
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_DSN=
SENTRY_AUTH_TOKEN=
SENTRY_ENVIRONMENT=local|development|staging|production
SENTRY_ORG=
SENTRY_PROJECT=
Expand All @@ -27,5 +33,3 @@ SMTP_HOST=
SMTP_PASS=
SMTP_FROM_ADDRESS=
SMTP_FROM_NAME=


1 change: 0 additions & 1 deletion apps/trustlab/eslint.webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ module.exports = {
resolve: {
alias: {
"@/trustlab": path.resolve(__dirname, "src/"),
content: path.resolve(__dirname, "content/"),
},
extensions: [".js"],
},
Expand Down
4 changes: 2 additions & 2 deletions apps/trustlab/instrumentation-client.js
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 "@/trustlab/utils";

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

// Add optional integrations for additional features
integrations: [Sentry.replayIntegration()],
Expand Down
2 changes: 1 addition & 1 deletion apps/trustlab/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ global.Element.prototype.scrollIntoView = jest.fn();
// NOTE: Since we use Jest for component testing i.e. unit testing, it's not
// recommended to load external env vars (since outcome will not be
// predictable)
process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
process.env.APP_URL = "http://localhost:3000";

jest.mock("next/router", () => ({
useRouter: jest.fn().mockImplementation(() => ({
Expand Down
12 changes: 6 additions & 6 deletions apps/trustlab/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import path from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PROJECT_ROOT = process.env.PROJECT_ROOT?.trim();
const outputFileTracingRoot = PROJECT_ROOT
? path.resolve(__dirname, PROJECT_ROOT)

const TRACING_ROOT = process.env.TRACING_ROOT?.trim();
const outputFileTracingRoot = TRACING_ROOT
? path.join(__dirname, TRACING_ROOT)
: undefined;

/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: process.env.NEXT_PUBLIC_IMAGE_DOMAINS?.split(",")
domains: process.env.IMAGE_DOMAINS?.split(",")
.map((d) => d.trim())
.filter(Boolean),
unoptimized:
process.env.NEXT_PUBLIC_IMAGE_UNOPTIMIZED?.trim()?.toLowerCase() ===
"true",
process.env.IMAGE_UNOPTIMIZED?.trim()?.toLowerCase() === "true",
},
output: "standalone",
outputFileTracingRoot,
Expand Down
23 changes: 13 additions & 10 deletions apps/trustlab/payload.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import {
} from "@/trustlab/payload/collections";
import plugins from "@/trustlab/payload/plugins";
import SiteSettings from "@/trustlab/payload/globals";
import { site } from "@/trustlab/utils";
import { defaultLocale, locales } from "@/trustlab/payload/utils/locales";
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const cors =
process?.env?.PAYLOAD_CORS?.split(",")
process.env.PAYLOAD_CORS?.split(",")
.map((d) => d.trim())
.filter(Boolean) ?? [];

const csrf =
process?.env?.PAYLOAD_CSRF?.split(",")
process.env.PAYLOAD_CSRF?.split(",")
.map((d) => d.trim())
.filter(Boolean) ?? [];

Expand All @@ -40,7 +42,7 @@ if (process.env.SMTP_HOST && process.env.SMTP_PASS) {
nodemailerAdapterArgs = {
defaultFromAddress:
process.env.SMTP_FROM_ADDRESS || "noreply@trustlab.africa",
defaultFromName: process.env.SENDGRID_FROM_NAME || "TrustLab CMS",
defaultFromName: process.env.SENDGRID_FROM_NAME || site.name,
// Any Nodemailer transport can be used
transportOptions: {
host: process.env.SMTP_HOST,
Expand All @@ -58,10 +60,10 @@ const email = nodemailerAdapter(nodemailerAdapterArgs);
export default buildConfig({
admin: {
importMap: {
baseDir: path.resolve(dirname),
baseDir: path.resolve(__dirname),
},
livePreview: {
url: process.env.NEXT_PUBLIC_APP_URL,
url: site.url,
collections: ["pages"],
globals: ["site-settings"],
},
Expand All @@ -84,7 +86,7 @@ export default buildConfig({
cors,
csrf,
db: mongooseAdapter({
url: process.env.MONGO_URL || "",
url: process.env.DATABASE_URL ?? false,
}),
email,
editor: lexicalEditor(),
Expand All @@ -100,10 +102,11 @@ export default buildConfig({
: undefined),
plugins: [...plugins],
secret: process.env.PAYLOAD_SECRET || "",
serverURL: process.env.NEXT_PUBLIC_APP_URL,
// Just the origin, without trailing slash.
serverURL: site.url.slice(0, -1),
sharp,
telemetry: process.env.NEXT_TELEMETRY_DISABLED === "0",
typescript: {
outputFile: path.resolve(dirname, "payload-types.ts"),
outputFile: path.resolve(__dirname, "payload-types.ts"),
},
});
Loading
Loading