Skip to content

CI/CD pipelines — per-module workflow runbook

Every built module in this repo has a dev CI/CD pipeline at .github/workflows/mod-<NNN>.yml. Each calls one of MOD-156's reusable workflows (reusable-lambda.yml or reusable-iac.yml) or, for script-based modules, runs a bespoke job directly.

Why this exists

FR-735 (the generate-workflows.py generator) is bank-wiki's deliverable and not yet in place. Until it lands, per-module workflows are written by hand using the same shape the generator will emit, so the generator is a drop-in replacement later.

Current per-module workflows

Module Type Workflow calls Trigger Notes
MOD-043 IaC reusable-iac.yml push/pr to MOD-043-*/ + shared-iac
MOD-044 Hybrid reusable-lambda.yml push to MOD-044-*/ + shared-iac
MOD-045 IaC reusable-iac.yml push/pr to MOD-045-*/ + shared-iac
MOD-046 Hybrid reusable-lambda.yml push to MOD-046-*/ + shared-iac
MOD-062 Lambda reusable-lambda.yml push to MOD-062-*/ + shared-iac
MOD-076 IaC reusable-iac.yml push/pr to MOD-076-*/ + shared-iac
MOD-097 Hybrid reusable-lambda.yml push to MOD-097-*/ + shared-iac
MOD-103 Script (Neon REST) bespoke workflow_dispatch only Needs BANK_NEON_API_KEY secret
MOD-104 IaC reusable-iac.yml push/pr to MOD-104-*/ + shared-iac Foundation module — high blast radius
MOD-156 Script (Octokit) bespoke push to MOD-156-*/src/ + dispatch Needs MOD156_GH_TOKEN secret

What every workflow does on push to main

Lambda modules (reusable-lambda.yml): 1. Checkout, setup Node 20 + pnpm 2. pnpm install --frozen-lockfile 3. pnpm typecheck 4. pnpm test:unit --coverage with 80 % line gate 5. pnpm test:integration — only if env.RUN_INTEGRATION == '1' 6. OIDC credentials via ${{ vars.CICD_ROLE_ARN }} 7. pnpm run deploy --stage <stage> (SST) 8. update-wiki.py --status Built

IaC modules (reusable-iac.yml): 1. Checkout, setup Node 20 + pnpm, install 2. OIDC credentials 3. On PR: pnpm run diff --stage <stage> (SST drift) 4. On push to main: pnpm run deploy --stage <stage> 5. On push to main: Verify every /bank/ SSM path in docs/design/MOD-<id>.md resolves (reads the repo-root doc) 6. On push to main: update-wiki.py --status Built

Script-based modules (MOD-103, MOD-156) inline the equivalent of the above with the relevant provisioner command.

Prerequisites for pipelines to actually run green

  1. vars.CICD_ROLE_ARN set on the repo — MOD-156's provisioner writes this. Already set on bank-platform, bank-core, bank-kyc.
  2. MOD-104 cicd role trust policy widenedMOD-104-oidc-trust-update.handoff.md requests MOD-104 extend the sub claim to the 8 repos × 3 environments. Until applied, OIDC assume-role fails for any environment: scoped job other than bank-platform's current wildcard match.
  3. Environment dev exists on the repo — MOD-156's provisioner creates these; on GitHub Free (current plan) the envs exist but without protection rules (deferred until plan upgrade).
  4. Module-specific secrets where applicable — documented inline in the per-module workflow headers.

Adding a pipeline for a new module

  1. Pick the right reusable template: reusable-lambda.yml if the module has Lambda code + SST deploy; reusable-iac.yml if pure IaC via SST; bespoke workflow if it uses REST-API provisioning scripts instead of SST.
  2. Copy .github/workflows/mod-043.yml (IaC) or mod-044.yml (Lambda) to .github/workflows/mod-<NNN>.yml.
  3. Replace every occurrence of the old MOD-NNN with the new one (module_id + module_dir).
  4. If the module has a repo-root design doc, the path filter in the on: block already includes docs/design/MOD-NNN.md — make sure that path exists.
  5. Commit. The first push that touches the module dir triggers the pipeline.

What's deliberately not here

  • Branch protection + status-check contexts — MOD-156 defines the rules; they're deferred until the GitHub plan supports them on private repos.
  • PR-time deploy blockingreusable-lambda.yml deploys unconditionally; PR triggers are disabled on Lambda workflows so PR branches never deploy. reusable-iac.yml is PR-safe and runs sst diff only.
  • Cross-module ordering — FR-735's generator will emit needs: dependencies between phase N and phase N+1 modules. Hand-written workflows don't implement that; if you need to rebuild Phase 0 before Phase 1 during a mass refresh, use workflow_dispatch on each workflow in order.