Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 197 additions & 83 deletions .github/workflows/cicd-3-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Loading