CI/CD & Environments

This guide covers the deployment pipeline, environment types, and automation workflows.

Environments

The monorepo supports three environment types:

Type Name Pattern Purpose Lifecycle
Production production Live user-facing environment Manual deploy via workflow_dispatch
Integration integration Staging / continuous integration Auto-deploys on merge to main
Ephemeral pr0001pr9999 Per-PR preview environments Auto-created on PR, destroyed on close

Environment Differences

Aspect Production Integration Ephemeral
DynamoDB removal RETAIN DESTROY DESTROY
Log retention 1 year 2 weeks 2 weeks
Lambda minification Yes No No
Source maps No Yes Yes
Self-signup No No Yes
SSO sign-in Yes Yes No
Assets bucket Yes Yes No
Data seeding Copied from integration
Docs publishing Yes (canonical) Yes (staging preview) No

CI/CD Workflows

Deployment Workflows

Workflow Trigger Environment
deploy-integration.yml Push to main integration
deploy-production.yml Manual (workflow_dispatch) production
deploy-ephemeral.yml PR opened/updated ephemeral (per-PR)
destroy-ephemeral.yml PR closed Destroys ephemeral stack

Verification Workflow (verify.yml)

Runs on every PR (ready for review / synchronize):

  1. Lintpnpm check --affected (lint + type-check + format across affected workspaces)
  2. Unit Testspnpm test across workspaces (with coverage)
  3. DB Integration Tests — backend repository tests against a Postgres service container
  4. Deployability — runs the domain/cert/quota preflight (preflightCustomDomain) then cdk synth for the PR's ephemeral stack, via deployability.orchestrator.ts. This catches CDK synthesis errors and deploy-invariant violations on the PR, so a green pipeline means "safe to deploy," not just "compiles and passes unit tests." It reuses the deploy orchestrator's fail-fast opening but does not seed reservations, run hooks, or deploy; a synth error that only manifests once shared policy refs are present is not caught here.
  5. JIRA Ticket Check — Validates PR title/branch naming

Deployment Pipeline (base-deploy.yml)

The reusable deployment workflow runs these jobs:

frontend-build ──┐
                  ├──► frontend-upload
infrastructure ──┘

     └──► publish-docs (production + integration only)
  1. frontend-build — Installs deps, builds Vite app, uploads artifact
  2. infrastructure — Runs the deploy orchestration script (see Deploy Orchestration below)
  3. frontend-upload — Downloads build artifact, syncs to S3, invalidates CloudFront
  4. publish-docs — Publishes Scalar documentation (production and integration only)

Docs Publishing: Documentation is published for production and integration only. Each environment has its own Scalar project and custom domain: production at monorepo-starter-docs.wseng.rp.devfactory.com, integration at monorepo-starter-docs-int.wseng.rp.devfactory.com. The API reference automatically points to the environment's own API, so reviewers can test endpoints directly from the docs. Ephemeral (PR) environments do not publish docs.

Deploy Orchestration

The PR-time Deployability check (in verify.yml) reuses this orchestration's fail-fast opening — the preflight plus cdk synth — without the seed, deploy, and hook steps, so synth failures surface on the PR instead of waiting for a deploy cycle.

The infrastructure job runs pnpm run deploy:<type> from apps/infra/, which invokes deploy.orchestrator.ts. This orchestrator runs the full deploy lifecycle in a single process:

  1. Seed reservations — provisions shared account-level resources (CloudFront origin request policies) and writes SSM reservations to protect them from concurrent cleanup
  2. Synth — runs cdk synth to produce the CloudFormation template (separate step because pre-deploy reads the synthesized template)
  3. Pre-deploy (pre-deploy.hook.ts) — imports orphaned CloudWatch log groups so CDK can adopt them
  4. CDK deploy — applies the CloudFormation changeset (Lambda, API Gateway, DynamoDB, Cognito, CloudFront)
  5. Post-deploy (post-deploy.hook.ts):
    • Publishes environment config JSON to S3 (used by frontend for environment switching)
    • Runs optional external integrations via the integration registry (e.g., Slack notifications, Datadog dashboard upserts)
    • Seeds ephemeral environments (copies users and DynamoDB data from integration)
    • Seeds the API and E2E test users (ephemeral and integration; never production) and stores their credentials in SSM, where pnpm script update-env and the post-deploy test jobs pick them up

Postgres Database Provisioning

Each environment connects to its own database on the shared dev Postgres cluster, named dtch_ws_mono_st_<env> (apps/infra/app.ts). The deploy does not create the database:

  • Ephemeral: deploy-ephemeral.yml has a Create DB Schema step that invokes the shared wseng-db-helper Lambda (create-ephemeral-schema, idempotent) before CDK deploy; destroy-ephemeral.yml enqueues its deletion.

  • Integration: the database is provisioned once during repo bootstrap, with the same Lambda:

    aws lambda invoke \
      --function-name arn:aws:lambda:us-east-1:856284715153:function:wseng-db-helper \
      --payload '{"type":"create-ephemeral-schema","product":"WsMonoSt","name":"dtch_ws_mono_st_integration","user":"<DB_USERNAME from the integration secret>"}' \
      --cli-binary-format raw-in-base64-out response.json
    

    If the integration deploy fails at the MigrationTrigger custom resource with database "..." does not exist, this step is the fix.

  • Production: the wseng-db-helper create path only targets the dev cluster; production database provisioning is a separate, deliberate operation (see issue #138).

Destroy Orchestration

When a PR is closed, destroy-ephemeral.yml runs pnpm run destroy:ephemeral from apps/infra/, which invokes destroy.orchestrator.ts:

  1. Pre-destroy (pre-destroy.hook.ts) — runs any pre-destroy validation
  2. CDK destroy — tears down the CloudFormation stack
  3. Post-destroy (post-destroy.hook.ts):
    • Deletes S3 environment configs
    • Removes SSM parameters
    • Cleans up orphaned CloudWatch log groups from CDK custom resources
    • Runs optional integration cleanup (e.g., revokes external resources registered by integrations)
    • Releases and sweeps SSM reservations for shared resources

Post-destroy is intentionally skipped when CDK destroy fails — the stack still exists, so cleaning up reservations or shared policies would be incorrect. On failure, the workflow opens a GitHub issue for manual intervention.

Note: Manual API calls can be made via the Scalar interactive docs (/docs/viewer) or by running the auto-generated tests in packages/api-tests/.

Automation Workflows

Workflow Schedule Purpose
auto-update-branches.yml Every 30 min Keeps feature branches up-to-date with main
auto-release-accepted.yml Every 15 min Auto-releases accepted PRs
review-pr.yml On PR events Automated PR review

Deployment Flow (End-to-End)

A typical integration deployment follows this sequence:

  1. Developer merges a PR into main
  2. deploy-integration.yml triggers automatically
  3. It calls base-deploy.yml with environment: integration
  4. frontend-build compiles the React app with Vite
  5. infrastructure runs the deploy orchestration script (deploy:integration), which seeds account-level resources, synthesizes, runs pre-deploy hooks, deploys the CDK stack, and runs post-deploy hooks (environment config publish, integration sync)
  6. frontend-upload syncs the built assets to S3 and invalidates the CloudFront cache
  7. publish-docs generates and publishes Scalar documentation with the latest API spec

For PR environments, the same pipeline runs with environment: prNNNN (the environment type is derived automatically from the name), but the publish-docs job is skipped (docs are only published for production and integration).

Managing Environments

Syncing Local .env from a Deployed Environment

# From the integration environment
pnpm script update-env --env integration

# From a specific PR environment
pnpm script update-env --pr 20

# Wait for a stack that is still deploying
pnpm script update-env --env integration --wait

Accessing Deployed Environments

Environment Frontend URL API URL Docs URL
Production https://monorepo.wseng.rp.devfactory.com https://api-monorepo.wseng.rp.devfactory.com https://monorepo-starter-docs.wseng.rp.devfactory.com
Integration https://monorepo-integration.wseng.rp.devfactory.com https://api-monorepo-integration.wseng.rp.devfactory.com https://monorepo-starter-docs-int.wseng.rp.devfactory.com
Ephemeral PR-specific CloudFront URL PR-specific API Gateway URL

Ephemeral environment URLs are output by the deploy step and posted as a PR comment.