From 2b15ba611680e9a7a26b98280a19523189b3b137 Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 12:46:53 +0100 Subject: [PATCH 1/7] switching to pre-release style handling --- .github/workflows/base-deploy.yml | 259 ++++++++++++++++++ .github/workflows/cicd-3-deploy.yaml | 204 -------------- .../{cicd-4-test.yaml => cicd-3-test.yaml} | 0 .github/workflows/cicd-4-preprod-deploy.yml | 32 +++ .github/workflows/cicd-5-prod-deploy.yml | 20 ++ DEPLOYMENT.md | 168 ++++++++++++ README.md | 5 + 7 files changed, 484 insertions(+), 204 deletions(-) create mode 100644 .github/workflows/base-deploy.yml delete mode 100644 .github/workflows/cicd-3-deploy.yaml rename .github/workflows/{cicd-4-test.yaml => cicd-3-test.yaml} (100%) create mode 100644 .github/workflows/cicd-4-preprod-deploy.yml create mode 100644 .github/workflows/cicd-5-prod-deploy.yml create mode 100644 DEPLOYMENT.md diff --git a/.github/workflows/base-deploy.yml b/.github/workflows/base-deploy.yml new file mode 100644 index 000000000..086272309 --- /dev/null +++ b/.github/workflows/base-deploy.yml @@ -0,0 +1,259 @@ +name: Base Deploy + +on: + workflow_call: + inputs: + environment: + description: "Target environment (preprod | prod)" + required: true + type: string + ref: + description: "Git ref to deploy (branch/tag/SHA). For prod, supply the RC tag to promote." + required: true + type: string + release_type: + description: "Version bump for base version (preprod only: patch|minor|major)" + required: false + default: "patch" + type: string + secrets: {} + +jobs: + metadata: + name: "Set CI/CD metadata" + runs-on: ubuntu-latest + timeout-minutes: 2 + outputs: + build_datetime: ${{ steps.variables.outputs.build_datetime }} + build_timestamp: ${{ steps.variables.outputs.build_timestamp }} + build_epoch: ${{ steps.variables.outputs.build_epoch }} + nodejs_version: ${{ steps.variables.outputs.nodejs_version }} + python_version: ${{ steps.variables.outputs.python_version }} + terraform_version: ${{ steps.variables.outputs.terraform_version }} + ref: ${{ steps.variables.outputs.ref }} + environment: ${{ steps.variables.outputs.environment }} + steps: + - name: "Checkout ref" + uses: actions/checkout@v5 + with: + ref: ${{ inputs.ref }} + fetch-depth: 0 # get full history + tags + + - name: "Set CI/CD variables" + id: variables + shell: bash + run: | + set -euo pipefail + datetime=$(date -u +'%Y-%m-%dT%H:%M:%S%z') + echo "build_datetime=$datetime" >> $GITHUB_OUTPUT + echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT + echo "nodejs_version=$(grep -E '^nodejs' .tool-versions 2>/dev/null | cut -d' ' -f2 | head -n1)" >> $GITHUB_OUTPUT + echo "python_version=$(grep -E '^python' .tool-versions 2>/dev/null | cut -d' ' -f2 | head -n1)" >> $GITHUB_OUTPUT + echo "terraform_version=$(grep -E '^terraform' .tool-versions 2>/dev/null | cut -d' ' -f2 | head -n1)" >> $GITHUB_OUTPUT + echo "ref=${{ inputs.ref }}" >> $GITHUB_OUTPUT + echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT + + - name: "List variables" + shell: bash + run: | + export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}" + export BUILD_TIMESTAMP="${{ steps.variables.outputs.build_timestamp }}" + export BUILD_EPOCH="${{ steps.variables.outputs.build_epoch }}" + export NODEJS_VERSION="${{ steps.variables.outputs.nodejs_version }}" + export PYTHON_VERSION="${{ steps.variables.outputs.python_version }}" + export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}" + export REF="${{ steps.variables.outputs.ref }}" + export ENVIRONMENT="${{ steps.variables.outputs.environment }}" + echo "build_datetime=$BUILD_DATETIME" + echo "build_timestamp=$BUILD_TIMESTAMP" + echo "build_epoch=$BUILD_EPOCH" + echo "nodejs_version=$NODEJS_VERSION" + echo "python_version=$PYTHON_VERSION" + echo "terraform_version=$TERRAFORM_VERSION" + echo "ref=$REF" + echo "environment=$ENVIRONMENT" + + deploy: + name: "Deploy to ${{ needs.metadata.outputs.environment }}" + runs-on: ubuntu-latest + needs: [metadata] + timeout-minutes: 45 + permissions: + id-token: write + contents: write + environment: ${{ needs.metadata.outputs.environment }} + steps: + - name: "Setup Terraform" + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ needs.metadata.outputs.terraform_version }} + + - name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: "Checkout repository at ref" + uses: actions/checkout@v5 + with: + ref: ${{ needs.metadata.outputs.ref }} + fetch-depth: 0 + + - name: "Build lambda artefact" + shell: bash + run: | + make dependencies install-python + make build + + - name: "Upload lambda artefact" + uses: actions/upload-artifact@v4 + with: + name: lambda + path: dist/lambda.zip + + - name: "Download Built Lambdas" + uses: actions/download-artifact@v5 + with: + name: lambda + path: ./build + + - name: "Configure AWS Credentials" + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role + aws-region: eu-west-2 + + - name: "Terraform Apply" + env: + ENVIRONMENT: ${{ needs.metadata.outputs.environment }} + WORKSPACE: "default" + TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }} + TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }} + TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }} + working-directory: ./infrastructure + shell: bash + run: | + set -euo pipefail + mkdir -p ./build + echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=networking tf-command=apply" + make terraform env=$ENVIRONMENT stack=networking tf-command=apply workspace=$WORKSPACE + echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=apply" + make terraform env=$ENVIRONMENT stack=api-layer tf-command=apply workspace=$WORKSPACE + + # ---------- Preprod path: create RC tag + pre-release ---------- + - name: "Create/Push RC tag for preprod" + if: ${{ needs.metadata.outputs.environment == 'preprod' }} + id: rc_tag + shell: bash + run: | + set -euo pipefail + git fetch --tags + + # Helper: get latest final and latest RC (across all bases) + latest_final="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1 || true)" + latest_any_rc="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*-rc.*' \ + | sort -V | tail -n1 || true)" + + # Determine the base version (vX.Y.Z) we will use for the next RC. + # If release_type=rc and we already have RCs, keep the SAME base as the latest RC. + # Otherwise, derive base from latest FINAL and bump per release_type. + if [[ "${{ inputs.release_type }}" == "rc" && -n "${latest_any_rc}" ]]; then + base="${latest_any_rc%-rc.*}" # strip '-rc.N' → vX.Y.Z + else + # Start from latest FINAL (or 0.0.0 if none) + if [[ -z "${latest_final}" ]]; then + base_major=0; base_minor=0; base_patch=0 + else + IFS='.' read -r base_major base_minor base_patch <<< "${latest_final#v}" + fi + + case "${{ inputs.release_type }}" in + major) base_major=$((base_major+1)); base_minor=0; base_patch=0 ;; + minor) base_minor=$((base_minor+1)); base_patch=0 ;; + patch|rc|*) base_patch=$((base_patch+1)) ;; # 'rc' with no prior RCs → default to patch bump + esac + + base="v${base_major}.${base_minor}.${base_patch}" + fi + + # Compute next RC number for this base + last_rc_for_base="$(git tag -l "${base}-rc.*" | sort -V | tail -n1 || true)" + if [[ -z "${last_rc_for_base}" ]]; then + next_rc="${base}-rc.1" + else + n="${last_rc_for_base##*-rc.}" + next_rc="${base}-rc.$((n+1))" + fi + + # Tag current commit (whatever ref was checked out) + sha="$(git rev-parse HEAD)" + echo "Tagging ${sha} as ${next_rc}" + git tag -a "${next_rc}" "${sha}" -m "Release candidate ${next_rc}" + git push origin "${next_rc}" + + echo "rc=${next_rc}" >> "$GITHUB_OUTPUT" + + - name: "Create GitHub Pre-release (preprod)" + if: ${{ needs.metadata.outputs.environment == 'preprod' }} + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.rc_tag.outputs.rc }} + release_name: "Pre-release ${{ steps.rc_tag.outputs.rc }}" + body: | + Auto pre-release created during preprod deployment. + draft: false + prerelease: true + + # ---------- Prod path: promote RC to final ---------- + - name: "Validate input is an RC tag (prod)" + if: ${{ needs.metadata.outputs.environment == 'prod' }} + shell: bash + run: | + set -euo pipefail + ref="${{ needs.metadata.outputs.ref }}" + if [[ ! "$ref" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then + echo "ERROR: For prod, 'ref' must be an RC tag like v1.4.0-rc.2 (got: $ref)" + exit 1 + fi + git fetch --tags --quiet + if ! git rev-parse -q --verify "refs/tags/$ref" >/dev/null; then + echo "ERROR: Tag '$ref' does not exist on origin." + exit 1 + fi + + - name: "Create final tag from RC (prod)" + if: ${{ needs.metadata.outputs.environment == 'prod' }} + id: final_tag + shell: bash + run: | + set -euo pipefail + rc="${{ needs.metadata.outputs.ref }}" + final="${rc%-rc.*}" # strip '-rc.N' + sha=$(git rev-list -n 1 "$rc") + + if git rev-parse -q --verify "refs/tags/${final}" >/dev/null; then + echo "ERROR: Final tag ${final} already exists." + exit 1 + fi + + echo "Promoting $rc ($sha) to final $final" + git tag -a "${final}" "${sha}" -m "Release ${final}" + git push origin "${final}" + echo "final=${final}" >> $GITHUB_OUTPUT + + - name: "Create GitHub Release (prod)" + if: ${{ needs.metadata.outputs.environment == 'prod' }} + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.final_tag.outputs.final }} + release_name: "Release ${{ steps.final_tag.outputs.final }}" + body: | + Auto-release created during production deployment. + draft: false + prerelease: false diff --git a/.github/workflows/cicd-3-deploy.yaml b/.github/workflows/cicd-3-deploy.yaml deleted file mode 100644 index 96e2fe61c..000000000 --- a/.github/workflows/cicd-3-deploy.yaml +++ /dev/null @@ -1,204 +0,0 @@ -# Deploys a given tag to a given environment and tags for semantic versioning -# creates semantic release - -name: "CI/CD deploy" - -concurrency: - group: terraform-deploy-${{ github.event.inputs.environment }} - cancel-in-progress: false - -on: - workflow_dispatch: - inputs: - tag: - description: "This is the tag that is going to be deployed" - required: true - default: "latest" - environment: - description: "Target environment (e.g., test, preprod or prod)" - required: true - type: choice - options: - - preprod - - prod - release_type: - description: "Version bump type (patch, minor, major)" - required: false - default: "patch" - type: choice - options: - - patch - - minor - - major - -jobs: - metadata: - name: "Set CI/CD metadata" - runs-on: ubuntu-latest - timeout-minutes: 1 - outputs: - build_datetime: ${{ steps.variables.outputs.build_datetime }} - build_timestamp: ${{ steps.variables.outputs.build_timestamp }} - build_epoch: ${{ steps.variables.outputs.build_epoch }} - nodejs_version: ${{ steps.variables.outputs.nodejs_version }} - python_version: ${{ steps.variables.outputs.python_version }} - terraform_version: ${{ steps.variables.outputs.terraform_version }} - version: ${{ steps.variables.outputs.version }} - tag: ${{ steps.variables.outputs.tag }} - steps: - - name: "Checkout tag" - uses: actions/checkout@v5 - with: - ref: ${{ github.event.inputs.tag }} - - - name: "Set CI/CD variables" - id: variables - run: | - datetime=$(date -u +'%Y-%m-%dT%H:%M:%S%z') - echo "build_datetime=$datetime" >> $GITHUB_OUTPUT - echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT - echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT - echo "nodejs_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "python_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "terraform_version=$(grep "^terraform" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - # TODO: Get the version, but it may not be the .version file as this should come from the CI/CD Pull Request Workflow - echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT - echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT - - name: "List variables" - run: | - export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}" - export BUILD_TIMESTAMP="${{ steps.variables.outputs.build_timestamp }}" - export BUILD_EPOCH="${{ steps.variables.outputs.build_epoch }}" - export NODEJS_VERSION="${{ steps.variables.outputs.nodejs_version }}" - export PYTHON_VERSION="${{ steps.variables.outputs.python_version }}" - export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}" - export VERSION="${{ steps.variables.outputs.version }}" - export TAG="${{ steps.variables.outputs.tag }}" - make list-variables - deploy: - name: "Deploy to an environment" - runs-on: ubuntu-latest - needs: [metadata] - environment: ${{ inputs.environment }} - timeout-minutes: 30 - permissions: - id-token: write - contents: write - steps: - - name: "Setup Terraform" - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: ${{ needs.metadata.outputs.terraform_version }} - - - name: "Set up Python" - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: "Checkout Repository" - uses: actions/checkout@v5 - - - name: "Build lambda artefact" - run: | - make dependencies install-python - make build - - - name: "Upload lambda artefact" - uses: actions/upload-artifact@v4 - with: - name: lambda - path: dist/lambda.zip - - - name: "Download Built Lambdas" - uses: actions/download-artifact@v5 - with: - name: lambda - path: ./build - - - name: "Configure AWS Credentials" - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role - aws-region: eu-west-2 - - - name: "Terraform Apply" - env: - ENVIRONMENT: ${{ inputs.environment }} - WORKSPACE: "default" - TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }} - TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }} - TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }} - - # just planning for now for safety and until review - run: | - mkdir -p ./build - echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=networking tf-command=apply" - make terraform env=$ENVIRONMENT stack=networking tf-command=apply workspace=$WORKSPACE - echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=apply" - make terraform env=$ENVIRONMENT stack=api-layer tf-command=apply workspace=$WORKSPACE - working-directory: ./infrastructure - - - name: "Tag the deployment using incremental semantic versioning" - id: next_tag - run: | - # Fetch all tags and sort them semantically - git fetch --tags - latest_tag=$(git tag --list 'v*' | sort -V | tail -n 1) - echo "Latest tag: $latest_tag" - - if [[ -z "$latest_tag" ]]; then - next_tag="v0.1.0" - else - # Extract the version numbers - IFS='.' read -r major minor patch <<< "${latest_tag#v}" - case "${{ github.event.inputs.release_type }}" in - major) - major=$((major + 1)) - minor=0 - patch=0 - ;; - minor) - minor=$((minor + 1)) - patch=0 - ;; - patch|*) - patch=$((patch + 1)) - ;; - esac - - next_tag="v${major}.${minor}.${patch}" - fi - - echo "Next tag: $next_tag" - echo "tag=$next_tag" >> $GITHUB_OUTPUT - - - name: "Create GitHub Release" - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.next_tag.outputs.tag }} - release_name: Release ${{ steps.next_tag.outputs.tag }} - body: | - Auto-release created during deployment. - draft: false - prerelease: ${{ inputs.environment == 'preprod' }} - - # TODO: complete notify step - # success: - # name: "Success notification" - # runs-on: ubuntu-latest - # needs: [deploy] - # steps: - # - name: "Check prerequisites for notification" - # id: check - # run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT - # - name: "Notify on deployment to an environment" - # if: steps.check.outputs.secret_exist == 'true' - # uses: nhs-england-tools/notify-msteams-action@v0.0.4 - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }} - # message-title: "Notification title" - # message-text: "This is a notification body" - # link: ${{ github.event.pull_request.html_url }} diff --git a/.github/workflows/cicd-4-test.yaml b/.github/workflows/cicd-3-test.yaml similarity index 100% rename from .github/workflows/cicd-4-test.yaml rename to .github/workflows/cicd-3-test.yaml diff --git a/.github/workflows/cicd-4-preprod-deploy.yml b/.github/workflows/cicd-4-preprod-deploy.yml new file mode 100644 index 000000000..bbb9ba082 --- /dev/null +++ b/.github/workflows/cicd-4-preprod-deploy.yml @@ -0,0 +1,32 @@ +name: Preprod Deploy + +concurrency: + group: terraform-deploy-preprod + cancel-in-progress: false + +on: + workflow_dispatch: + inputs: + ref: + description: "Branch/Tag/SHA to deploy to preprod (dev tag)" + required: true + default: "main" + release_type: + description: "Version bump type (use 'rc' to keep the same base and just increment RC)" + required: true + default: "rc" + type: choice + options: + - rc + - patch + - minor + - major + +jobs: + call: + uses: ./.github/workflows/base-deploy.yml + with: + environment: preprod + ref: ${{ inputs.ref }} + release_type: ${{ inputs.release_type }} + secrets: inherit diff --git a/.github/workflows/cicd-5-prod-deploy.yml b/.github/workflows/cicd-5-prod-deploy.yml new file mode 100644 index 000000000..90edf55e4 --- /dev/null +++ b/.github/workflows/cicd-5-prod-deploy.yml @@ -0,0 +1,20 @@ +name: Prod Promote + +concurrency: + group: terraform-deploy-prod + cancel-in-progress: false + +on: + workflow_dispatch: + inputs: + ref: + description: "RC tag to promote (e.g. v1.4.0-rc.2)" + required: true + +jobs: + call: + uses: ./.github/workflows/base-deploy.yml + with: + environment: prod + ref: ${{ inputs.ref }} + secrets: inherit diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 000000000..61feb5b3c --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,168 @@ +# Deployment & Release Process + +This repo uses GitHub Actions to deploy through four environments: + +- **dev** – continuous integration deploys on push to `main`. Creates a `dev-` tag. +- **test** – manual deploy of an existing tag (usually a `dev-` tag). No releases or SemVer tags. +- **preprod** – manual deploy that **cuts/bumps a Release Candidate (RC)** tag (`vX.Y.Z-rc.N`) and creates a **GitHub pre-release**. +- **prod** – manual promotion of a specific RC to a **final SemVer tag** (`vX.Y.Z`) and a **GitHub Release**. + +Releases are immutable and auditable: +- **RC tags** live only in preprod. +- **Final tags** (`vX.Y.Z`) live only in prod and point to the exact commit that shipped. + +--- + +## Workflow Map + +| Stage | Workflow file | Trigger | What it does | Tags / Releases | +|------|-------------------------------------------------------------------------|---------|--------------|-----------------| +| **Pull Request** | `.github/workflows/cicd-1-pull-request.yml` | `pull_request` (opened/sync/reopened) | Commit/Test/Build/Acceptance stages | No tags/releases | +| **Dev** | `.github/workflows/cicd-2-publish.yml` | `push` to `main` | Builds & deploys to dev | Creates and pushes `dev-YYYYMMDDHHMMSS` tag | +| **Test** | `.github/workflows/cicd-3-test.yml` | Manual (`workflow_dispatch`) | Deploys the chosen tag to test | No tags, no releases | +| **Preprod** | `.github/workflows/cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Deploys chosen ref and **creates/bumps an RC tag**; pre-release | `vX.Y.Z-rc.N` + GitHub **pre-release** | +| **Prod** | `.github/workflows/cicd-5-prod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Promotes a specific RC to final | `vX.Y.Z` + GitHub **Release** | + +> **Note:** The preprod/prod entry workflows are thin wrappers around a **reusable** workflow (`base-deploy.yml`). + +--- + +## Pull Request Workflow (CI) + +**File:** `CI/CD pull request` + +- Triggers on PR events (not drafts). +- Runs stages: **commit → test → build → acceptance** via reusable stage files: + - `stage-1-commit.yaml` + - `stage-2-test.yaml` + - `stage-3-build.yaml` + - `stage-4-acceptance.yaml` +- Does not deploy or create tags/releases. + +--- + +## Dev Deployment (continuous on main) + +**File:** `CI/CD publish` +**Trigger:** push to `main` + +- Builds & deploys to **dev**. +- Creates a timestamped **dev tag**: `dev-YYYYMMDDHHMMSS` +- No SemVer, no GitHub Release. + +**Why:** fast feedback and a stable pointer (the dev tag) you can later promote to **test** or use as the **preprod ref**. + +--- + +## Test Deployment (manual, by tag) + +**File:** `CI/CD deploy to TEST` +**Trigger:** manual (`workflow_dispatch`) + +**Inputs** +- `tag`: the ref to deploy (e.g., a **dev** tag created by the dev workflow). +- `environment`: fixed to `test`. + +**Behavior** +- Checks out the provided tag, builds, and deploys to **test**. +- **No new tags** created. **No GitHub Releases** created. + +**Recommended usage** +- Deploy the **same commit** that was verified in dev by supplying the `dev-` tag here. + +--- + +## Preprod (Release Candidates) + +**Entry workflow:** `cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` +**Trigger:** manual (`workflow_dispatch`) + +### Inputs +- **`ref`**: branch/tag/SHA to deploy (`dev-` tag). +- **`release_type`**: one of: +- `patch` – start a new **patch** series → `vX.Y.(Z+1)-rc.1` +- `minor` – start a new **minor** series → `vX.(Y+1).0-rc.1` +- `major` – start a new **major** series → `v(X+1).0.0-rc.1` +- `rc` – **keep the same base** version and cut the **next RC** (e.g. `-rc.1` → `-rc.2`) + +### Behavior +- Tags the **checked-out commit** as the next **RC** (`vX.Y.Z-rc.N`) and pushes it. +- Deploys to **preprod**. +- Creates a **GitHub pre-release** for that RC. + +### When to use which `release_type` +- Use **`patch`/`minor`/`major`** when starting a **new base version** (first RC becomes `-rc.1`). +- Use **`rc`** when you need another candidate for the **same base** (`-rc.N+1`). + +--- + +## Prod (Final Releases) + +**Entry workflow:** `cicd-5-prod-deploy.yml` → calls `base-deploy.yml` +**Trigger:** manual (`workflow_dispatch`) + +### Inputs +- **`ref`**: the **RC tag** to promote (e.g. `v1.4.0-rc.2`). + +### Behavior +- Validates the RC tag exists. +- Creates the corresponding **final tag** (e.g. `v1.4.0`) at the **same commit**. +- Deploys to **prod**. +- Creates a **GitHub Release**. + +--- + +## Decision Guide (what to pick, when) + +| Situation | Workflow | Input: `ref` | Input: `release_type` | Result | +|-----------|----------|--------------|------------------------|--------| +| Deploy automatically after merge to main | **Dev** (auto) | `main` (implicit) | n/a | Deploys to dev, creates `dev-YYYYMMDDHHMMSS` | +| Deploy an existing build to test | **Test** (manual) | a tag (e.g. `dev-20250817…`) | n/a | Deploys to test (no tags/releases) | +| Start a **new patch release** into preprod | **Preprod** (manual) | branch/tag/SHA | `patch` | `vX.Y.(Z+1)-rc.1` + pre-release | +| Start a **new minor release** into preprod | **Preprod** | branch/tag/SHA | `minor` | `vX.(Y+1).0-rc.1` + pre-release | +| Start a **new major release** into preprod | **Preprod** | branch/tag/SHA | `major` | `v(X+1).0.0-rc.1` + pre-release | +| Cut **another candidate** for the **same base** | **Preprod** | branch/tag/SHA (same train) | `rc` | `vX.Y.Z-rc.N+1` + pre-release | +| Promote a **tested RC** to production | **Prod** (manual) | RC tag (e.g. `v1.4.0-rc.2`) | n/a | `v1.4.0` + GitHub Release | + +--- + +## Versioning Rules + +- **Dev tags**: `dev-YYYYMMDDHHMMSS` (automation convenience; never promoted to prod directly). +- **RC tags**: `vX.Y.Z-rc.N` (preprod only; immutable; one per candidate). +- **Final tags**: `vX.Y.Z` (prod only; immutable; exactly what shipped). + +**Promotion path** +- Choose a commit (often via a **dev tag**) → cut RC(s) in **preprod** → promote the selected RC to **prod**. + +--- + +## Common Q&A + +**Can I use a dev tag as the preprod `ref`?** +Yes. `ref` can be any branch/tag/SHA. Using a `dev-` tag guarantees you deploy the **same commit** previously built on dev. + +**What does `rc` mean?** +**Release Candidate**: a build that could become the final version if it passes testing. Each new candidate for the same base version increments the suffix: `-rc.1`, `-rc.2`, … + +**What if I already have `v1.4.0-rc.1` and I need another preprod build for the same release?** +Run **Preprod Deploy** with `release_type=rc` to get `v1.4.0-rc.2`. + +**What if I discover issues after preprod?** +Fix, then cut a new RC (`-rc.N+1`). Only promote to prod when ready. + +--- + +## Quick Examples + +**New minor release** +1. Dev (auto) → creates `dev-20250818…`. +2. Preprod (manual) → `ref=dev-20250818…`, `release_type=minor` → `v1.4.0-rc.1`. +3. Preprod (manual) → `release_type=rc` → `v1.4.0-rc.2`. +4. Prod (manual) → `ref=v1.4.0-rc.2` → final `v1.4.0`. + +**Same release, more candidates** +- Already at `v1.3.3-rc.1`. +- Preprod → `release_type=rc` → `v1.3.3-rc.2`, test, repeat as needed. +- Prod → promote the stable RC to `v1.3.3`. + diff --git a/README.md b/README.md index d27ef9560..efff99c6d 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The software will only be used for signposting an individual to an appropriate s - [Design](#design) - [Diagrams](#diagrams) - [Contributing](#contributing) + - [Deployment](#deployment) - [Contacts](#contacts) - [Licence](#licence) @@ -254,6 +255,10 @@ Describe or link templates on how to raise an issue, feature request or make a c - Backlog, board, roadmap, ways of working - High-level requirements, guiding principles, decision records, etc. +## Deployment + +See [DEPLOYMENT.md](./DEPLOYMENT.md) for details on how to cut release candidates and promote them to production. + ## Contacts Please contact the team on [Slack](https://nhsdigitalcorporate.enterprise.slack.com/archives/C08ATG7TBDW) From bae40d61e8bdae5d12b606b5c630faa65f8db0dc Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 12:54:53 +0100 Subject: [PATCH 2/7] formatting and english usage --- DEPLOYMENT.md | 32 +++++++++---------- .../config/vocabularies/words/accept.txt | 6 ++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 61feb5b3c..fa883afa5 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -15,13 +15,13 @@ Releases are immutable and auditable: ## Workflow Map -| Stage | Workflow file | Trigger | What it does | Tags / Releases | -|------|-------------------------------------------------------------------------|---------|--------------|-----------------| -| **Pull Request** | `.github/workflows/cicd-1-pull-request.yml` | `pull_request` (opened/sync/reopened) | Commit/Test/Build/Acceptance stages | No tags/releases | -| **Dev** | `.github/workflows/cicd-2-publish.yml` | `push` to `main` | Builds & deploys to dev | Creates and pushes `dev-YYYYMMDDHHMMSS` tag | -| **Test** | `.github/workflows/cicd-3-test.yml` | Manual (`workflow_dispatch`) | Deploys the chosen tag to test | No tags, no releases | -| **Preprod** | `.github/workflows/cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Deploys chosen ref and **creates/bumps an RC tag**; pre-release | `vX.Y.Z-rc.N` + GitHub **pre-release** | -| **Prod** | `.github/workflows/cicd-5-prod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Promotes a specific RC to final | `vX.Y.Z` + GitHub **Release** | +| Stage | Workflow file | Trigger | What it does | Tags / Releases | +|------------------|-------------------------------------------------------------------------|---------------------------------------|-----------------------------------------------------------------|---------------------------------------------| +| **Pull Request** | `.github/workflows/cicd-1-pull-request.yml` | `pull_request` (opened/sync/reopened) | Commit/Test/Build/Acceptance stages | No tags/releases | +| **Dev** | `.github/workflows/cicd-2-publish.yml` | `push` to `main` | Builds & deploys to dev | Creates and pushes `dev-YYYYMMDDHHMMSS` tag | +| **Test** | `.github/workflows/cicd-3-test.yml` | Manual (`workflow_dispatch`) | Deploys the chosen tag to test | No tags, no releases | +| **Preprod** | `.github/workflows/cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Deploys chosen ref and **creates/bumps an RC tag**; pre-release | `vX.Y.Z-rc.N` + GitHub **pre-release** | +| **Prod** | `.github/workflows/cicd-5-prod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Promotes a specific RC to final | `vX.Y.Z` + GitHub **Release** | > **Note:** The preprod/prod entry workflows are thin wrappers around a **reusable** workflow (`base-deploy.yml`). @@ -114,15 +114,15 @@ Releases are immutable and auditable: ## Decision Guide (what to pick, when) -| Situation | Workflow | Input: `ref` | Input: `release_type` | Result | -|-----------|----------|--------------|------------------------|--------| -| Deploy automatically after merge to main | **Dev** (auto) | `main` (implicit) | n/a | Deploys to dev, creates `dev-YYYYMMDDHHMMSS` | -| Deploy an existing build to test | **Test** (manual) | a tag (e.g. `dev-20250817…`) | n/a | Deploys to test (no tags/releases) | -| Start a **new patch release** into preprod | **Preprod** (manual) | branch/tag/SHA | `patch` | `vX.Y.(Z+1)-rc.1` + pre-release | -| Start a **new minor release** into preprod | **Preprod** | branch/tag/SHA | `minor` | `vX.(Y+1).0-rc.1` + pre-release | -| Start a **new major release** into preprod | **Preprod** | branch/tag/SHA | `major` | `v(X+1).0.0-rc.1` + pre-release | -| Cut **another candidate** for the **same base** | **Preprod** | branch/tag/SHA (same train) | `rc` | `vX.Y.Z-rc.N+1` + pre-release | -| Promote a **tested RC** to production | **Prod** (manual) | RC tag (e.g. `v1.4.0-rc.2`) | n/a | `v1.4.0` + GitHub Release | +| Situation | Workflow | Input: `ref` | Input: `release_type` | Result | +|-------------------------------------------------|----------------------|------------------------------|-----------------------|----------------------------------------------| +| Deploy automatically after merge to main | **Dev** (auto) | `main` (implicit) | n/a | Deploys to dev, creates `dev-YYYYMMDDHHMMSS` | +| Deploy an existing build to test | **Test** (manual) | a tag (e.g. `dev-20250817…`) | n/a | Deploys to test (no tags/releases) | +| Start a **new patch release** into preprod | **Preprod** (manual) | branch/tag/SHA | `patch` | `vX.Y.(Z+1)-rc.1` + pre-release | +| Start a **new minor release** into preprod | **Preprod** | branch/tag/SHA | `minor` | `vX.(Y+1).0-rc.1` + pre-release | +| Start a **new major release** into preprod | **Preprod** | branch/tag/SHA | `major` | `v(X+1).0.0-rc.1` + pre-release | +| Cut **another candidate** for the **same base** | **Preprod** | branch/tag/SHA (same train) | `rc` | `vX.Y.Z-rc.N+1` + pre-release | +| Promote a **tested RC** to production | **Prod** (manual) | RC tag (e.g. `v1.4.0-rc.2`) | n/a | `v1.4.0` + GitHub Release | --- diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 7ed71edb8..f9146a3f1 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -29,3 +29,9 @@ wireup Pydantic yanai Portman +repo +dev +preprod +Preprod +Dev +auditable From 07bb6e69f3e8c8bdaac5b238853314aff9136e1b Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 12:59:51 +0100 Subject: [PATCH 3/7] spacing issues --- DEPLOYMENT.md | 35 ++++++++++++------- .../config/vocabularies/words/accept.txt | 3 +- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index fa883afa5..34cac8e48 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -8,6 +8,7 @@ This repo uses GitHub Actions to deploy through four environments: - **prod** – manual promotion of a specific RC to a **final SemVer tag** (`vX.Y.Z`) and a **GitHub Release**. Releases are immutable and auditable: + - **RC tags** live only in preprod. - **Final tags** (`vX.Y.Z`) live only in prod and point to the exact commit that shipped. @@ -30,8 +31,8 @@ Releases are immutable and auditable: ## Pull Request Workflow (CI) **File:** `CI/CD pull request` +**Trigger:** PR events (not drafts) -- Triggers on PR events (not drafts). - Runs stages: **commit → test → build → acceptance** via reusable stage files: - `stage-1-commit.yaml` - `stage-2-test.yaml` @@ -59,15 +60,18 @@ Releases are immutable and auditable: **File:** `CI/CD deploy to TEST` **Trigger:** manual (`workflow_dispatch`) -**Inputs** +### Inputs + - `tag`: the ref to deploy (e.g., a **dev** tag created by the dev workflow). - `environment`: fixed to `test`. -**Behavior** +### Behavior + - Checks out the provided tag, builds, and deploys to **test**. - **No new tags** created. **No GitHub Releases** created. -**Recommended usage** +### Recommended usage + - Deploy the **same commit** that was verified in dev by supplying the `dev-` tag here. --- @@ -78,19 +82,22 @@ Releases are immutable and auditable: **Trigger:** manual (`workflow_dispatch`) ### Inputs + - **`ref`**: branch/tag/SHA to deploy (`dev-` tag). - **`release_type`**: one of: -- `patch` – start a new **patch** series → `vX.Y.(Z+1)-rc.1` -- `minor` – start a new **minor** series → `vX.(Y+1).0-rc.1` -- `major` – start a new **major** series → `v(X+1).0.0-rc.1` -- `rc` – **keep the same base** version and cut the **next RC** (e.g. `-rc.1` → `-rc.2`) + - `patch` – start a new **patch** series → `vX.Y.(Z+1)-rc.1` + - `minor` – start a new **minor** series → `vX.(Y+1).0-rc.1` + - `major` – start a new **major** series → `v(X+1).0.0-rc.1` + - `rc` – **keep the same base** version and cut the **next RC** (e.g. `-rc.1` → `-rc.2`) ### Behavior + - Tags the **checked-out commit** as the next **RC** (`vX.Y.Z-rc.N`) and pushes it. - Deploys to **preprod**. - Creates a **GitHub pre-release** for that RC. ### When to use which `release_type` + - Use **`patch`/`minor`/`major`** when starting a **new base version** (first RC becomes `-rc.1`). - Use **`rc`** when you need another candidate for the **same base** (`-rc.N+1`). @@ -102,9 +109,11 @@ Releases are immutable and auditable: **Trigger:** manual (`workflow_dispatch`) ### Inputs + - **`ref`**: the **RC tag** to promote (e.g. `v1.4.0-rc.2`). ### Behavior + - Validates the RC tag exists. - Creates the corresponding **final tag** (e.g. `v1.4.0`) at the **same commit**. - Deploys to **prod**. @@ -132,7 +141,8 @@ Releases are immutable and auditable: - **RC tags**: `vX.Y.Z-rc.N` (preprod only; immutable; one per candidate). - **Final tags**: `vX.Y.Z` (prod only; immutable; exactly what shipped). -**Promotion path** +### Promotion path + - Choose a commit (often via a **dev tag**) → cut RC(s) in **preprod** → promote the selected RC to **prod**. --- @@ -155,14 +165,15 @@ Fix, then cut a new RC (`-rc.N+1`). Only promote to prod when ready. ## Quick Examples -**New minor release** +### New minor release + 1. Dev (auto) → creates `dev-20250818…`. 2. Preprod (manual) → `ref=dev-20250818…`, `release_type=minor` → `v1.4.0-rc.1`. 3. Preprod (manual) → `release_type=rc` → `v1.4.0-rc.2`. 4. Prod (manual) → `ref=v1.4.0-rc.2` → final `v1.4.0`. -**Same release, more candidates** +### Same release, more candidates + - Already at `v1.3.3-rc.1`. - Preprod → `release_type=rc` → `v1.3.3-rc.2`, test, repeat as needed. - Prod → promote the stable RC to `v1.3.3`. - diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index f9146a3f1..1f1e49434 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -31,7 +31,8 @@ yanai Portman repo dev -preprod Preprod Dev auditable +DEV +preprod From ee04bbc016ccc0c6e26d3a481f3c19d6377a7edc Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 13:03:11 +0100 Subject: [PATCH 4/7] spelling --- DEPLOYMENT.md | 58 +++++++++---------- .../config/vocabularies/words/accept.txt | 3 - 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 34cac8e48..a410a0023 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -2,14 +2,14 @@ This repo uses GitHub Actions to deploy through four environments: -- **dev** – continuous integration deploys on push to `main`. Creates a `dev-` tag. -- **test** – manual deploy of an existing tag (usually a `dev-` tag). No releases or SemVer tags. -- **preprod** – manual deploy that **cuts/bumps a Release Candidate (RC)** tag (`vX.Y.Z-rc.N`) and creates a **GitHub pre-release**. +- **Dev** – continuous integration deploys on push to `main`. Creates a `Dev-` tag. +- **test** – manual deploy of an existing tag (usually a `Dev-` tag). No releases or SemVer tags. +- **Preprod** – manual deploy that **cuts/bumps a Release Candidate (RC)** tag (`vX.Y.Z-rc.N`) and creates a **GitHub pre-release**. - **prod** – manual promotion of a specific RC to a **final SemVer tag** (`vX.Y.Z`) and a **GitHub Release**. Releases are immutable and auditable: -- **RC tags** live only in preprod. +- **RC tags** live only in Preprod. - **Final tags** (`vX.Y.Z`) live only in prod and point to the exact commit that shipped. --- @@ -19,12 +19,12 @@ Releases are immutable and auditable: | Stage | Workflow file | Trigger | What it does | Tags / Releases | |------------------|-------------------------------------------------------------------------|---------------------------------------|-----------------------------------------------------------------|---------------------------------------------| | **Pull Request** | `.github/workflows/cicd-1-pull-request.yml` | `pull_request` (opened/sync/reopened) | Commit/Test/Build/Acceptance stages | No tags/releases | -| **Dev** | `.github/workflows/cicd-2-publish.yml` | `push` to `main` | Builds & deploys to dev | Creates and pushes `dev-YYYYMMDDHHMMSS` tag | +| **Dev** | `.github/workflows/cicd-2-publish.yml` | `push` to `main` | Builds & deploys to Dev | Creates and pushes `Dev-YYYYMMDDHHMMSS` tag | | **Test** | `.github/workflows/cicd-3-test.yml` | Manual (`workflow_dispatch`) | Deploys the chosen tag to test | No tags, no releases | -| **Preprod** | `.github/workflows/cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Deploys chosen ref and **creates/bumps an RC tag**; pre-release | `vX.Y.Z-rc.N` + GitHub **pre-release** | +| **Preprod** | `.github/workflows/cicd-4-Preprod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Deploys chosen ref and **creates/bumps an RC tag**; pre-release | `vX.Y.Z-rc.N` + GitHub **pre-release** | | **Prod** | `.github/workflows/cicd-5-prod-deploy.yml` → calls `base-deploy.yml` | Manual (`workflow_dispatch`) | Promotes a specific RC to final | `vX.Y.Z` + GitHub **Release** | -> **Note:** The preprod/prod entry workflows are thin wrappers around a **reusable** workflow (`base-deploy.yml`). +> **Note:** The Preprod/prod entry workflows are thin wrappers around a **reusable** workflow (`base-deploy.yml`). --- @@ -47,11 +47,11 @@ Releases are immutable and auditable: **File:** `CI/CD publish` **Trigger:** push to `main` -- Builds & deploys to **dev**. -- Creates a timestamped **dev tag**: `dev-YYYYMMDDHHMMSS` +- Builds & deploys to **Dev**. +- Creates a timestamped **Dev tag**: `Dev-YYYYMMDDHHMMSS` - No SemVer, no GitHub Release. -**Why:** fast feedback and a stable pointer (the dev tag) you can later promote to **test** or use as the **preprod ref**. +**Why:** fast feedback and a stable pointer (the Dev tag) you can later promote to **test** or use as the **Preprod ref**. --- @@ -62,7 +62,7 @@ Releases are immutable and auditable: ### Inputs -- `tag`: the ref to deploy (e.g., a **dev** tag created by the dev workflow). +- `tag`: the ref to deploy (e.g., a **Dev** tag created by the Dev workflow). - `environment`: fixed to `test`. ### Behavior @@ -72,18 +72,18 @@ Releases are immutable and auditable: ### Recommended usage -- Deploy the **same commit** that was verified in dev by supplying the `dev-` tag here. +- Deploy the **same commit** that was verified in Dev by supplying the `Dev-` tag here. --- ## Preprod (Release Candidates) -**Entry workflow:** `cicd-4-preprod-deploy.yml` → calls `base-deploy.yml` +**Entry workflow:** `cicd-4-Preprod-deploy.yml` → calls `base-deploy.yml` **Trigger:** manual (`workflow_dispatch`) ### Inputs -- **`ref`**: branch/tag/SHA to deploy (`dev-` tag). +- **`ref`**: branch/tag/SHA to deploy (`Dev-` tag). - **`release_type`**: one of: - `patch` – start a new **patch** series → `vX.Y.(Z+1)-rc.1` - `minor` – start a new **minor** series → `vX.(Y+1).0-rc.1` @@ -93,7 +93,7 @@ Releases are immutable and auditable: ### Behavior - Tags the **checked-out commit** as the next **RC** (`vX.Y.Z-rc.N`) and pushes it. -- Deploys to **preprod**. +- Deploys to **Preprod**. - Creates a **GitHub pre-release** for that RC. ### When to use which `release_type` @@ -125,11 +125,11 @@ Releases are immutable and auditable: | Situation | Workflow | Input: `ref` | Input: `release_type` | Result | |-------------------------------------------------|----------------------|------------------------------|-----------------------|----------------------------------------------| -| Deploy automatically after merge to main | **Dev** (auto) | `main` (implicit) | n/a | Deploys to dev, creates `dev-YYYYMMDDHHMMSS` | -| Deploy an existing build to test | **Test** (manual) | a tag (e.g. `dev-20250817…`) | n/a | Deploys to test (no tags/releases) | -| Start a **new patch release** into preprod | **Preprod** (manual) | branch/tag/SHA | `patch` | `vX.Y.(Z+1)-rc.1` + pre-release | -| Start a **new minor release** into preprod | **Preprod** | branch/tag/SHA | `minor` | `vX.(Y+1).0-rc.1` + pre-release | -| Start a **new major release** into preprod | **Preprod** | branch/tag/SHA | `major` | `v(X+1).0.0-rc.1` + pre-release | +| Deploy automatically after merge to main | **Dev** (auto) | `main` (implicit) | n/a | Deploys to Dev, creates `Dev-YYYYMMDDHHMMSS` | +| Deploy an existing build to test | **Test** (manual) | a tag (e.g. `Dev-20250817…`) | n/a | Deploys to test (no tags/releases) | +| Start a **new patch release** into Preprod | **Preprod** (manual) | branch/tag/SHA | `patch` | `vX.Y.(Z+1)-rc.1` + pre-release | +| Start a **new minor release** into Preprod | **Preprod** | branch/tag/SHA | `minor` | `vX.(Y+1).0-rc.1` + pre-release | +| Start a **new major release** into Preprod | **Preprod** | branch/tag/SHA | `major` | `v(X+1).0.0-rc.1` + pre-release | | Cut **another candidate** for the **same base** | **Preprod** | branch/tag/SHA (same train) | `rc` | `vX.Y.Z-rc.N+1` + pre-release | | Promote a **tested RC** to production | **Prod** (manual) | RC tag (e.g. `v1.4.0-rc.2`) | n/a | `v1.4.0` + GitHub Release | @@ -137,28 +137,28 @@ Releases are immutable and auditable: ## Versioning Rules -- **Dev tags**: `dev-YYYYMMDDHHMMSS` (automation convenience; never promoted to prod directly). -- **RC tags**: `vX.Y.Z-rc.N` (preprod only; immutable; one per candidate). +- **Dev tags**: `Dev-YYYYMMDDHHMMSS` (automation convenience; never promoted to prod directly). +- **RC tags**: `vX.Y.Z-rc.N` (Preprod only; immutable; one per candidate). - **Final tags**: `vX.Y.Z` (prod only; immutable; exactly what shipped). ### Promotion path -- Choose a commit (often via a **dev tag**) → cut RC(s) in **preprod** → promote the selected RC to **prod**. +- Choose a commit (often via a **Dev tag**) → cut RC(s) in **Preprod** → promote the selected RC to **prod**. --- ## Common Q&A -**Can I use a dev tag as the preprod `ref`?** -Yes. `ref` can be any branch/tag/SHA. Using a `dev-` tag guarantees you deploy the **same commit** previously built on dev. +**Can I use a Dev tag as the Preprod `ref`?** +Yes. `ref` can be any branch/tag/SHA. Using a `Dev-` tag guarantees you deploy the **same commit** previously built on Dev. **What does `rc` mean?** **Release Candidate**: a build that could become the final version if it passes testing. Each new candidate for the same base version increments the suffix: `-rc.1`, `-rc.2`, … -**What if I already have `v1.4.0-rc.1` and I need another preprod build for the same release?** +**What if I already have `v1.4.0-rc.1` and I need another Preprod build for the same release?** Run **Preprod Deploy** with `release_type=rc` to get `v1.4.0-rc.2`. -**What if I discover issues after preprod?** +**What if I discover issues after Preprod?** Fix, then cut a new RC (`-rc.N+1`). Only promote to prod when ready. --- @@ -167,8 +167,8 @@ Fix, then cut a new RC (`-rc.N+1`). Only promote to prod when ready. ### New minor release -1. Dev (auto) → creates `dev-20250818…`. -2. Preprod (manual) → `ref=dev-20250818…`, `release_type=minor` → `v1.4.0-rc.1`. +1. Dev (auto) → creates `Dev-20250818…`. +2. Preprod (manual) → `ref=Dev-20250818…`, `release_type=minor` → `v1.4.0-rc.1`. 3. Preprod (manual) → `release_type=rc` → `v1.4.0-rc.2`. 4. Prod (manual) → `ref=v1.4.0-rc.2` → final `v1.4.0`. diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 1f1e49434..603ea003b 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -30,9 +30,6 @@ Pydantic yanai Portman repo -dev Preprod Dev auditable -DEV -preprod From e8bcb7531af1cc74ea7b6c602e097cfde716b5a4 Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 13:06:38 +0100 Subject: [PATCH 5/7] adding to accept --- scripts/config/vale/styles/config/vocabularies/words/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 603ea003b..4a3c551cf 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -33,3 +33,4 @@ repo Preprod Dev auditable +DEV From ce6ffec6877b0321a97e105f8084b6ef64ed7ab8 Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 13:13:07 +0100 Subject: [PATCH 6/7] more fighting with vale --- README.md | 4 ++-- .../config/vale/styles/config/vocabularies/words/accept.txt | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index efff99c6d..a2a89de63 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The software will only be used for signposting an individual to an appropriate s - [Prerequisites](#prerequisites) - [Configuration](#configuration) - [Environment variables - Local](#environment-variables---local) - - [Environment variables - DEV, PROD or PRE-PROD](#environment-variables---dev-prod-or-pre-prod) + - [Environment variables - Dev, Prod or Preprod](#environment-variables---dev-prod-or-pre-prod) - [Usage](#usage) - [Testing](#testing) - [Sandbox and Specification](#sandbox-and-specification) @@ -82,7 +82,7 @@ The following software packages, or their equivalents, are expected to be instal | `LOG_LEVEL` | `WARNING` | Logging level. Must be one of `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL` as per [Logging Levels](https://docs.python.org/3/library/logging.html#logging-levels) | | `RULES_BUCKET_NAME` | `test-rules-bucket` | AWS S3 bucket from which to read rules. | -#### Environment variables - DEV, PROD or PRE-PROD +#### Environment variables - Dev, Prod or Preprod | Variable | Default | Description | Comments | |-------------------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 4a3c551cf..603ea003b 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -33,4 +33,3 @@ repo Preprod Dev auditable -DEV From 06b8d389f5c88fa526a91fa5ce7a9a1d0e6dac19 Mon Sep 17 00:00:00 2001 From: TOEL2 Date: Tue, 19 Aug 2025 13:16:20 +0100 Subject: [PATCH 7/7] fixing link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2a89de63..6c6e3f546 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The software will only be used for signposting an individual to an appropriate s - [Prerequisites](#prerequisites) - [Configuration](#configuration) - [Environment variables - Local](#environment-variables---local) - - [Environment variables - Dev, Prod or Preprod](#environment-variables---dev-prod-or-pre-prod) + - [Environment variables - Dev, Prod or Preprod](#environment-variables---dev-prod-or-preprod) - [Usage](#usage) - [Testing](#testing) - [Sandbox and Specification](#sandbox-and-specification)