CI/CD
Two pipelines. One proves a pull request; the other walks a build through six regional environments, each behind a manual approval.
- Pipelines 2 — PR, and deploy
- Environments 6 regional, plus dev
- Gates Manual approval before every environment
- Production Not wired in
The PR pipeline
Runs on a pull request and does not deploy: install the SDK, restore, build, test, publish coverage.
Its job is to answer one question — would this break the build — before anyone reviews the change.
The deploy pipeline
Triggers on main. Builds once, then promotes the same artifacts through every environment.
flowchart TD
B["<b>Build & test</b><br/>restore · build · test · coverage<br/>publish API + migrator"]
D["Dev"]
TN["Test NZ"]
TA["Test AU"]
TC["Test CA"]
UN["UAT NZ"]
UA["UAT AU"]
UC["UAT CA"]
B --> D
D -.->|approval| TN
D -.->|approval| TA
D -.->|approval| TC
TN -.->|approval| UN
TA -.->|approval| UA
TC -.->|approval| UC
style B fill:#e8f4f9,stroke:#00415a,stroke-width:2px,color:#002230
style D fill:#f8fafc,stroke:#64748b,color:#0f172a
style TN fill:#fffbeb,stroke:#b45309,color:#451a03
style TA fill:#fffbeb,stroke:#b45309,color:#451a03
style TC fill:#fffbeb,stroke:#b45309,color:#451a03
style UN fill:#eef2ff,stroke:#4f46e5,color:#1e1b4b
style UA fill:#eef2ff,stroke:#4f46e5,color:#1e1b4b
style UC fill:#eef2ff,stroke:#4f46e5,color:#1e1b4b
Build once, deploy many. Every environment receives the artifacts produced by the single build stage. Nothing is rebuilt per environment, so what reaches UAT is byte-for-byte what passed the tests.
Countries promote independently. UAT for a country is gated on that country's test environment, not on the others. New Zealand can reach UAT while Australia is still being verified.
Every environment is a manual gate. There is no automatic promotion anywhere in the chain.
What a deployment actually does
Three steps, in this order, per environment:
| 1. Migrate the database | The migrator runs first, as a standalone artifact — no source tree needed |
| 2. Configure app settings | Settings and secret references are written to the app service |
| 3. Deploy the application | The API is published to the app service |
Important
Migrations run before the new code. For the duration of a deployment the old application is running against the new schema. Every migration therefore has to be backwards-compatible with the code currently deployed — add a column before you read it, and drop one only in a later release than the one that stopped writing it. A migration that renames or removes something the running code still uses will break the site during its own deployment.
Vulnerability gating
The restore step is also the security gate. NuGet audit raises advisories as build warnings, and warnings are promoted to errors — so a dependency with a known advisory fails the build rather than producing a report nobody reads.
Clearing one means bumping the version: in the shared build properties for a transitive dependency, or in the project file for a direct one.
Environments
| Environment | Purpose |
|---|---|
| Dev | Shared development environment. First stop after a build |
| Test — NZ, AU, CA | Regional verification |
| UAT — NZ, AU, CA | Business acceptance |
Each is a complete deployment with its own database and configuration. There is no production environment in the pipeline yet, which is why the platform is described as pre-live.
Known rough edges
Documentation changes trigger a deployment. The trigger path list includes docs/**, so editing
this page queues the full deploy pipeline and waits on approval gates that nobody intends to click. It
was harmless when docs/ held a handful of files; with a documentation site in the repository it is
noise. Removing docs/** from the trigger, or excluding it, would fix it.
Coverage is observed, not enforced. Coverage is published and trended, with no threshold and no gate. That is a deliberate starting position — a number to watch before deciding what to require.
No production stamp is wired in. Adding one is a pipeline change plus an environment, not a redesign.
Where it is defined
azure/pipelines/azure-pipelines.yml |
The deploy pipeline and its stage chain |
azure/pipelines/azure-pipelines-pr.yml |
The PR pipeline |
azure/pipelines/templates/deploy-environment.yml |
What one environment's deployment does |
azure/pipelines/parameters/*.yml |
Per-environment names, hostnames and settings |
Infrastructure itself is not defined here. This repository deploys into infrastructure owned by a separate Bicep repository; it contains no infrastructure-as-code of its own.