Deploy Model

A multi-repo deploy is manifest-driven and orchestrated centrally. A service repo holds almost no deploy logic — it just calls into foundry-ops, which owns the pipeline.

The flow

service-repo push
  → thin caller workflow (in the service repo)
    → foundry-ops reusable deploy.yml  (workflow_call)
      → orchestrator (orchestrator/deploy.py)
        → reads foundry-ops/platform.json, dispatches on deploy.strategy
          → smart IaC: tofu plan → apply ONLY on change
          → build
          → publish (e.g. S3 sync + CloudFront invalidate for static)

Who owns what

PieceLives inRole
Platform shapefoundry-ops/platform.jsonDeclares services, strategies, IaC targets
Thin callerEach service repoMinimal workflow that calls foundry-ops
Reusable workflow + orchestratorfoundry-opsCheckout, credentials, and the deploy engine
Infrastructurefoundry-iac + per-repo ci/iacShared control plane + app-edge stacks

Smart IaC

The orchestrator plans the service's IaC stack with tofu plan -detailed-exitcode and only applies when there is an actual change (exit code 2); a no-change plan (exit 0) skips the apply. This keeps every deploy cheap and idempotent. Details in Orchestrator & Strategies.

Two deploy models coexist today

Monorepo (in-repo): foundry generate emits a self-contained pipeline + ci/scripts/ that run inside the same repo as the manifest. This is what a single-repo platform uses.

Multi-repo (orchestrated): the flow above, owned by foundry-ops. Today it implements the static strategy end-to-end; service, desktop, and game-publisher are stubbed. Thin callers are generated from the central manifest by foundry generate callers; the reusable workflow and orchestrator engine are hand-maintained in the ops repo.