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 &amp; 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.