Skip to content

Commit 30850c6

Browse files
committed
MAINTENANCE: Sync upstream changes from repository template and update dependencies
1 parent 47df0a8 commit 30850c6

44 files changed

Lines changed: 2076 additions & 1215 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- [ ] I have added tests to cover my changes
2626
- [ ] I have updated the documentation accordingly
2727
- [ ] This PR is a result of pair or mob programming
28+
<!-- - [ ] If I have used the 'skip-trivy-package' label I have done so responsibly and in the knowledge that this is being fixed as part of a separate ticket/PR. TODO - Re-visit Trivy usage https://nhsd-jira.digital.nhs.uk/browse/CCM-15549 -->
2829

2930
---
3031

.github/SECURITY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ If you wish to notify us of a vulnerability via email, please include detailed i
2121

2222
You can reach us at:
2323

24-
- _[ A product team email address ]_
25-
- [cybersecurity@nhs.net](cybersecurity@nhs.net)
24+
- [england.nhsnotify@nhs.net](mailto:england.nhsnotify@nhs.net)
25+
- [cybersecurity@nhs.net](mailto:cybersecurity@nhs.net)
2626

2727
### NCSC
2828

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: "Check Todo usage"
2+
description: "Check Todo usage"
3+
runs:
4+
using: "composite"
5+
steps:
6+
- name: "Check Todo usage"
7+
shell: bash
8+
run: |
9+
export BRANCH_NAME=origin/${{ github.event.repository.default_branch }}
10+
check=branch ./scripts/githooks/check-todos.sh

.github/actions/lint-terraform/action.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ inputs:
77
runs:
88
using: "composite"
99
steps:
10+
- name: "Install Terraform binary"
11+
shell: bash
12+
run: |
13+
asdf plugin add terraform || true
14+
asdf install terraform || true
1015
- name: "Check Terraform format"
1116
shell: bash
1217
run: |
1318
check_only=true scripts/githooks/check-terraform-format.sh
1419
- name: "Validate Terraform"
1520
shell: bash
1621
run: |
17-
stacks=${{ inputs.root-modules }}
18-
for dir in $(find infrastructure/environments -maxdepth 1 -mindepth 1 -type d; echo ${stacks//,/$'\n'}); do
19-
dir=$dir opts='-backend=false' make terraform-init
20-
dir=$dir make terraform-validate
21-
done
22+
make terraform-validate-all
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#TODO - Re-visit Trivy usage https://nhsd-jira.digital.nhs.uk/browse/CCM-15549
2+
# name: "Trivy IaC Scan"
3+
# description: "Scan Terraform IaC using Trivy"
4+
# runs:
5+
# using: "composite"
6+
# steps:
7+
# - name: "Trivy Terraform IaC Scan"
8+
# shell: bash
9+
# run: |
10+
# components_exit_code=0
11+
# modules_exit_code=0
12+
# asdf plugin add trivy || true
13+
# asdf install trivy || true
14+
# ./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/components || components_exit_code=$?
15+
# ./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/modules || modules_exit_code=$?
16+
17+
# if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then
18+
# echo "Trivy misconfigurations detected."
19+
# exit 1
20+
# fi
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#TODO - Re-visit Trivy usage https://nhsd-jira.digital.nhs.uk/browse/CCM-15549
2+
# name: "Trivy Package Scan"
3+
# description: "Scan project packages using Trivy"
4+
# runs:
5+
# using: "composite"
6+
# steps:
7+
# - name: "Trivy Package Scan"
8+
# shell: bash
9+
# run: |
10+
# exit_code=0
11+
# asdf plugin add trivy || true
12+
# asdf install trivy || true
13+
# ./scripts/terraform/trivy-scan.sh --mode package . || exit_code=$?
14+
15+
# if [ $exit_code -ne 0 ]; then
16+
# echo "Trivy has detected package vulnerablilites. Please refer to https://nhsd-confluence.digital.nhs.uk/spaces/RIS/pages/1257636917/PLAT-KOP-012+-+Trivy+Pipeline+Vulnerability+Scanning+Exemption"
17+
# exit 1
18+
# fi
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/bin/bash
2+
3+
# Triggers a remote GitHub workflow in nhs-notify-internal and waits for completion.
4+
5+
# Usage:
6+
# ./dispatch_internal_repo_workflow.sh \
7+
# --infraRepoName <repo> \
8+
# --releaseVersion <version> \
9+
# --targetWorkflow <workflow.yaml> \
10+
# --targetEnvironment <env> \
11+
# --targetComponent <component> \
12+
# --targetAccountGroup <group> \
13+
# --terraformAction <action> \
14+
# --internalRef <ref> \
15+
# --overrides <overrides> \
16+
# --overrideProjectName <name> \
17+
# --overrideRoleName <name>
18+
19+
#
20+
# All arguments are required except terraformAction, and internalRef.
21+
# Example:
22+
# ./dispatch_internal_repo_workflow.sh \
23+
# --infraRepoName "nhs-notify-dns" \
24+
# --releaseVersion "v1.2.3" \
25+
# --targetWorkflow "deploy.yaml" \
26+
# --targetEnvironment "prod" \
27+
# --targetComponent "web" \
28+
# --targetAccountGroup "core" \
29+
# --terraformAction "apply" \
30+
# --internalRef "main" \
31+
# --overrides "tf_var=someString" \
32+
# --overrideProjectName nhs \
33+
# --overrideRoleName nhs-service-iam-role
34+
35+
set -e
36+
37+
while [[ $# -gt 0 ]]; do
38+
case $1 in
39+
--infraRepoName) # Name of the infrastructure repo in NHSDigital org (required)
40+
infraRepoName="$2"
41+
shift 2
42+
;;
43+
--releaseVersion) # Release version, commit, or tag to deploy (required)
44+
releaseVersion="$2"
45+
shift 2
46+
;;
47+
--targetWorkflow) # Name of the workflow file to call in nhs-notify-internal (required)
48+
targetWorkflow="$2"
49+
shift 2
50+
;;
51+
--targetEnvironment) # Terraform environment to deploy (required)
52+
targetEnvironment="$2"
53+
shift 2
54+
;;
55+
--targetComponent) # Terraform component to deploy (required)
56+
targetComponent="$2"
57+
shift 2
58+
;;
59+
--targetAccountGroup) # Terraform account group to deploy (required)
60+
targetAccountGroup="$2"
61+
shift 2
62+
;;
63+
--terraformAction) # Terraform action to run (optional)
64+
terraformAction="$2"
65+
shift 2
66+
;;
67+
--internalRef) # Internal repo reference branch or tag (optional, default: "main")
68+
internalRef="$2"
69+
shift 2
70+
;;
71+
--overrides) # Terraform overrides for passing in extra variables (optional)
72+
overrides="$2"
73+
shift 2
74+
;;
75+
--overrideProjectName) # Override the project name (optional)
76+
overrideProjectName="$2"
77+
shift 2
78+
;;
79+
--overrideRoleName) # Override the role name (optional)
80+
overrideRoleName="$2"
81+
shift 2
82+
;;
83+
*)
84+
echo "[ERROR] Unknown argument: $1"
85+
exit 1
86+
;;
87+
esac
88+
done
89+
90+
if [[ -z "$APP_PEM_FILE" ]]; then
91+
echo "[ERROR] PEM_FILE environment variable is not set or is empty."
92+
exit 1
93+
fi
94+
95+
if [[ -z "$APP_CLIENT_ID" ]]; then
96+
echo "[ERROR] CLIENT_ID environment variable is not set or is empty."
97+
exit 1
98+
fi
99+
100+
now=$(date +%s)
101+
iat=$((${now} - 60)) # Issues 60 seconds in the past
102+
exp=$((${now} + 600)) # Expires 10 minutes in the future
103+
104+
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }
105+
106+
header_json='{
107+
"typ":"JWT",
108+
"alg":"RS256"
109+
}'
110+
# Header encode
111+
header=$( echo -n "${header_json}" | b64enc )
112+
113+
payload_json="{
114+
\"iat\":${iat},
115+
\"exp\":${exp},
116+
\"iss\":\"${APP_CLIENT_ID}\"
117+
}"
118+
# Payload encode
119+
payload=$( echo -n "${payload_json}" | b64enc )
120+
121+
# Signature
122+
header_payload="${header}"."${payload}"
123+
signature=$(
124+
openssl dgst -sha256 -sign <(echo -n "${APP_PEM_FILE}") \
125+
<(echo -n "${header_payload}") | b64enc
126+
)
127+
128+
# Create JWT
129+
JWT="${header_payload}"."${signature}"
130+
131+
INSTALLATION_ID=$(curl -X GET \
132+
-H "Accept: application/vnd.github+json" \
133+
-H "Authorization: Bearer ${JWT}" \
134+
-H "X-GitHub-Api-Version: 2022-11-28" \
135+
--url "https://api.github.com/app/installations" | jq -r '.[0].id')
136+
137+
PR_TRIGGER_PAT=$(curl --request POST \
138+
--url "https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens" \
139+
-H "Accept: application/vnd.github+json" \
140+
-H "Authorization: Bearer ${JWT}" \
141+
-H "X-GitHub-Api-Version: 2022-11-28" | jq -r '.token')
142+
143+
# Set default values if not provided
144+
if [[ -z "$PR_TRIGGER_PAT" ]]; then
145+
echo "[ERROR] PR_TRIGGER_PAT environment variable is not set or is empty."
146+
exit 1
147+
fi
148+
149+
if [[ -z "$overrides" ]]; then
150+
overrides=""
151+
fi
152+
153+
if [[ -z "$internalRef" ]]; then
154+
internalRef="main"
155+
fi
156+
157+
echo "==================== Workflow Dispatch Parameters ===================="
158+
echo " infraRepoName: $infraRepoName"
159+
echo " releaseVersion: $releaseVersion"
160+
echo " targetWorkflow: $targetWorkflow"
161+
echo " targetEnvironment: $targetEnvironment"
162+
echo " targetComponent: $targetComponent"
163+
echo " targetAccountGroup: $targetAccountGroup"
164+
echo " terraformAction: $terraformAction"
165+
echo " internalRef: $internalRef"
166+
echo " overrides: $overrides"
167+
echo " overrideProjectName: $overrideProjectName"
168+
echo " overrideRoleName: $overrideRoleName"
169+
echo " targetProject: $targetProject"
170+
171+
DISPATCH_EVENT=$(jq -ncM \
172+
--arg infraRepoName "$infraRepoName" \
173+
--arg releaseVersion "$releaseVersion" \
174+
--arg targetEnvironment "$targetEnvironment" \
175+
--arg targetAccountGroup "$targetAccountGroup" \
176+
--arg targetComponent "$targetComponent" \
177+
--arg terraformAction "$terraformAction" \
178+
--arg targetWorkflow "$targetWorkflow" \
179+
--arg overrides "$overrides" \
180+
--arg overrideProjectName "$overrideProjectName" \
181+
--arg overrideRoleName "$overrideRoleName" \
182+
--arg targetProject "$targetProject" \
183+
'{
184+
"ref": "'"$internalRef"'",
185+
"inputs": (
186+
(if $infraRepoName != "" then { "infraRepoName": $infraRepoName } else {} end) +
187+
(if $terraformAction != "" then { "terraformAction": $terraformAction } else {} end) +
188+
(if $overrideProjectName != "" then { "overrideProjectName": $overrideProjectName } else {} end) +
189+
(if $overrideRoleName != "" then { "overrideRoleName": $overrideRoleName } else {} end) +
190+
(if $targetProject != "" then { "targetProject": $targetProject } else {} end) +
191+
{
192+
"releaseVersion": $releaseVersion,
193+
"targetEnvironment": $targetEnvironment,
194+
"targetAccountGroup": $targetAccountGroup,
195+
"targetComponent": $targetComponent,
196+
"overrides": $overrides,
197+
}
198+
)
199+
}')
200+
201+
echo "[INFO] Triggering workflow '$targetWorkflow' in nhs-notify-internal..."
202+
203+
trigger_response=$(curl -s -L \
204+
--fail \
205+
-X POST \
206+
-H "Accept: application/vnd.github+json" \
207+
-H "Authorization: Bearer ${PR_TRIGGER_PAT}" \
208+
-H "X-GitHub-Api-Version: 2022-11-28" \
209+
"https://api.github.com/repos/NHSDigital/nhs-notify-internal/actions/workflows/$targetWorkflow/dispatches" \
210+
-d "$DISPATCH_EVENT" 2>&1)
211+
212+
if [[ $? -ne 0 ]]; then
213+
echo "[ERROR] Failed to trigger workflow. Response: $trigger_response"
214+
exit 1
215+
fi
216+
217+
echo "[INFO] Workflow trigger request sent successfully, waiting for completion..."
218+
219+
sleep 10 # Wait a few seconds before checking for the presence of the api to account for GitHub updating
220+
221+
# Poll GitHub API to check the workflow status
222+
workflow_run_url=""
223+
224+
for _ in {1..18}; do
225+
226+
response=$(curl -s -L \
227+
-H "Accept: application/vnd.github+json" \
228+
-H "Authorization: Bearer ${PR_TRIGGER_PAT}" \
229+
-H "X-GitHub-Api-Version: 2022-11-28" \
230+
"https://api.github.com/repos/NHSDigital/nhs-notify-internal/actions/runs?event=workflow_dispatch")
231+
232+
if ! echo "$response" | jq empty 2>/dev/null; then
233+
echo "[ERROR] Invalid JSON response from GitHub API during workflow polling:"
234+
echo "$response"
235+
exit 1
236+
fi
237+
238+
workflow_run_url=$(echo "$response" | jq -r \
239+
--arg targetWorkflow "$targetWorkflow" \
240+
--arg targetEnvironment "$targetEnvironment" \
241+
--arg targetAccountGroup "$targetAccountGroup" \
242+
--arg targetComponent "$targetComponent" \
243+
--arg terraformAction "$terraformAction" \
244+
'.workflow_runs[]
245+
| select(.path == ".github/workflows/" + $targetWorkflow)
246+
| select(.name
247+
| contains($targetEnvironment)
248+
and contains($targetAccountGroup)
249+
and contains($targetComponent)
250+
and contains($terraformAction)
251+
)
252+
| .url')
253+
254+
if [[ -n "$workflow_run_url" && "$workflow_run_url" != null ]]; then
255+
# Workflow_run_url is a list of all workflows which were run for this combination of inputs, but are the API uri
256+
workflow_run_url=$(echo "$workflow_run_url" | head -n 1)
257+
258+
# Take the first and strip it back to being an accessible url
259+
# Example https://api.github.com/repos/MyOrg/my-repo/actions/runs/12346789 becomes
260+
# becomes https://github.com/MyOrg/my-repo/actions/runs/12346789
261+
workflow_run_ui_url=${workflow_run_url/api./} # Strips the api. prefix
262+
workflow_run_ui_url=${workflow_run_ui_url/\/repos/} # Strips the repos/ uri
263+
echo "[INFO] Found workflow run url: $workflow_run_ui_url"
264+
break
265+
fi
266+
267+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Waiting for workflow to start..."
268+
sleep 10
269+
done
270+
271+
if [[ -z "$workflow_run_url" || "$workflow_run_url" == null ]]; then
272+
echo "[ERROR] Failed to get the workflow run url. Exiting."
273+
exit 1
274+
fi
275+
276+
# Wait for workflow completion
277+
while true; do
278+
sleep 10
279+
response=$(curl -s -L \
280+
-H "Authorization: Bearer ${PR_TRIGGER_PAT}" \
281+
-H "Accept: application/vnd.github+json" \
282+
"$workflow_run_url")
283+
284+
status=$(echo "$response" | jq -r '.status')
285+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Workflow status: $status"
286+
287+
if [ "$status" == "completed" ]; then
288+
conclusion=$(echo "$response" | jq -r '.conclusion')
289+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Workflow conclusion: $conclusion"
290+
291+
if [ -z "$conclusion" ] || [ "$conclusion" == "null" ]; then
292+
echo "[WARN] Workflow marked completed but conclusion not yet available, retrying..."
293+
sleep 5
294+
continue
295+
fi
296+
297+
if [ "$conclusion" == "success" ]; then
298+
echo "[SUCCESS] Workflow completed successfully!"
299+
exit 0
300+
else
301+
echo "[FAIL] Workflow failed with conclusion: $conclusion"
302+
exit 1
303+
fi
304+
fi
305+
done

0 commit comments

Comments
 (0)