From 4388af2d360bda5417016e9ecf1045a384cb8e09 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Mon, 13 Apr 2026 14:26:37 +0000 Subject: [PATCH 01/10] first pass --- .github/workflows/cicd-3-deploy.yaml | 280 +++++++++++++++++++-------- 1 file changed, 197 insertions(+), 83 deletions(-) diff --git a/.github/workflows/cicd-3-deploy.yaml b/.github/workflows/cicd-3-deploy.yaml index 6a46ed13d..f581ddf15 100644 --- a/.github/workflows/cicd-3-deploy.yaml +++ b/.github/workflows/cicd-3-deploy.yaml @@ -3,112 +3,226 @@ name: "2. CD - Deploy" on: workflow_dispatch: inputs: - include_prereleases: + backend_account_group: + description: "Target backend account group" type: choice - description: "Include pre-releases" - default: "true" + required: true + default: dev options: - - "true" - - "false" - version: - type: string - default: latest - description: "Install specific version" - -run-name: "Include prerelease: ${{ inputs.include_prereleases }} Version: ${{ inputs.version }} by @${{ github.actor }}" + - dev + - nonprod + - prod + backend_environment: + description: "Target backend environment" + type: string + required: true + default: main + apim_environment: + description: "Target APIM environment" + type: choice + required: true + default: internal-dev + options: + - internal-dev + - int + - prod + source_type: + description: "Deployment source type" + type: choice + required: true + default: release + options: + - release + - branch + - pr + source_value: + description: "Release tag, branch name, or PR number" + type: string + required: true + +run-name: >- + Deploy backend=${{ inputs.backend_account_group }}/${{ inputs.backend_environment }} + apim=${{ inputs.apim_environment }} + source=${{ inputs.source_type }}:${{ inputs.source_value }} by @${{ github.actor }} + permissions: - contents: read - pages: write id-token: write + contents: read + packages: read jobs: - metadata: - name: "Set CI/CD metadata" + validate: + name: Validate deployment request runs-on: ubuntu-latest - timeout-minutes: 1 + timeout-minutes: 5 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 }} + release_version: ${{ steps.validate.outputs.release_version }} + is_release: ${{ steps.validate.outputs.is_release }} + build_artifact_version: ${{ steps.validate.outputs.build_artifact_version }} + target_account_group: ${{ steps.validate.outputs.target_account_group }} + target_environment: ${{ steps.validate.outputs.target_environment }} + apim_environment: ${{ steps.validate.outputs.apim_environment }} steps: - - name: "Checkout code" - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - 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\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "python_version=$(grep "^python\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "terraform_version=$(grep "^terraform\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - 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-jekyll: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: metadata - steps: - - name: "Checkout code" - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: "Get version" - id: get-asset-version + - name: Validate inputs and resolve source + id: validate shell: bash env: GH_TOKEN: ${{ github.token }} run: | - if [[ ${{inputs.include_prereleases}} == true ]]; then - json=$(gh release list --json tagName --limit 1 --exclude-drafts) - else - json=$(gh release list --json tagName --limit 1 --exclude-drafts --exclude-pre-releases) + set -euo pipefail + + backend_account_group="${{ inputs.backend_account_group }}" + backend_environment="${{ inputs.backend_environment }}" + apim_environment="${{ inputs.apim_environment }}" + source_type="${{ inputs.source_type }}" + source_value="${{ inputs.source_value }}" + + if [[ -z "$source_value" ]]; then + echo "[ERROR] source_value cannot be empty." + exit 1 + fi + + if [[ "$backend_account_group" == "prod" && "$apim_environment" != "prod" ]]; then + echo "[ERROR] PROD backend and PROD APIM can only be deployed together." + exit 1 + fi + + if [[ "$apim_environment" == "prod" && "$backend_account_group" != "prod" ]]; then + echo "[ERROR] PROD backend and PROD APIM can only be deployed together." + exit 1 fi - echo $json + is_release="false" + release_version="$source_value" - release_version=$(echo $json | (jq -r '.[0].tagName')) - if [[ $release_version == null ]]; then exit 1; else echo $release_version; fi + if [[ "$source_type" == "release" ]]; then + if [[ ! "$source_value" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then + echo "[ERROR] Release tags must be semantic versions, for example v1.2.3." + exit 1 + fi - if [[ ${{inputs.version}} == latest ]]; then - echo release_version=$(echo $release_version) >> $GITHUB_OUTPUT + gh release view "$source_value" --repo "$GITHUB_REPOSITORY" >/dev/null + + oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" + gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets \ + --jq '.assets[].name' | grep -x "$oas_asset" >/dev/null + + is_release="true" + elif [[ "$source_type" == "branch" ]]; then + if [[ "$backend_account_group" != "dev" ]]; then + echo "[ERROR] Branch deployments are only allowed for dev backend deployments." + exit 1 + fi + + branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length') + if [[ "$branch_matches" -eq 0 ]]; then + echo "[ERROR] Branch '$source_value' not found in repository." + exit 1 + fi + elif [[ "$source_type" == "pr" ]]; then + if [[ "$backend_account_group" != "dev" ]]; then + echo "[ERROR] PR deployments are only allowed for dev backend deployments." + exit 1 + fi + + if [[ ! "$source_value" =~ ^[0-9]+$ ]]; then + echo "[ERROR] PR source_value must be a numeric PR number." + exit 1 + fi + + release_version=$(gh pr view "$source_value" --repo "$GITHUB_REPOSITORY" --json headRefName --jq '.headRefName') + if [[ -z "$release_version" || "$release_version" == "null" ]]; then + echo "[ERROR] PR #$source_value was not found." + exit 1 + fi else - echo release_version=$(echo ${{inputs.version}}) >> $GITHUB_OUTPUT + echo "[ERROR] Unsupported source type '$source_type'." + exit 1 fi - - name: "Get release version" - id: download-asset - shell: bash + if [[ "$backend_account_group" == "nonprod" || "$backend_account_group" == "prod" ]]; then + if [[ "$is_release" != "true" ]]; then + echo "[ERROR] Only tagged releases can be deployed to NONPROD and PROD backends." + exit 1 + fi + fi + + case "$backend_account_group" in + dev) + target_account_group="nhs-notify-supplier-api-dev" + ;; + nonprod) + target_account_group="nhs-notify-supplier-api-nonprod" + ;; + prod) + target_account_group="nhs-notify-supplier-api-prod" + ;; + *) + echo "[ERROR] Unsupported backend account group '$backend_account_group'." + exit 1 + ;; + esac + + build_artifact_version="" + if [[ "$is_release" != "true" ]]; then + build_artifact_version="manual" + fi + + echo "release_version=$release_version" >> "$GITHUB_OUTPUT" + echo "is_release=$is_release" >> "$GITHUB_OUTPUT" + echo "build_artifact_version=$build_artifact_version" >> "$GITHUB_OUTPUT" + echo "target_account_group=$target_account_group" >> "$GITHUB_OUTPUT" + echo "target_environment=$backend_environment" >> "$GITHUB_OUTPUT" + echo "apim_environment=$apim_environment" >> "$GITHUB_OUTPUT" + + deploy-backend: + name: Deploy backend + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: validate + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + + - name: Deploy backend environment env: - GH_TOKEN: ${{ github.token }} + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} run: | - gh release download ${{steps.get-asset-version.outputs.release_version}} -p jekyll-docs-*.tar --output artifact.tar + bash .github/scripts/dispatch_internal_repo_workflow.sh \ + --releaseVersion "${{ needs.validate.outputs.release_version }}" \ + --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \ + --targetEnvironment "${{ needs.validate.outputs.target_environment }}" \ + --targetAccountGroup "${{ needs.validate.outputs.target_account_group }}" \ + --targetComponent "api" \ + --terraformAction "apply" - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + deploy-proxy: + name: Deploy proxy + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: [validate, deploy-backend] + steps: + - name: Build OAS spec for non-release source + if: ${{ needs.validate.outputs.is_release != 'true' }} + uses: ./.github/actions/build-oas-spec with: - name: jekyll-docs-${{steps.get-asset-version.outputs.release_version}} - path: artifact.tar + version: ${{ needs.validate.outputs.build_artifact_version }} + apimEnv: ${{ needs.validate.outputs.apim_environment }} + nodejs_version: "22.22.0" + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 + - name: Deploy proxy + env: + PROXYGEN_API_NAME: nhs-notify-supplier + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + uses: ./.github/actions/build-proxies with: - artifact_name: jekyll-docs-${{steps.get-asset-version.outputs.release_version}} + targetComponent: api + environment: ${{ needs.validate.outputs.target_environment }} + apimEnv: ${{ needs.validate.outputs.apim_environment }} + runId: "${{ github.run_id }}" + releaseVersion: ${{ needs.validate.outputs.release_version }} + isRelease: ${{ needs.validate.outputs.is_release }} + version: ${{ needs.validate.outputs.build_artifact_version }} From 4da65f7edb9d1a7b3f82ea8f61f576491b1e6d55 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Mon, 20 Apr 2026 17:06:43 +0000 Subject: [PATCH 02/10] Add reusable and thin wrappers --- .github/workflows/cicd-3-deploy.yaml | 237 +++--------------- ...loy.yaml => deploy-dynamic-env-proxy.yaml} | 29 +-- .../deploy-static-env-backend-only.yaml | 46 ++++ .../deploy-static-env-proxy-only.yaml | 52 ++++ .github/workflows/deploy-supplier-api.yaml | 232 +++++++++++++++++ 5 files changed, 378 insertions(+), 218 deletions(-) rename .github/workflows/{manual-proxy-environment-deploy.yaml => deploy-dynamic-env-proxy.yaml} (73%) create mode 100644 .github/workflows/deploy-static-env-backend-only.yaml create mode 100644 .github/workflows/deploy-static-env-proxy-only.yaml create mode 100644 .github/workflows/deploy-supplier-api.yaml diff --git a/.github/workflows/cicd-3-deploy.yaml b/.github/workflows/cicd-3-deploy.yaml index f581ddf15..5ad9dec7a 100644 --- a/.github/workflows/cicd-3-deploy.yaml +++ b/.github/workflows/cicd-3-deploy.yaml @@ -3,6 +3,23 @@ name: "2. CD - Deploy" on: workflow_dispatch: inputs: + source_type: + description: "Deployment source type" + type: choice + required: true + default: release + options: + - release + - branch + source_value: + description: "Release tag or branch name" + type: string + required: true + deploy_backend: + description: "Deploy backend infrastructure" + type: boolean + required: false + default: true backend_account_group: description: "Target backend account group" type: choice @@ -12,11 +29,11 @@ on: - dev - nonprod - prod - backend_environment: - description: "Target backend environment" - type: string - required: true - default: main + deploy_proxy: + description: "Deploy APIM proxy" + type: boolean + required: false + default: true apim_environment: description: "Target APIM environment" type: choice @@ -26,24 +43,16 @@ on: - internal-dev - int - prod - source_type: - description: "Deployment source type" - type: choice - required: true - default: release - options: - - release - - branch - - pr - source_value: - description: "Release tag, branch name, or PR number" - type: string - required: true + build_sandbox: + description: "Build sandbox container" + type: boolean + required: false + default: false run-name: >- - Deploy backend=${{ inputs.backend_account_group }}/${{ inputs.backend_environment }} - apim=${{ inputs.apim_environment }} - source=${{ inputs.source_type }}:${{ inputs.source_value }} by @${{ github.actor }} + Deploy backend=${{ inputs.backend_account_group }} apim=${{ + inputs.apim_environment }} source=${{ inputs.source_type }}:${{ + inputs.source_value }} by @${{ github.actor }} permissions: id-token: write @@ -51,178 +60,14 @@ permissions: packages: read jobs: - validate: - name: Validate deployment request - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - release_version: ${{ steps.validate.outputs.release_version }} - is_release: ${{ steps.validate.outputs.is_release }} - build_artifact_version: ${{ steps.validate.outputs.build_artifact_version }} - target_account_group: ${{ steps.validate.outputs.target_account_group }} - target_environment: ${{ steps.validate.outputs.target_environment }} - apim_environment: ${{ steps.validate.outputs.apim_environment }} - steps: - - name: Validate inputs and resolve source - id: validate - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - backend_account_group="${{ inputs.backend_account_group }}" - backend_environment="${{ inputs.backend_environment }}" - apim_environment="${{ inputs.apim_environment }}" - source_type="${{ inputs.source_type }}" - source_value="${{ inputs.source_value }}" - - if [[ -z "$source_value" ]]; then - echo "[ERROR] source_value cannot be empty." - exit 1 - fi - - if [[ "$backend_account_group" == "prod" && "$apim_environment" != "prod" ]]; then - echo "[ERROR] PROD backend and PROD APIM can only be deployed together." - exit 1 - fi - - if [[ "$apim_environment" == "prod" && "$backend_account_group" != "prod" ]]; then - echo "[ERROR] PROD backend and PROD APIM can only be deployed together." - exit 1 - fi - - is_release="false" - release_version="$source_value" - - if [[ "$source_type" == "release" ]]; then - if [[ ! "$source_value" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then - echo "[ERROR] Release tags must be semantic versions, for example v1.2.3." - exit 1 - fi - - gh release view "$source_value" --repo "$GITHUB_REPOSITORY" >/dev/null - - oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" - gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets \ - --jq '.assets[].name' | grep -x "$oas_asset" >/dev/null - - is_release="true" - elif [[ "$source_type" == "branch" ]]; then - if [[ "$backend_account_group" != "dev" ]]; then - echo "[ERROR] Branch deployments are only allowed for dev backend deployments." - exit 1 - fi - - branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length') - if [[ "$branch_matches" -eq 0 ]]; then - echo "[ERROR] Branch '$source_value' not found in repository." - exit 1 - fi - elif [[ "$source_type" == "pr" ]]; then - if [[ "$backend_account_group" != "dev" ]]; then - echo "[ERROR] PR deployments are only allowed for dev backend deployments." - exit 1 - fi - - if [[ ! "$source_value" =~ ^[0-9]+$ ]]; then - echo "[ERROR] PR source_value must be a numeric PR number." - exit 1 - fi - - release_version=$(gh pr view "$source_value" --repo "$GITHUB_REPOSITORY" --json headRefName --jq '.headRefName') - if [[ -z "$release_version" || "$release_version" == "null" ]]; then - echo "[ERROR] PR #$source_value was not found." - exit 1 - fi - else - echo "[ERROR] Unsupported source type '$source_type'." - exit 1 - fi - - if [[ "$backend_account_group" == "nonprod" || "$backend_account_group" == "prod" ]]; then - if [[ "$is_release" != "true" ]]; then - echo "[ERROR] Only tagged releases can be deployed to NONPROD and PROD backends." - exit 1 - fi - fi - - case "$backend_account_group" in - dev) - target_account_group="nhs-notify-supplier-api-dev" - ;; - nonprod) - target_account_group="nhs-notify-supplier-api-nonprod" - ;; - prod) - target_account_group="nhs-notify-supplier-api-prod" - ;; - *) - echo "[ERROR] Unsupported backend account group '$backend_account_group'." - exit 1 - ;; - esac - - build_artifact_version="" - if [[ "$is_release" != "true" ]]; then - build_artifact_version="manual" - fi - - echo "release_version=$release_version" >> "$GITHUB_OUTPUT" - echo "is_release=$is_release" >> "$GITHUB_OUTPUT" - echo "build_artifact_version=$build_artifact_version" >> "$GITHUB_OUTPUT" - echo "target_account_group=$target_account_group" >> "$GITHUB_OUTPUT" - echo "target_environment=$backend_environment" >> "$GITHUB_OUTPUT" - echo "apim_environment=$apim_environment" >> "$GITHUB_OUTPUT" - - deploy-backend: - name: Deploy backend - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: validate - steps: - - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - - name: Deploy backend environment - env: - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - run: | - bash .github/scripts/dispatch_internal_repo_workflow.sh \ - --releaseVersion "${{ needs.validate.outputs.release_version }}" \ - --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \ - --targetEnvironment "${{ needs.validate.outputs.target_environment }}" \ - --targetAccountGroup "${{ needs.validate.outputs.target_account_group }}" \ - --targetComponent "api" \ - --terraformAction "apply" - - deploy-proxy: - name: Deploy proxy - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: [validate, deploy-backend] - steps: - - name: Build OAS spec for non-release source - if: ${{ needs.validate.outputs.is_release != 'true' }} - uses: ./.github/actions/build-oas-spec - with: - version: ${{ needs.validate.outputs.build_artifact_version }} - apimEnv: ${{ needs.validate.outputs.apim_environment }} - nodejs_version: "22.22.0" - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Deploy proxy - env: - PROXYGEN_API_NAME: nhs-notify-supplier - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - uses: ./.github/actions/build-proxies - with: - targetComponent: api - environment: ${{ needs.validate.outputs.target_environment }} - apimEnv: ${{ needs.validate.outputs.apim_environment }} - runId: "${{ github.run_id }}" - releaseVersion: ${{ needs.validate.outputs.release_version }} - isRelease: ${{ needs.validate.outputs.is_release }} - version: ${{ needs.validate.outputs.build_artifact_version }} + deploy: + uses: ./.github/workflows/deploy-supplier-api.yaml + secrets: inherit + with: + backend_account_group: ${{ inputs.backend_account_group }} + apim_environment: ${{ inputs.apim_environment }} + source_type: ${{ inputs.source_type }} + source_value: ${{ inputs.source_value }} + deploy_backend: ${{ inputs.deploy_backend }} + deploy_proxy: ${{ inputs.deploy_proxy }} + build_sandbox: ${{ inputs.build_sandbox }} diff --git a/.github/workflows/manual-proxy-environment-deploy.yaml b/.github/workflows/deploy-dynamic-env-proxy.yaml similarity index 73% rename from .github/workflows/manual-proxy-environment-deploy.yaml rename to .github/workflows/deploy-dynamic-env-proxy.yaml index 9516f8a1d..c44c57b6c 100644 --- a/.github/workflows/manual-proxy-environment-deploy.yaml +++ b/.github/workflows/deploy-dynamic-env-proxy.yaml @@ -1,18 +1,9 @@ name: Deploy proxy to environment -run-name: Proxygen Deployment for ${{ inputs.proxy_environment }} +run-name: Proxygen Deployment for internal-dev on: workflow_dispatch: inputs: - proxy_environment: - description: Name of the proxygen environment to deploy to - required: true - type: choice - default: internal-dev - options: - - internal-dev - - int - - prod build_sandbox: description: Build sandbox container? required: false @@ -40,7 +31,8 @@ jobs: node-version: 22 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: "Check if pull request exists for this branch and set ENVIRONMENT/APIM_ENV" + - name: "Check if pull request exists for this branch and set + ENVIRONMENT/APIM_ENV" id: pr_exists env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -48,11 +40,7 @@ jobs: branch_name=${GITHUB_HEAD_REF:-$(echo $GITHUB_REF | sed 's#refs/heads/##')} echo "Current branch is '$branch_name'" - if [ -z "${{ inputs.proxy_environment }}" ]; then - ENVIRONMENT="internal-dev" - else - ENVIRONMENT="${{ inputs.proxy_environment }}" - fi + ENVIRONMENT="internal-dev" pr_json=$(gh pr list --head "$branch_name" --state open --json number --limit 1) pr_number=$(echo "$pr_json" | jq -r '.[0].number // empty') @@ -62,14 +50,11 @@ jobs: echo "does_pull_request_exist=true" >> $GITHUB_OUTPUT echo "pr_number=$pr_number" >> $GITHUB_OUTPUT APIM_ENV="$ENVIRONMENT-pr" - echo "changing environment variable so that PR number is used in proxy pipeline for setting env vars" + # changing environment variable so that PR number is used in proxy pipeline for setting env vars ENVIRONMENT="pr$pr_number" else - echo "Pull request doesn't exist, setting target env to main" - echo "does_pull_request_exist=false" >> $GITHUB_OUTPUT - echo "pr_number=" >> $GITHUB_OUTPUT - APIM_ENV="$ENVIRONMENT" - ENVIRONMENT="main" + echo "[ERROR] Pull request $pr_number doesn't exist." + exit 1 fi echo "ENVIRONMENT=$ENVIRONMENT" >> $GITHUB_ENV diff --git a/.github/workflows/deploy-static-env-backend-only.yaml b/.github/workflows/deploy-static-env-backend-only.yaml new file mode 100644 index 000000000..b7e33de24 --- /dev/null +++ b/.github/workflows/deploy-static-env-backend-only.yaml @@ -0,0 +1,46 @@ +name: Deploy backend only + +on: + workflow_dispatch: + inputs: + backend_account_group: + description: "Target backend account group" + type: choice + required: true + default: dev + options: + - dev + - nonprod + - prod + source_type: + description: "Deployment source type" + type: choice + required: true + default: release + options: + - release + - branch + source_value: + description: "Release tag or branch name" + type: string + required: true + +run-name: >- + Deploy backend=${{ inputs.backend_account_group }} source=${{ + inputs.source_type }}:${{ inputs.source_value }} by @${{ github.actor }} + +permissions: + id-token: write + contents: read + packages: read + +jobs: + deploy: + uses: ./.github/workflows/deploy-supplier-api.yaml + secrets: inherit + with: + backend_account_group: ${{ inputs.backend_account_group }} + source_type: ${{ inputs.source_type }} + source_value: ${{ inputs.source_value }} + deploy_backend: true + deploy_proxy: false diff --git a/.github/workflows/deploy-static-env-proxy-only.yaml b/.github/workflows/deploy-static-env-proxy-only.yaml new file mode 100644 index 000000000..26762ad10 --- /dev/null +++ b/.github/workflows/deploy-static-env-proxy-only.yaml @@ -0,0 +1,52 @@ +name: Deploy proxy only + +on: + workflow_dispatch: + inputs: + apim_environment: + description: "Target APIM environment" + type: choice + required: true + default: internal-dev + options: + - internal-dev + - int + - prod + source_type: + description: "Deployment source type" + type: choice + required: true + default: release + options: + - release + - branch + source_value: + description: "Release tag or branch name" + type: string + required: true + build_sandbox: + description: "Build sandbox container" + type: boolean + required: false + default: false + +run-name: >- + Deploy proxy apim=${{ inputs.apim_environment }} source=${{ inputs.source_type + }}:${{ inputs.source_value }} by @${{ github.actor }} + +permissions: + id-token: write + contents: read + packages: read + +jobs: + deploy: + uses: ./.github/workflows/deploy-supplier-api.yaml + secrets: inherit + with: + apim_environment: ${{ inputs.apim_environment }} + source_type: ${{ inputs.source_type }} + source_value: ${{ inputs.source_value }} + deploy_backend: false + deploy_proxy: true + build_sandbox: ${{ inputs.build_sandbox }} diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml new file mode 100644 index 000000000..791bf8bc5 --- /dev/null +++ b/.github/workflows/deploy-supplier-api.yaml @@ -0,0 +1,232 @@ +name: Deploy Supplier API + +on: + workflow_call: + inputs: + source_type: + description: "Deployment source type (release, branch)" + type: string + required: true + source_value: + description: "Release tag or branch name" + type: string + required: true + deploy_backend: + description: "Deploy backend infrastructure" + type: boolean + required: false + default: true + backend_account_group: + description: "Target backend account group (dev, nonprod, prod). Required when + deploy_backend is true." + type: string + required: false + default: "" + deploy_proxy: + description: "Deploy APIM proxy" + type: boolean + required: false + default: true + apim_environment: + description: "Target APIM environment (internal-dev, int, prod). Required when + deploy_proxy is true." + type: string + required: false + default: "" + build_sandbox: + description: "Build sandbox container" + type: boolean + required: false + default: false + +permissions: + id-token: write + contents: read + packages: read + +jobs: + validate: + name: Validate deployment request + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + release_version: ${{ steps.validate.outputs.release_version }} + is_release: ${{ steps.validate.outputs.is_release }} + build_artifact_version: ${{ steps.validate.outputs.build_artifact_version }} + target_account_group: ${{ steps.validate.outputs.target_account_group }} + target_environment: ${{ steps.validate.outputs.target_environment }} + apim_environment: ${{ steps.validate.outputs.apim_environment }} + steps: + - name: Validate inputs and resolve source + id: validate + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + backend_account_group="${{ inputs.backend_account_group }}" + apim_environment="${{ inputs.apim_environment }}" + source_type="${{ inputs.source_type }}" + source_value="${{ inputs.source_value }}" + deploy_backend="${{ inputs.deploy_backend }}" + deploy_proxy="${{ inputs.deploy_proxy }}" + + # --- Shared source validation --- + + if [[ -z "$source_value" ]]; then + echo "[ERROR] source_value cannot be empty." + exit 1 + fi + + is_release="false" + release_version="$source_value" + + if [[ "$source_type" == "release" ]]; then + if [[ ! "$source_value" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then + echo "[ERROR] Release tags must be semantic versions, for example v1.2.3." + exit 1 + fi + gh release view "$source_value" --repo "$GITHUB_REPOSITORY" >/dev/null + is_release="true" + elif [[ "$source_type" == "branch" ]]; then + branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length') + if [[ "$branch_matches" -eq 0 ]]; then + echo "[ERROR] Branch '$source_value' not found in repository." + exit 1 + fi + else + echo "[ERROR] Unsupported source type '$source_type'. Use 'release' or 'branch'." + exit 1 + fi + + # --- Backend validation (only when deploying backend) --- + + target_account_group="" + + if [[ "$deploy_backend" == "true" ]]; then + if [[ -z "$backend_account_group" ]]; then + echo "[ERROR] backend_account_group is required when deploy_backend is true." + exit 1 + fi + + if [[ "$source_type" == "branch" && "$backend_account_group" != "dev" ]]; then + echo "[ERROR] Branch deployments are only allowed for dev backend deployments." + exit 1 + fi + + if [[ "$backend_account_group" == "nonprod" || "$backend_account_group" == "prod" ]]; then + if [[ "$is_release" == "false" ]]; then + echo "[ERROR] Only tagged releases can be deployed to NONPROD and PROD backends." + exit 1 + fi + fi + + case "$backend_account_group" in + dev) target_account_group="nhs-notify-supplier-api-dev" ;; + nonprod) target_account_group="nhs-notify-supplier-api-nonprod" ;; + prod) target_account_group="nhs-notify-supplier-api-prod" ;; + *) + echo "[ERROR] Unsupported backend account group '$backend_account_group'." + exit 1 + ;; + esac + fi + + # --- Proxy validation (only when deploying proxy) --- + + if [[ "$deploy_proxy" == "true" ]]; then + if [[ -z "$apim_environment" ]]; then + echo "[ERROR] apim_environment is required when deploy_proxy is true." + exit 1 + fi + + if [[ "$is_release" == "true" ]]; then + oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" + gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets \ + --jq '.assets[].name' | grep -x "$oas_asset" >/dev/null + fi + fi + + # --- Cross-validation (only when deploying both) --- + + if [[ "$deploy_backend" == "true" && "$deploy_proxy" == "true" ]]; then + if [[ ("$backend_account_group" == "prod") != ("$apim_environment" == "prod") ]]; then + echo "[ERROR] PROD backend and PROD APIM can only be deployed together." + exit 1 + fi + fi + + build_artifact_version="" + if [[ "$is_release" == "false" ]]; then + build_artifact_version="nonrelease-$source_type-$source_value" + fi + + # --- Outputs --- + + echo "release_version=$release_version" >> "$GITHUB_OUTPUT" + echo "is_release=$is_release" >> "$GITHUB_OUTPUT" + echo "build_artifact_version=$build_artifact_version" >> "$GITHUB_OUTPUT" + echo "target_account_group=$target_account_group" >> "$GITHUB_OUTPUT" + # For static envs we want to deploy to main + echo "target_environment=main" >> "$GITHUB_OUTPUT" + echo "apim_environment=$apim_environment" >> "$GITHUB_OUTPUT" + + deploy-backend: + name: Deploy backend + if: ${{ inputs.deploy_backend }} + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: validate + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + + - name: Deploy backend environment + env: + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + run: | + bash .github/scripts/dispatch_internal_repo_workflow.sh \ + --releaseVersion "${{ needs.validate.outputs.release_version }}" \ + --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \ + --targetEnvironment "${{ needs.validate.outputs.target_environment }}" \ + --targetAccountGroup "${{ needs.validate.outputs.target_account_group }}" \ + --targetComponent "api" \ + --terraformAction "apply" + + deploy-proxy: + name: Deploy proxy + if: ${{ inputs.deploy_proxy && !failure() && !cancelled() }} + runs-on: ubuntu-latest + timeout-minutes: 30 + needs: [ validate, deploy-backend ] + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + + - name: Build OAS spec for non-release source + if: ${{ needs.validate.outputs.is_release == 'false' }} + uses: ./.github/actions/build-oas-spec + with: + version: ${{ needs.validate.outputs.build_artifact_version }} + apimEnv: ${{ needs.validate.outputs.apim_environment }} + buildSandbox: ${{ inputs.build_sandbox }} + nodejs_version: "22.22.0" + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Deploy proxy + env: + PROXYGEN_API_NAME: nhs-notify-supplier + APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} + APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} + uses: ./.github/actions/build-proxies + with: + targetComponent: api + environment: ${{ needs.validate.outputs.target_environment }} + apimEnv: ${{ needs.validate.outputs.apim_environment }} + runId: "${{ github.run_id }}" + releaseVersion: ${{ needs.validate.outputs.release_version }} + isRelease: ${{ needs.validate.outputs.is_release }} + buildSandbox: ${{ inputs.build_sandbox }} + version: ${{ needs.validate.outputs.build_artifact_version }} From e748c545088e7112e60b28a249c98bc1f6974000 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 21 Apr 2026 10:25:23 +0000 Subject: [PATCH 03/10] minor improvements to deploy --- .github/workflows/deploy-supplier-api.yaml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index 791bf8bc5..f63ac675a 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -74,6 +74,11 @@ jobs: # --- Shared source validation --- + if [[ "$deploy_backend" != "true" && "$deploy_proxy" != "true" ]]; then + echo "[ERROR] At least one of deploy_backend or deploy_proxy must be true." + exit 1 + fi + if [[ -z "$source_value" ]]; then echo "[ERROR] source_value cannot be empty." exit 1 @@ -87,7 +92,7 @@ jobs: echo "[ERROR] Release tags must be semantic versions, for example v1.2.3." exit 1 fi - gh release view "$source_value" --repo "$GITHUB_REPOSITORY" >/dev/null + release_json=$(gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets) is_release="true" elif [[ "$source_type" == "branch" ]]; then branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length') @@ -143,8 +148,7 @@ jobs: if [[ "$is_release" == "true" ]]; then oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" - gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets \ - --jq '.assets[].name' | grep -x "$oas_asset" >/dev/null + echo "$release_json" | jq -r '.assets[].name' | grep -x "$oas_asset" >/dev/null fi fi @@ -180,7 +184,7 @@ jobs: needs: validate steps: - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Deploy backend environment env: @@ -203,7 +207,12 @@ jobs: needs: [ validate, deploy-backend ] steps: - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Resolve nodejs version + id: toolversions + run: echo "nodejs_version=$(grep '^nodejs\s' .tool-versions | cut -f2 -d' ')" >> + "$GITHUB_OUTPUT" - name: Build OAS spec for non-release source if: ${{ needs.validate.outputs.is_release == 'false' }} @@ -212,7 +221,7 @@ jobs: version: ${{ needs.validate.outputs.build_artifact_version }} apimEnv: ${{ needs.validate.outputs.apim_environment }} buildSandbox: ${{ inputs.build_sandbox }} - nodejs_version: "22.22.0" + nodejs_version: ${{ steps.toolversions.outputs.nodejs_version }} NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Deploy proxy From 63e104aa25c38eb5aea6a89e81b7f81add5e87ec Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 21 Apr 2026 10:45:33 +0000 Subject: [PATCH 04/10] refactoring other workflows to use reusable deploy --- .../workflows/deploy-dynamic-env-proxy.yaml | 27 ++++--- .github/workflows/deploy-supplier-api.yaml | 1 - .github/workflows/pr_closed.yaml | 44 ++++------- .github/workflows/release_created.yaml | 74 ++++--------------- 4 files changed, 45 insertions(+), 101 deletions(-) diff --git a/.github/workflows/deploy-dynamic-env-proxy.yaml b/.github/workflows/deploy-dynamic-env-proxy.yaml index c44c57b6c..5c4607ae0 100644 --- a/.github/workflows/deploy-dynamic-env-proxy.yaml +++ b/.github/workflows/deploy-dynamic-env-proxy.yaml @@ -1,5 +1,5 @@ -name: Deploy proxy to environment -run-name: Proxygen Deployment for internal-dev +name: Deploy dynamic PR environment proxy +run-name: Deploy proxy for PR environment on internal-dev by @${{ github.actor }} on: workflow_dispatch: @@ -15,22 +15,23 @@ permissions: packages: read jobs: - deploy-environment: + deploy-pr-proxy: runs-on: ubuntu-latest - name: Deploy to Environment + name: Deploy proxy to dynamic PR environment steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 - with: - node-version: 22 - - name: Npm install + - name: Install dependencies uses: ./.github/actions/node-install with: - node-version: 22 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Resolve nodejs version + id: toolversions + run: echo "nodejs_version=$(grep '^nodejs\s' .tool-versions | cut -f2 -d' ')" >> + "$GITHUB_OUTPUT" + - name: "Check if pull request exists for this branch and set ENVIRONMENT/APIM_ENV" id: pr_exists @@ -60,14 +61,16 @@ jobs: echo "ENVIRONMENT=$ENVIRONMENT" >> $GITHUB_ENV echo "APIM_ENV=$APIM_ENV" >> $GITHUB_ENV - - name: "Build OAS spec" + - name: Build OAS spec uses: ./.github/actions/build-oas-spec with: + version: "pr${{ steps.pr_exists.outputs.pr_number }}" apimEnv: "${{ env.APIM_ENV }}" buildSandbox: ${{ inputs.build_sandbox }} + nodejs_version: ${{ steps.toolversions.outputs.nodejs_version }} NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: "Build proxies" + - name: Deploy proxy env: PROXYGEN_API_NAME: nhs-notify-supplier PR_NUMBER: ${{ steps.pr_exists.outputs.pr_number }} @@ -75,8 +78,10 @@ jobs: APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} uses: ./.github/actions/build-proxies with: + targetComponent: api environment: "${{ env.ENVIRONMENT }}" apimEnv: "${{ env.APIM_ENV }}" runId: "${{ github.run_id }}" buildSandbox: ${{ inputs.build_sandbox }} releaseVersion: ${{ github.ref_name }} + version: "pr${{ steps.pr_exists.outputs.pr_number }}" diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index f63ac675a..2cfe4b907 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -231,7 +231,6 @@ jobs: APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} uses: ./.github/actions/build-proxies with: - targetComponent: api environment: ${{ needs.validate.outputs.target_environment }} apimEnv: ${{ needs.validate.outputs.apim_environment }} runId: "${{ github.run_id }}" diff --git a/.github/workflows/pr_closed.yaml b/.github/workflows/pr_closed.yaml index a27a896b9..090e9fd99 100644 --- a/.github/workflows/pr_closed.yaml +++ b/.github/workflows/pr_closed.yaml @@ -3,7 +3,7 @@ name: PR Closed on: workflow_dispatch: pull_request: - types: [closed] + types: [ closed ] branches: - main @@ -34,33 +34,15 @@ jobs: deploy-main: needs: check-merge-or-workflow-dispatch name: Deploy changes to main in dev AWS account - runs-on: ubuntu-latest if: needs.check-merge-or-workflow-dispatch.outputs.deploy == 'true' - - permissions: - id-token: write - contents: read - - strategy: - max-parallel: 1 - matrix: - component: [api] - - steps: - - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: Updating Main Environment - env: - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - run: | - bash .github/scripts/dispatch_internal_repo_workflow.sh \ - --releaseVersion "main" \ - --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \ - --targetEnvironment "main" \ - --targetAccountGroup "nhs-notify-supplier-api-dev" \ - --targetComponent "${{ matrix.component }}" \ - --terraformAction "apply" + uses: ./.github/workflows/deploy-supplier-api.yaml + secrets: inherit + with: + source_type: branch + source_value: main + deploy_backend: true + backend_account_group: dev + deploy_proxy: false check-event-schemas-version-change: name: Check for event schemas package version change @@ -78,8 +60,8 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version-file: '.tool-versions' - registry-url: 'https://npm.pkg.github.com' + node-version-file: ".tool-versions" + registry-url: "https://npm.pkg.github.com" - name: check if local version differs from latest published version id: check-version @@ -137,8 +119,8 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version-file: '.tool-versions' - registry-url: 'https://npm.pkg.github.com' + node-version-file: ".tool-versions" + registry-url: "https://npm.pkg.github.com" - name: Install dependencies env: diff --git a/.github/workflows/release_created.yaml b/.github/workflows/release_created.yaml index 47d9c855a..26301130c 100644 --- a/.github/workflows/release_created.yaml +++ b/.github/workflows/release_created.yaml @@ -2,67 +2,25 @@ name: Github Release Created on: release: - types: ["released"] # Inherits all input defaults - workflow_dispatch: - inputs: - releaseVersion: - description: Release, tag, branch, or commit ID to be used for deployment - required: false - default: "main" - type: string + types: [ "released" ] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false -jobs: - deploy-main: - name: Deploy changes to main in nonprod AWS Account - runs-on: ubuntu-latest - - permissions: - id-token: write - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: Deploy Nonprod Environment - env: - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - run: | - bash .github/scripts/dispatch_internal_repo_workflow.sh \ - --releaseVersion "${{ github.event.release.tag_name || inputs.releaseVersion }}" \ - --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \ - --targetEnvironment "main" \ - --targetAccountGroup "nhs-notify-supplier-api-nonprod" \ - --targetComponent "api" \ - --terraformAction "apply" - deploy-proxy: - needs: deploy-main #wait for backend deploy to complete - name: "Deploy proxy" - runs-on: ubuntu-latest - timeout-minutes: 10 +permissions: + id-token: write + contents: read + packages: read - permissions: - id-token: write - contents: read - actions: read - - env: - PROXYGEN_API_NAME: nhs-notify-supplier - APP_CLIENT_ID: ${{ secrets.APP_CLIENT_ID }} - APP_PEM_FILE: ${{ secrets.APP_PEM_FILE }} - - steps: - - name: "Checkout code" - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: "Build proxies" - uses: ./.github/actions/build-proxies - with: - environment: "main" - apimEnv: "int" - runId: "${{ github.run_id }}" - releaseVersion: "${{ github.event.release.tag_name || inputs.releaseVersion }}" - isRelease: true +jobs: + deploy: + uses: ./.github/workflows/deploy-supplier-api.yaml + secrets: inherit + with: + source_type: release + source_value: ${{ github.event.release.tag_name }} + deploy_backend: true + backend_account_group: nonprod + deploy_proxy: true + apim_environment: int From b8582e38745f5e594673d4a17a0bb8d55ded4d34 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 21 Apr 2026 11:02:22 +0000 Subject: [PATCH 05/10] fix validation --- .github/workflows/deploy-supplier-api.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index 2cfe4b907..011bdbfc8 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -155,10 +155,14 @@ jobs: # --- Cross-validation (only when deploying both) --- if [[ "$deploy_backend" == "true" && "$deploy_proxy" == "true" ]]; then - if [[ ("$backend_account_group" == "prod") != ("$apim_environment" == "prod") ]]; then - echo "[ERROR] PROD backend and PROD APIM can only be deployed together." - exit 1 - fi + case "${backend_account_group}:${apim_environment}" in + dev:internal-dev|nonprod:int|prod:prod) ;; + *) + echo "[ERROR] Mismatched backend/APIM pair: '${backend_account_group}' and '${apim_environment}'." + echo "[ERROR] Valid combinations: dev/internal-dev, nonprod/int, prod/prod." + exit 1 + ;; + esac fi build_artifact_version="" From 012245482d7aba8f422078f96854c6dce1346f2d Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Tue, 21 Apr 2026 11:48:31 +0000 Subject: [PATCH 06/10] fix validation and order --- .github/workflows/deploy-supplier-api.yaml | 45 ++++++++++++++-------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index 011bdbfc8..f421f0054 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -92,10 +92,18 @@ jobs: echo "[ERROR] Release tags must be semantic versions, for example v1.2.3." exit 1 fi - release_json=$(gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets) + if ! release_json=$(gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets 2>&1); then + echo "[ERROR] Release '$source_value' not found in repository." + echo "$release_json" + exit 1 + fi is_release="true" elif [[ "$source_type" == "branch" ]]; then - branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length') + if ! branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length' 2>&1); then + echo "[ERROR] Failed to check branch '$source_value'." + echo "$branch_matches" + exit 1 + fi if [[ "$branch_matches" -eq 0 ]]; then echo "[ERROR] Branch '$source_value' not found in repository." exit 1 @@ -105,6 +113,19 @@ jobs: exit 1 fi + # --- Cross-validation (only when deploying both) --- + + if [[ "$deploy_backend" == "true" && "$deploy_proxy" == "true" ]]; then + case "${backend_account_group}:${apim_environment}" in + dev:internal-dev|nonprod:int|prod:prod) ;; + *) + echo "[ERROR] Mismatched backend/APIM pair: '${backend_account_group}' and '${apim_environment}'." + echo "[ERROR] Valid combinations: dev/internal-dev, nonprod/int, prod/prod." + exit 1 + ;; + esac + fi + # --- Backend validation (only when deploying backend) --- target_account_group="" @@ -148,21 +169,13 @@ jobs: if [[ "$is_release" == "true" ]]; then oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" - echo "$release_json" | jq -r '.assets[].name' | grep -x "$oas_asset" >/dev/null - fi - fi - - # --- Cross-validation (only when deploying both) --- - - if [[ "$deploy_backend" == "true" && "$deploy_proxy" == "true" ]]; then - case "${backend_account_group}:${apim_environment}" in - dev:internal-dev|nonprod:int|prod:prod) ;; - *) - echo "[ERROR] Mismatched backend/APIM pair: '${backend_account_group}' and '${apim_environment}'." - echo "[ERROR] Valid combinations: dev/internal-dev, nonprod/int, prod/prod." + if ! echo "$release_json" | jq -r '.assets[].name' | grep -qx "$oas_asset"; then + echo "[ERROR] Release '$source_value' does not contain required OAS asset '$oas_asset'." + echo "Available assets:" + echo "$release_json" | jq -r '.assets[].name' exit 1 - ;; - esac + fi + fi fi build_artifact_version="" From 2341b8705b950cd2ac33452651e2f49878bc9773 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 22 Apr 2026 15:00:29 +0000 Subject: [PATCH 07/10] deploy defaults to false --- .github/workflows/deploy-supplier-api.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index f421f0054..913a5e016 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -15,7 +15,7 @@ on: description: "Deploy backend infrastructure" type: boolean required: false - default: true + default: false backend_account_group: description: "Target backend account group (dev, nonprod, prod). Required when deploy_backend is true." @@ -26,7 +26,7 @@ on: description: "Deploy APIM proxy" type: boolean required: false - default: true + default: false apim_environment: description: "Target APIM environment (internal-dev, int, prod). Required when deploy_proxy is true." From 4b9857b70c96150dcd91a3198a2f31173c784f00 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 22 Apr 2026 15:30:07 +0000 Subject: [PATCH 08/10] cicd 3 deploy run name --- .github/workflows/cicd-3-deploy.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd-3-deploy.yaml b/.github/workflows/cicd-3-deploy.yaml index 5ad9dec7a..b0385ad8d 100644 --- a/.github/workflows/cicd-3-deploy.yaml +++ b/.github/workflows/cicd-3-deploy.yaml @@ -50,9 +50,10 @@ on: default: false run-name: >- - Deploy backend=${{ inputs.backend_account_group }} apim=${{ - inputs.apim_environment }} source=${{ inputs.source_type }}:${{ - inputs.source_value }} by @${{ github.actor }} + Deploy ${{ inputs.deploy_backend && format('backend={0}', + inputs.backend_account_group) || '' }} ${{ inputs.deploy_proxy && + format('apim={0}', inputs.apim_environment) || '' }} source=${{ + inputs.source_type }}:${{ inputs.source_value }} by @${{ github.actor }} permissions: id-token: write From 6c935b3c4dc66a049960aa7af0ce287518af4786 Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 22 Apr 2026 16:33:30 +0000 Subject: [PATCH 09/10] fix oas asset lookup --- .github/workflows/deploy-supplier-api.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-supplier-api.yaml b/.github/workflows/deploy-supplier-api.yaml index 913a5e016..c7d96d9eb 100644 --- a/.github/workflows/deploy-supplier-api.yaml +++ b/.github/workflows/deploy-supplier-api.yaml @@ -168,9 +168,9 @@ jobs: fi if [[ "$is_release" == "true" ]]; then - oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip" - if ! echo "$release_json" | jq -r '.assets[].name' | grep -qx "$oas_asset"; then - echo "[ERROR] Release '$source_value' does not contain required OAS asset '$oas_asset'." + oas_asset_pattern="^api-oas-specification-${apim_environment}-${source_value}-.*\.zip$" + if ! echo "$release_json" | jq -r '.assets[].name' | grep -qx "$oas_asset_pattern"; then + echo "[ERROR] Release '$source_value' does not contain required OAS asset matching pattern '$oas_asset_pattern'." echo "Available assets:" echo "$release_json" | jq -r '.assets[].name' exit 1 From 37e3389d7ef6b3bf65040c553e732c4c552ad65c Mon Sep 17 00:00:00 2001 From: Francisco Videira Date: Wed, 22 Apr 2026 16:44:07 +0000 Subject: [PATCH 10/10] change defaults --- .github/workflows/cicd-3-deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd-3-deploy.yaml b/.github/workflows/cicd-3-deploy.yaml index b0385ad8d..d3754c2ba 100644 --- a/.github/workflows/cicd-3-deploy.yaml +++ b/.github/workflows/cicd-3-deploy.yaml @@ -19,7 +19,7 @@ on: description: "Deploy backend infrastructure" type: boolean required: false - default: true + default: false backend_account_group: description: "Target backend account group" type: choice @@ -33,7 +33,7 @@ on: description: "Deploy APIM proxy" type: boolean required: false - default: true + default: false apim_environment: description: "Target APIM environment" type: choice