Skip to content

CI / CD graph

Five GitHub Actions workflows on Blacksmith runners (2-vCPU Ubuntu 24.04). All run on Blacksmith (blacksmith-2vcpu-ubuntu-2404).

flowchart TD
    PR([Pull request opened / updated])
    PUSH([Push to main])
    CRON4H([Cron: every 4 hours])
    CRON30M([Cron: every 30 minutes])
    MANUAL([Manual workflow_dispatch])

    PR --> CI
    PUSH --> CI
    PUSH --> DEPLOY
    PUSH --> PULUMI_APPLY
    PR --> PULUMI_PREVIEW
    CRON4H --> E2E_PROD
    CRON30M --> SMOKE
    MANUAL --> SMOKE

    subgraph CI["ci.yml — Lint · Test · Build"]
        LTB["Job 1: lint-typecheck-test-build
        ──────────────────────────────
        • pnpm install (cached)
        • oxlint
        • oxfmt --check
        • Drizzle journal sync check
        • tsc (Turbo affected)
        • build workspace packages
        • vitest unit tests
        • build affected apps"]

        E2E["Job 2: e2e-sync
        ──────────────────────────────
        • Docker: 3× Postgres + Redis + MinIO
        • Bootstrap Directus (migrations + seed)
        • Bootstrap Vendure (migrations + seed)
        • Start scheduler-api (tsx)
        • Run e2e test suite (vitest)
        • Cleanup Stripe test accounts"]

        LTB -->|must pass| E2E
    end

    subgraph DEPLOY["deploy-production.yml — Build Docker images"]
        DC["detect-changes
        Turbo affected analysis"]

        DV["deploy-vendure-server
        + vendure-worker"]
        DD["deploy-directus"]
        DS["deploy-scheduler-api"]
        DSI["deploy-sites
        site-main · site-shop · site-hypno"]
        DP["deploy-portal"]
        DPB["deploy-portal-business
        + portal-business-web"]
        DDOC["deploy-docs"]

        DC --> DV & DD & DS & DSI & DP & DPB & DDOC
    end

    subgraph PULUMI_PREVIEW["pulumi.yml (PR preview)"]
        PP["pulumi preview --stack stage
        Posts plan as PR comment"]
    end

    subgraph PULUMI_APPLY["pulumi.yml (push apply)"]
        PA["pulumi up --stack stage
        Auto-approves resource updates"]
    end

    subgraph E2E_PROD["e2e-prod.yml — Production health checks"]
        EP["Full-flow tests against Railway
        • Real DB writes + cleanup
        • Tenant provisioning + onboarding
        On failure: open/update GitHub issue
        label: production-alert-e2e"]
    end

    subgraph SMOKE["smoke-prod.yml — Quick smoke"]
        SP["Read-only health checks
        No writes to production data"]
    end

Tests that run in the e2e-sync job (write to local Docker DBs, cleaned up after):

Test file What it covers
business-cascade.e2e.test.ts Tenant provisioning → full convergence sync
convergence-drift-reconcile.e2e.test.ts Mirror sync after Directus/Vendure drift
booking-reminders.e2e.test.ts SMS/email reminders via BullMQ
link-orphans.e2e.test.ts Orphan cleanup + reconciliation
public-payments-smoke.test.ts Public API booking + Stripe flow
workos-oidc-wiring.e2e.test.ts Directus OIDC (skipped unless WORKOS_E2E_ENABLED)

Each deploy-* job:

  1. Checks out code
  2. Runs turbo prune --scope <app> to isolate the sub-tree
  3. Builds a multi-stage Docker image (pruner → installer → builder → runner)
  4. Pushes image to GHCR tagged with ${{ github.sha }}
  5. Triggers Railway redeploy via GitHub → Railway integration
  • scheduler-api tsc requires NODE_OPTIONS=--max-old-space-size=6144 on the 2-vCPU runner or it OOMs.
  • E2E sync builds a hardcoded subset of workspace packages before starting scheduler-api; adding a new @nexus/* runtime dep requires a matching entry in .github/workflows/ci.yml.
  • Pulumi auto-runs pulumi up on merge; never run it manually.
  • e2e-prod cron was trimmed from */10 * * * * to 0 */4 * * * to reduce Railway API load.