Skip to content

Commit 4388af2

Browse files
first pass
1 parent 72c1a69 commit 4388af2

1 file changed

Lines changed: 197 additions & 83 deletions

File tree

.github/workflows/cicd-3-deploy.yaml

Lines changed: 197 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,112 +3,226 @@ name: "2. CD - Deploy"
33
on:
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+
1948
permissions:
20-
contents: read
21-
pages: write
2249
id-token: write
50+
contents: read
51+
packages: read
2352

2453
jobs:
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

Comments
 (0)