@@ -3,112 +3,226 @@ name: "2. CD - Deploy"
33on :
44 workflow_dispatch :
55 inputs :
6- include_prereleases :
6+ backend_account_group :
7+ description : " Target backend account group"
78 type : choice
8- description : " Include pre-releases "
9- default : " true "
9+ required : true
10+ default : dev
1011 options :
11- - " true"
12- - " false"
13- version :
14- type : string
15- default : latest
16- description : " Install specific version"
17-
18- run-name : " Include prerelease: ${{ inputs.include_prereleases }} Version: ${{ inputs.version }} by @${{ github.actor }}"
12+ - dev
13+ - nonprod
14+ - prod
15+ backend_environment :
16+ description : " Target backend environment"
17+ type : string
18+ required : true
19+ default : main
20+ apim_environment :
21+ description : " Target APIM environment"
22+ type : choice
23+ required : true
24+ default : internal-dev
25+ options :
26+ - internal-dev
27+ - int
28+ - prod
29+ source_type :
30+ description : " Deployment source type"
31+ type : choice
32+ required : true
33+ default : release
34+ options :
35+ - release
36+ - branch
37+ - pr
38+ source_value :
39+ description : " Release tag, branch name, or PR number"
40+ type : string
41+ required : true
42+
43+ run-name : >-
44+ Deploy backend=${{ inputs.backend_account_group }}/${{ inputs.backend_environment }}
45+ apim=${{ inputs.apim_environment }}
46+ source=${{ inputs.source_type }}:${{ inputs.source_value }} by @${{ github.actor }}
47+
1948permissions :
20- contents : read
21- pages : write
2249 id-token : write
50+ contents : read
51+ packages : read
2352
2453jobs :
25- metadata :
26- name : " Set CI/CD metadata "
54+ validate :
55+ name : Validate deployment request
2756 runs-on : ubuntu-latest
28- timeout-minutes : 1
57+ timeout-minutes : 5
2958 outputs :
30- build_datetime : ${{ steps.variables.outputs.build_datetime }}
31- build_timestamp : ${{ steps.variables.outputs.build_timestamp }}
32- build_epoch : ${{ steps.variables.outputs.build_epoch }}
33- nodejs_version : ${{ steps.variables.outputs.nodejs_version }}
34- python_version : ${{ steps.variables.outputs.python_version }}
35- terraform_version : ${{ steps.variables.outputs.terraform_version }}
36- version : ${{ steps.variables.outputs.version }}
37- # tag: ${{ steps.variables.outputs.tag }}
59+ release_version : ${{ steps.validate.outputs.release_version }}
60+ is_release : ${{ steps.validate.outputs.is_release }}
61+ build_artifact_version : ${{ steps.validate.outputs.build_artifact_version }}
62+ target_account_group : ${{ steps.validate.outputs.target_account_group }}
63+ target_environment : ${{ steps.validate.outputs.target_environment }}
64+ apim_environment : ${{ steps.validate.outputs.apim_environment }}
3865 steps :
39- - name : " Checkout code"
40- uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
41- - name : " Set CI/CD variables"
42- id : variables
43- run : |
44- datetime=$(date -u +'%Y-%m-%dT%H:%M:%S%z')
45- echo "build_datetime=$datetime" >> $GITHUB_OUTPUT
46- echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
47- echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT
48- echo "nodejs_version=$(grep "^nodejs\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
49- echo "python_version=$(grep "^python\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
50- echo "terraform_version=$(grep "^terraform\s" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
51- echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT
52- # echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
53- - name : " List variables"
54- run : |
55- export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}"
56- export BUILD_TIMESTAMP="${{ steps.variables.outputs.build_timestamp }}"
57- export BUILD_EPOCH="${{ steps.variables.outputs.build_epoch }}"
58- export NODEJS_VERSION="${{ steps.variables.outputs.nodejs_version }}"
59- export PYTHON_VERSION="${{ steps.variables.outputs.python_version }}"
60- export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}"
61- export VERSION="${{ steps.variables.outputs.version }}"
62- # export TAG="${{ steps.variables.outputs.tag }}"
63- make list-variables
64-
65- deploy-jekyll :
66- environment :
67- name : github-pages
68- url : ${{ steps.deployment.outputs.page_url }}
69- runs-on : ubuntu-latest
70- needs : metadata
71- steps :
72- - name : " Checkout code"
73- uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
74- - name : " Get version"
75- id : get-asset-version
66+ - name : Validate inputs and resolve source
67+ id : validate
7668 shell : bash
7769 env :
7870 GH_TOKEN : ${{ github.token }}
7971 run : |
80- if [[ ${{inputs.include_prereleases}} == true ]]; then
81- json=$(gh release list --json tagName --limit 1 --exclude-drafts)
82- else
83- json=$(gh release list --json tagName --limit 1 --exclude-drafts --exclude-pre-releases)
72+ set -euo pipefail
73+
74+ backend_account_group="${{ inputs.backend_account_group }}"
75+ backend_environment="${{ inputs.backend_environment }}"
76+ apim_environment="${{ inputs.apim_environment }}"
77+ source_type="${{ inputs.source_type }}"
78+ source_value="${{ inputs.source_value }}"
79+
80+ if [[ -z "$source_value" ]]; then
81+ echo "[ERROR] source_value cannot be empty."
82+ exit 1
83+ fi
84+
85+ if [[ "$backend_account_group" == "prod" && "$apim_environment" != "prod" ]]; then
86+ echo "[ERROR] PROD backend and PROD APIM can only be deployed together."
87+ exit 1
88+ fi
89+
90+ if [[ "$apim_environment" == "prod" && "$backend_account_group" != "prod" ]]; then
91+ echo "[ERROR] PROD backend and PROD APIM can only be deployed together."
92+ exit 1
8493 fi
8594
86- echo $json
95+ is_release="false"
96+ release_version="$source_value"
8797
88- release_version=$(echo $json | (jq -r '.[0].tagName'))
89- if [[ $release_version == null ]]; then exit 1; else echo $release_version; fi
98+ if [[ "$source_type" == "release" ]]; then
99+ if [[ ! "$source_value" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.+][0-9A-Za-z.-]+)?$ ]]; then
100+ echo "[ERROR] Release tags must be semantic versions, for example v1.2.3."
101+ exit 1
102+ fi
90103
91- if [[ ${{inputs.version}} == latest ]]; then
92- echo release_version=$(echo $release_version) >> $GITHUB_OUTPUT
104+ gh release view "$source_value" --repo "$GITHUB_REPOSITORY" >/dev/null
105+
106+ oas_asset="api-oas-specification-${apim_environment}-${source_value}.zip"
107+ gh release view "$source_value" --repo "$GITHUB_REPOSITORY" --json assets \
108+ --jq '.assets[].name' | grep -x "$oas_asset" >/dev/null
109+
110+ is_release="true"
111+ elif [[ "$source_type" == "branch" ]]; then
112+ if [[ "$backend_account_group" != "dev" ]]; then
113+ echo "[ERROR] Branch deployments are only allowed for dev backend deployments."
114+ exit 1
115+ fi
116+
117+ branch_matches=$(gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${source_value}" --jq 'length')
118+ if [[ "$branch_matches" -eq 0 ]]; then
119+ echo "[ERROR] Branch '$source_value' not found in repository."
120+ exit 1
121+ fi
122+ elif [[ "$source_type" == "pr" ]]; then
123+ if [[ "$backend_account_group" != "dev" ]]; then
124+ echo "[ERROR] PR deployments are only allowed for dev backend deployments."
125+ exit 1
126+ fi
127+
128+ if [[ ! "$source_value" =~ ^[0-9]+$ ]]; then
129+ echo "[ERROR] PR source_value must be a numeric PR number."
130+ exit 1
131+ fi
132+
133+ release_version=$(gh pr view "$source_value" --repo "$GITHUB_REPOSITORY" --json headRefName --jq '.headRefName')
134+ if [[ -z "$release_version" || "$release_version" == "null" ]]; then
135+ echo "[ERROR] PR #$source_value was not found."
136+ exit 1
137+ fi
93138 else
94- echo release_version=$(echo ${{inputs.version}}) >> $GITHUB_OUTPUT
139+ echo "[ERROR] Unsupported source type '$source_type'."
140+ exit 1
95141 fi
96142
97- - name : " Get release version"
98- id : download-asset
99- shell : bash
143+ if [[ "$backend_account_group" == "nonprod" || "$backend_account_group" == "prod" ]]; then
144+ if [[ "$is_release" != "true" ]]; then
145+ echo "[ERROR] Only tagged releases can be deployed to NONPROD and PROD backends."
146+ exit 1
147+ fi
148+ fi
149+
150+ case "$backend_account_group" in
151+ dev)
152+ target_account_group="nhs-notify-supplier-api-dev"
153+ ;;
154+ nonprod)
155+ target_account_group="nhs-notify-supplier-api-nonprod"
156+ ;;
157+ prod)
158+ target_account_group="nhs-notify-supplier-api-prod"
159+ ;;
160+ *)
161+ echo "[ERROR] Unsupported backend account group '$backend_account_group'."
162+ exit 1
163+ ;;
164+ esac
165+
166+ build_artifact_version=""
167+ if [[ "$is_release" != "true" ]]; then
168+ build_artifact_version="manual"
169+ fi
170+
171+ echo "release_version=$release_version" >> "$GITHUB_OUTPUT"
172+ echo "is_release=$is_release" >> "$GITHUB_OUTPUT"
173+ echo "build_artifact_version=$build_artifact_version" >> "$GITHUB_OUTPUT"
174+ echo "target_account_group=$target_account_group" >> "$GITHUB_OUTPUT"
175+ echo "target_environment=$backend_environment" >> "$GITHUB_OUTPUT"
176+ echo "apim_environment=$apim_environment" >> "$GITHUB_OUTPUT"
177+
178+ deploy-backend :
179+ name : Deploy backend
180+ runs-on : ubuntu-latest
181+ timeout-minutes : 30
182+ needs : validate
183+ steps :
184+ - name : Checkout repository
185+ uses : actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
186+
187+ - name : Deploy backend environment
100188 env :
101- GH_TOKEN : ${{ github.token }}
189+ APP_CLIENT_ID : ${{ secrets.APP_CLIENT_ID }}
190+ APP_PEM_FILE : ${{ secrets.APP_PEM_FILE }}
102191 run : |
103- gh release download ${{steps.get-asset-version.outputs.release_version}} -p jekyll-docs-*.tar --output artifact.tar
192+ bash .github/scripts/dispatch_internal_repo_workflow.sh \
193+ --releaseVersion "${{ needs.validate.outputs.release_version }}" \
194+ --targetWorkflow "dispatch-deploy-static-notify-supplier-api-env.yaml" \
195+ --targetEnvironment "${{ needs.validate.outputs.target_environment }}" \
196+ --targetAccountGroup "${{ needs.validate.outputs.target_account_group }}" \
197+ --targetComponent "api" \
198+ --terraformAction "apply"
104199
105- - uses : actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
200+ deploy-proxy :
201+ name : Deploy proxy
202+ runs-on : ubuntu-latest
203+ timeout-minutes : 30
204+ needs : [validate, deploy-backend]
205+ steps :
206+ - name : Build OAS spec for non-release source
207+ if : ${{ needs.validate.outputs.is_release != 'true' }}
208+ uses : ./.github/actions/build-oas-spec
106209 with :
107- name : jekyll-docs-${{steps.get-asset-version.outputs.release_version}}
108- path : artifact.tar
210+ version : ${{ needs.validate.outputs.build_artifact_version }}
211+ apimEnv : ${{ needs.validate.outputs.apim_environment }}
212+ nodejs_version : " 22.22.0"
213+ NODE_AUTH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
109214
110- - name : Deploy to GitHub Pages
111- id : deployment
112- uses : actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
215+ - name : Deploy proxy
216+ env :
217+ PROXYGEN_API_NAME : nhs-notify-supplier
218+ APP_CLIENT_ID : ${{ secrets.APP_CLIENT_ID }}
219+ APP_PEM_FILE : ${{ secrets.APP_PEM_FILE }}
220+ uses : ./.github/actions/build-proxies
113221 with :
114- artifact_name : jekyll-docs-${{steps.get-asset-version.outputs.release_version}}
222+ targetComponent : api
223+ environment : ${{ needs.validate.outputs.target_environment }}
224+ apimEnv : ${{ needs.validate.outputs.apim_environment }}
225+ runId : " ${{ github.run_id }}"
226+ releaseVersion : ${{ needs.validate.outputs.release_version }}
227+ isRelease : ${{ needs.validate.outputs.is_release }}
228+ version : ${{ needs.validate.outputs.build_artifact_version }}
0 commit comments