Skip to content

Deployment Workflows

Wavemap separates CI, app/API CD, docs publishing, and infrastructure mutation because they answer different questions.

  • CI asks whether a commit is healthy enough to merge.
  • Deployed-dev CD asks whether the selected ref, artifacts, credentials, runtime config, cloud state, and smoke target can run in the shared dev environment.
  • Docs CD asks whether the static docs artifact can build, publish, and smoke independently from the app/API runtime.
  • Infrastructure mutation asks whether a cloud resource graph transition is safe to apply.

The CI workflow is .github/workflows/ci.yml. It does not mutate deployed infrastructure.

Current CI jobs:

  • verify_tooling_and_code
  • docs_build
  • i18n_verification
  • fe_unit_tests
  • fe_e2e_smoke
  • docker_builds

The docs build job validates the Starlight site. The i18n job keeps localization failures visible as i18n contract failures rather than browser smoke surprises.

The deployed-dev workflow is .github/workflows/deploy-dev.yml.

Command contracts for this lane live in the Deploy Dev command reference.

Pushes to develop run the conservative deploy-endpoint profile:

  1. Preflight.
  2. Read-only cloud deploy plan.
  3. Backend and frontend image build/push.
  4. Runtime deploy through the Pulumi-modeled SSM document.
  5. Read-only database migration status.
  6. Migration-only repair when the deployed database is behind the deployed API image.
  7. Endpoint smoke.
  8. Last-good runtime release receipt with app image identity and final migration state.
  9. Rollback attempt plus endpoint smoke if a push-triggered runtime deploy or endpoint smoke fails before a migration ran.

Reviewed operator procedures for workflow dispatch recipe selection, host stop/wake recovery, runtime config readiness, runtime deploy, last-good release receipts, rollback, database reset, and media discrepancy reporting live in Runbooks.

Manual dispatch exposes recipe-first profiles:

ProfilePurpose
preflightLocal/repo checks without cloud mutation.
cloud-planCloud-aware non-mutating validation and dry-run planning.
deploy-endpointApp/API deploy with migration status/repair and endpoint smoke.
deploy-seeded-browserDeploy plus reset, seeded smoke, and browser smoke.
deploy-mediaDeploy plus reset, seeded smoke, media smoke, and browser media smoke.
deploy-lifecycleDeploy plus lifecycle-oriented wake/cold-start proof.
customExplicit one-off stage combinations.

Raw stage toggles should be treated as add-ons or custom controls. Operators should choose a named profile first.

CD is layered so each actor has a narrow job and a visible mutation boundary. When a deploy behavior changes, update the layer that owns the behavior instead of hiding it in GitHub YAML or duplicating it across notes.

LayerOwnsDoes Not Own
Human operatorProfile selection, approval of destructive or privileged operations, and interpretation of failure evidence.Silent infrastructure mutation, implicit database reset, or unreviewed permission expansion.
GitHub Actions workflowTrigger policy, selected ref checkout, environment protection, concurrency, job DAG, credential setup, permissions, artifacts, and summaries.Business logic hidden in YAML, decrypted runtime secrets, infrastructure graph changes during routine app CD, or command semantics.
Public wavemap CLIStable operator command routes and compatibility-script delegation.Live cloud execution details or typed file-contract internals.
Shell wrappersLive external execution, provider CLIs, wait loops, dry-run/--execute gates, GitHub output files, and operator process control.Public route registration, long-lived architecture decisions, or untyped parsing that belongs in operations helpers.
@wavemap/operationsTyped planning, validation, summaries, projections, helper file contracts, and no-cloud checks.Public CLI dependency, direct routine cloud mutation, or provider credential ownership.
Pulumi stackCloud resource graph, stable outputs, runtime SSM document, IAM/resource contracts, DNS, ingress, registry, runtime host, media storage, and control plane.Routine app release orchestration, image identity, database reset, smoke checks, or app-runtime rollback.
GitHub deploy roleNarrow OIDC-authenticated cloud permissions required by selected deploy jobs.Direct EC2 stop/start outside approved controls, Pulumi updates, runtime secret decryption, or broad inventory authority.
Runtime host and instance rolePull selected images, read /wavemap/dev/runtime/*, render env files and Compose, start containers, access media storage, and register with SSM.GitHub orchestration, Route53 control-plane writes, host shutdown authority, deploy profile selection, or workflow summaries.
Smoke jobsProve selected deployed behavior and publish diagnosable failure evidence.Repairing the app, mutating infrastructure, or silently relaxing failed gates.

The routine app/API CD handoff is:

  1. GitHub Actions selects the ref and profile, then resolves immutable source and image identity.
  2. Preflight runs repo-local checks before cloud authentication or mutation.
  3. The cloud plan reads the current deployment contract, validates the cloud target, checks live runtime-config metadata, and prints dry-run deploy plans.
  4. Backend and frontend image jobs build or reuse immutable sha-<full-git-sha> ECR tags.
  5. Runtime deploy sends the Pulumi-modeled SSM document with selected image URIs and deployment identity.
  6. The runtime host reads SSM runtime parameters, renders env files and Compose, pulls images, and starts the app stack.
  7. Database status compares the live Drizzle ledger against the deployed API image migration journal.
  8. A typed database gate converts that status into pass, migrate, or block.
  9. If the gate returns migrate, CD runs pending migrations only, then rechecks status.
  10. Endpoint smoke proves the public app, backend health/readiness, and i18n manifest after the database gate passes.
  11. A smoke-passing endpoint deploy records the last-good runtime release receipt with app image identity and migration state.
  12. Push-triggered endpoint deploy failures attempt app-runtime rollback to that receipt only when the run did not apply migrations.
  13. Optional recipe stages add destructive reset, seeded smoke, browser smoke, media proof, discrepancy reporting, or lifecycle proof only when selected.

Deployment contract migration status: app/API CD now has the repo-side contract-read path. Cloud jobs read deployment-contracts/<pulumiStack>/current.json from the private artifact store, unwrap contract.values into the existing planning surface, and no longer require a routine Pulumi Cloud token. The live run remains gated on applying the app deploy role’s contract-read grant and publishing a refreshed contract that includes runtimeDeployment.executor. The full store and actor map lives in the operator store and access model: routine CD reads deployment contracts, infra operators own Pulumi state, runtime secrets stay in SSM, and GitHub environment values remain bootstrap selectors rather than cloud state.

The deploy-time secret boundary is:

GitHub Actions
-> reads the sanitized deployment contract, runtime parameter references, image tags, and deploy metadata
-> sends SSM command payloads that reference parameter paths
Runtime host
-> reads /wavemap/dev/runtime/* from SSM Parameter Store
-> renders env files and docker-compose.yml locally
-> pulls selected images and starts containers

GitHub workflow logs, summaries, and artifacts must not contain decrypted SecureString values or rendered env files.

Docs CD is a separate static lane. It reads the current sanitized deployment contract, builds the docs app, publishes apps/wavemap-docs/dist through the docs deploy command, invalidates the docs CloudFront distribution, and smokes docs.wavemap.app. It does not use Pulumi stack output reads during routine deploy, the EC2 app runtime, backend API, database, wake path, app media bucket, app runtime release receipt, or deployed-dev rollback path.

When responsibilities change, update the matching reference:

Change TypeDocumentation Home
Public command, flag, compatibility script, wrapper owner, or default mutation postureWavemap CLI Command Reference
Pipeline order, actor responsibilities, profile shape, smoke-lane promotion, or CD secret boundaryThis page
Reviewed manual procedure, recovery path, destructive reset, rollback, or discrepancy reportRunbooks
Pulumi graph transition, IAM/trust boundary, resource lifecycle class, or infrastructure mutation policyInfrastructure Change Policy
Cloud API action, resource scope, actor trust boundary, evidence, or explicit automation non-goalCloud Automation Permission Review
Runtime value ownership, cost posture, control plane, or lifecycle boundaryDeployed Dev Environment
Docs hosting target, docs publish permissions, or docs smoke behaviorDocs Hosting

Smoke lanes are promoted by cost, destructiveness, and signal quality.

Command contracts for deployed-dev and docs smoke checks live in the Smoke command reference.

LaneDefault PostureWhat It Proves
Endpoint smokeRoutine branch-CD gate.Frontend readiness, app entrypoint, backend health/ready routes, and i18n manifest availability.
Wake smokeManual/add-on.The wake route and endpoint smoke still work when the host is already awake or can be woken.
Browser routing smokeManual/add-on candidate for promotion.Non-destructive public route rendering and unauthenticated redirect behavior.
Database statusManual/read-only operator check.Live Drizzle ledger state matches, is behind, or is ahead of the deployed API image.
Database migrateManual/schema mutation with --execute.Pending migrations can be applied without reset or seed when the DB is safely behind the image.
Database resetManual/profile-scoped and destructive.Disposable deployed-dev data can be reset intentionally.
Seeded smokeRuns after opted-in reset.Seeded API and page routes work against known data.
Browser smokeManual/profile-scoped.Seeded browser interactions work with useful Playwright artifacts on failure.
Media smokeManual/profile-scoped.Upload/delete storage semantics and public media URLs work against real S3/CloudFront.
Browser media smokeManual/profile-scoped.Browser-rendered media works through the deployed path.
Cold-start browser smokeManual lifecycle proof.A deliberately stopped host serves the cold-start page, wakes, and returns to the original route.

Broader E2E remains release-confidence expansion. It should not become the default deployed-dev gate until auth/session setup, seed ownership, cleanup, artifacts, and runtime timing are boring.

The docs workflow is .github/workflows/deploy-docs.yml.

Command contracts for publishing and smoke live in the Deploy Docs and Smoke command reference sections.

It is independent from the app/API deploy workflow:

  1. Check out the selected ref.
  2. Prepare docs deploy cloud context.
  3. Read and validate the current deployment contract from the private artifact store.
  4. Build with pnpm build:docs.
  5. Publish apps/wavemap-docs/dist with pnpm wavemap -- deploy docs publish.
  6. Smoke with pnpm wavemap -- smoke docs.
  7. Write a docs deploy summary.

The docs workflow supports manual dispatch and path-filtered develop pushes. Changes under apps/wavemap-docs/working-notes/** do not trigger docs publish by themselves.

Prefer public pnpm wavemap -- ... route names in durable docs and operator notes:

Terminal window
pnpm wavemap -- --help
pnpm wavemap -- deploy dev preflight
pnpm wavemap -- deploy dev contracts
pnpm wavemap -- deploy dev cloud-plan
pnpm wavemap -- deploy dev diagnostics endpoints
pnpm wavemap -- deploy dev runtime deploy
pnpm wavemap -- deploy dev runtime rollback
pnpm wavemap -- deploy dev database status
pnpm wavemap -- deploy dev database migrate
pnpm wavemap -- deploy dev database reset
pnpm wavemap -- deploy dev media discrepancy-report
pnpm wavemap -- deploy docs publish
pnpm wavemap -- smoke dev
pnpm wavemap -- smoke docs
pnpm wavemap -- infra topology capture
pnpm wavemap -- infra topology project-inventory
pnpm wavemap -- infra topology project-graph
pnpm wavemap -- infra topology project-diagram

pnpm wm -- ... is available as a local shortcut. Root scripts such as pnpm deploy:dev:runtime, pnpm smoke:docs, and pnpm infra:topology:capture remain compatibility aliases for humans, CI, and older notes; document them beside the public route rather than as a separate command surface. The canonical public surface lives in the Wavemap CLI Command Reference.

Relevant command reference sections: Deploy Dev, Deploy Docs, Smoke, and Infra Topology.

The public CLI routes to shell wrappers. Shell wrappers may call @wavemap/operations for typed planning, validation, projection, summaries, and helper files. Shell keeps live external command execution and explicit mutation gates.

  • Keep behavior inside repo-owned commands where practical; let GitHub Actions orchestrate checkout, setup, environment selection, concurrency, permissions, and summaries.
  • Use immutable image identity: deploymentVersion = imageTag = sha-<full-git-sha>.
  • Keep branch-triggered deploys conservative until heavier lanes prove stable and low-noise.
  • Keep docs deployment static and separate from the app/API runtime.
  • Keep Pulumi updates outside normal app-release CD. Before any workflow performs pulumi up, it must publish the preview summary contract and wait for explicit approval.
  • Keep Pulumi state backend migration in the infra-operator runbook path. Routine app/API and docs CD should keep reading deployment contracts, not Pulumi state, regardless of whether the current Pulumi backend is Pulumi Cloud or S3.