Skip to content

Commit 3d6b300

Browse files
Merge branch 'main' into test/DTOSS-3883-test-part2
2 parents b479f7b + 2fa0f10 commit 3d6b300

59 files changed

Lines changed: 3888 additions & 1908 deletions

File tree

Some content is hidden

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

.github/workflows/ci-ui-tests.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ jobs:
3131
- name: Install Playwright browsers
3232
run: npx playwright install --with-deps
3333

34-
# <-- ADD THIS DEBUG STEP HERE
35-
- name: Debug AUTH_SECRET
36-
run: echo "AUTH_SECRET is set: ${AUTH_SECRET:+yes}"
37-
env:
38-
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
39-
4034
- name: Start application and run tests
4135
env:
4236
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}

.github/workflows/cicd-1-pull-request.yaml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ on:
99
pull_request:
1010
types: [opened, reopened, synchronize]
1111

12+
permissions:
13+
contents: read
14+
id-token: write
15+
pull-requests: write
16+
1217
jobs:
1318
metadata:
1419
name: "Set CI/CD metadata"
@@ -118,14 +123,15 @@ jobs:
118123
build-image-stage: # Recommended maximum execution time is 3 minutes
119124
name: "Image build stage"
120125
needs: [metadata, commit-stage, test-stage, analysis-stage]
121-
uses: NHSDigital/dtos-devops-templates/.github/workflows/stage-3-build-images.yaml@main
126+
uses: ./.github/workflows/stage-3-build-images.yaml
122127
if: needs.metadata.outputs.does_pull_request_exist == 'true' || github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened'))
123128
with:
124129
docker_compose_file: application/CohortManager/compose.yaml
125130
excluded_containers_csv_list: azurite,azurite-setup,sql-server
126131
environment_tag: ${{ needs.metadata.outputs.environment_tag }}
127132
function_app_source_code_path: application/CohortManager/src
128133
project_name: cohort-manager
134+
build_all_images: true
129135
secrets: inherit
130136
acceptance-stage: # Recommended maximum execution time is 10 minutes
131137
name: "Acceptance stage"
@@ -141,6 +147,18 @@ jobs:
141147
terraform_version: "${{ needs.metadata.outputs.terraform_version }}"
142148
version: "${{ needs.metadata.outputs.version }}"
143149
secrets: inherit
150+
deploy-stage:
151+
if: github.ref == 'refs/heads/main' && github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'deploy')
152+
name: Deploy pr-${{ github.event.pull_request.number }} for commit ${{ github.event.pull_request.head.sha }}
153+
needs: [metadata, build-image-stage]
154+
permissions:
155+
id-token: write
156+
contents: read
157+
uses: ./.github/workflows/stage-4-deploy.yaml
158+
with:
159+
environments: "[\"sandbox\"]"
160+
commit_sha: ${{ github.event.pull_request.head.sha }}
161+
secrets: inherit
144162
validate-title-stage:
145163
name: Validate PR title
146164
runs-on: ubuntu-latest
@@ -152,7 +170,6 @@ jobs:
152170
steps:
153171
- uses: amannn/action-semantic-pull-request@v5
154172
id: validate
155-
156173
- uses: thollander/actions-comment-pull-request@v2
157174
if: ${{ failure() && steps.validate.conclusion == 'failure' }}
158175
with:

.github/workflows/cicd-2-publish.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
name: "CI/CD publish"
22

3-
on:
4-
pull_request:
5-
types: [closed]
6-
branches:
7-
- main
8-
93
jobs:
104
metadata:
115
name: "Set CI/CD metadata"
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
name: Docker Image CI
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
environment_tag:
7+
description: Environment of the deployment
8+
required: true
9+
type: string
10+
default: development
11+
docker_compose_file:
12+
description: The path of the compose.yaml file needed to build docker images
13+
required: true
14+
type: string
15+
function_app_source_code_path:
16+
description: The source path of the function app source code for the docker builds
17+
required: true
18+
type: string
19+
project_name:
20+
description: The name of the project
21+
required: true
22+
type: string
23+
excluded_containers_csv_list:
24+
description: Excluded containers in a comma separated list
25+
required: true
26+
type: string
27+
build_all_images:
28+
description: Build all images (true) or only changed ones (false)
29+
required: false
30+
type: boolean
31+
default: false
32+
33+
jobs:
34+
get-functions:
35+
runs-on: ubuntu-latest
36+
permissions:
37+
contents: read
38+
pull-requests: read
39+
id-token: write
40+
outputs:
41+
FUNC_NAMES: ${{ steps.get-function-names.outputs.FUNC_NAMES }}
42+
DOCKER_COMPOSE_DIR: ${{ steps.get-function-names.outputs.DOCKER_COMPOSE_DIR }}
43+
steps:
44+
- uses: actions/checkout@v4
45+
with:
46+
fetch-depth: 2
47+
token: ${{ secrets.GITHUB_TOKEN }}
48+
49+
- name: Checkout dtos-devops-templates repository
50+
uses: actions/checkout@v4
51+
with:
52+
repository: NHSDigital/dtos-devops-templates
53+
path: templates
54+
ref: main
55+
56+
- name: Determine which Docker container(s) to build
57+
id: get-function-names
58+
env:
59+
COMPOSE_FILES_CSV: ${{ inputs.docker_compose_file }}
60+
EXCLUDED_CONTAINERS_CSV: ${{ inputs.excluded_containers_csv_list }}
61+
SOURCE_CODE_PATH: ${{ inputs.function_app_source_code_path }}
62+
MANUAL_BUILD_ALL: ${{ inputs.build_all_images || false }}
63+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
64+
run: bash scripts/deployment/get-docker-names.sh
65+
66+
build-and-push:
67+
runs-on: ubuntu-latest
68+
permissions:
69+
id-token: write
70+
contents: read
71+
pull-requests: read
72+
needs: get-functions
73+
strategy:
74+
matrix:
75+
function: ${{ fromJSON(needs.get-functions.outputs.FUNC_NAMES) }}
76+
if: needs.get-functions.outputs.FUNC_NAMES != '[]'
77+
outputs:
78+
pr_num_tag: ${{ env.PR_NUM_TAG }}
79+
short_commit_hash: ${{ env.COMMIT_HASH_TAG }}
80+
steps:
81+
- uses: actions/checkout@v4
82+
with:
83+
token: ${{ secrets.GITHUB_TOKEN }}
84+
fetch-depth: 1
85+
submodules: 'true'
86+
87+
- name: Checkout dtos-devops-templates repository
88+
uses: actions/checkout@v4
89+
with:
90+
repository: NHSDigital/dtos-devops-templates
91+
path: templates
92+
ref: main
93+
94+
- name: Az CLI login
95+
if: github.ref == 'refs/heads/main'
96+
uses: azure/login@v2
97+
with:
98+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
99+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
100+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
101+
102+
- name: Azure Container Registry login
103+
if: github.ref == 'refs/heads/main'
104+
run: az acr login --name ${{ secrets.ACR_NAME }}
105+
106+
- name: Create Tags
107+
env:
108+
GH_TOKEN: ${{ github.token }}
109+
ENVIRONMENT_TAG: ${{ inputs.environment_tag }}
110+
continue-on-error: false
111+
run: |
112+
echo "The branch is: ${GITHUB_REF}"
113+
114+
if [[ "${GITHUB_REF}" == refs/pull/*/merge ]]; then
115+
PR_NUM_TAG=$(echo "${GITHUB_REF}" | sed 's/refs\/pull\/\([0-9]*\)\/merge/\1/')
116+
else
117+
PULLS_JSON=$(gh api /repos/{owner}/{repo}/commits/${GITHUB_SHA}/pulls)
118+
ORIGINATING_BRANCH=$(echo ${PULLS_JSON} | jq -r '.[].head.ref' | python3 -c "import sys, urllib.parse; print(urllib.parse.quote_plus(sys.stdin.read().strip()))")
119+
echo "ORIGINATING_BRANCH: ${ORIGINATING_BRANCH}"
120+
PR_NUM_TAG=$(echo ${PULLS_JSON} | jq -r '.[].number')
121+
fi
122+
123+
echo "PR_NUM_TAG: pr${PR_NUM_TAG}"
124+
echo "PR_NUM_TAG=pr${PR_NUM_TAG}" >> ${GITHUB_ENV}
125+
126+
SHORT_COMMIT_HASH=$(git rev-parse --short ${GITHUB_SHA})
127+
echo "Commit hash tag: ${SHORT_COMMIT_HASH}"
128+
echo "COMMIT_HASH_TAG=${SHORT_COMMIT_HASH}" >> ${GITHUB_ENV}
129+
130+
echo "ENVIRONMENT_TAG=${ENVIRONMENT_TAG}" >> ${GITHUB_ENV}
131+
132+
- name: Build and Push Image
133+
working-directory: ${{ steps.get-function-names.outputs.DOCKER_COMPOSE_DIR }}
134+
continue-on-error: false
135+
env:
136+
COMPOSE_FILE: ${{ inputs.docker_compose_file }}
137+
PROJECT_NAME: ${{ inputs.project_name }}
138+
run: |
139+
function=${{ matrix.function }}
140+
141+
echo PROJECT_NAME: ${PROJECT_NAME}
142+
143+
if [ -z "${function}" ]; then
144+
echo "Function variable is empty. Skipping Docker build."
145+
exit 0
146+
fi
147+
148+
# Build the image
149+
docker compose -f ${COMPOSE_FILE//,/ -f } -p ${PROJECT_NAME} --profile "*" build --no-cache --pull ${function}
150+
151+
repo_name="${{ secrets.ACR_NAME }}.azurecr.io/${PROJECT_NAME}-${function}"
152+
echo $(repo_name)
153+
154+
# Tag the image
155+
echo "Tag the image:"
156+
docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${COMMIT_HASH_TAG}"
157+
docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${PR_NUM_TAG}"
158+
docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${ENVIRONMENT_TAG}"
159+
160+
# If this variable is set, the create-sbom-report.sh script will scan this docker image instead.
161+
export CHECK_DOCKER_IMAGE=${PROJECT_NAME}-${function}:latest
162+
export FORCE_USE_DOCKER=true
163+
164+
export PR_NUM_TAG=${PR_NUM_TAG}
165+
echo "PR_NUM_TAG=${PR_NUM_TAG}" >> ${GITHUB_ENV}
166+
167+
# Push the image to the repository
168+
if [ "${GITHUB_REF}" == 'refs/heads/main' ]; then
169+
docker push "${repo_name}:${COMMIT_HASH_TAG}"
170+
if [ "${PR_NUM_TAG}" != 'pr' ]; then
171+
docker push "${repo_name}:${PR_NUM_TAG}"
172+
fi
173+
docker push "${repo_name}:${ENVIRONMENT_TAG}"
174+
fi
175+
176+
export SBOM_REPOSITORY_REPORT="sbom-${function}-repository-report"
177+
echo "SBOM_REPOSITORY_REPORT=$SBOM_REPOSITORY_REPORT" >> $GITHUB_ENV
178+
bash -x ${GITHUB_WORKSPACE}/templates/scripts/reports/create-sbom-report.sh
179+
180+
export VULNERABILITIES_REPOSITORY_REPORT="vulnerabilities-${function}-repository-report"
181+
echo "VULNERABILITIES_REPOSITORY_REPORT=$VULNERABILITIES_REPOSITORY_REPORT" >> $GITHUB_ENV
182+
bash -x ${GITHUB_WORKSPACE}/templates/scripts/reports/scan-vulnerabilities.sh
183+
184+
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sudo sh -s -- -b /usr/local/bin
185+
186+
SCAN_RESULTS=$(grype "${PROJECT_NAME}-${function}:latest" --scope all-layers)
187+
# ANSI color codes
188+
RED="\033[0;31m"
189+
RESET="\033[0m"
190+
191+
# Define your log file
192+
VULNERABILITIES_SUMMARY_LOGFILE="${PROJECT_NAME}-${function}-vulnerabilities-summary.txt"
193+
echo "VULNERABILITIES_SUMMARY_LOGFILE=$VULNERABILITIES_SUMMARY_LOGFILE" >> $GITHUB_ENV
194+
195+
# Clear existing log file (or create if it doesn't exist)
196+
> "$VULNERABILITIES_SUMMARY_LOGFILE"
197+
198+
for SEVERITY in CRITICAL HIGH MEDIUM; do
199+
{
200+
echo ""
201+
echo "${PROJECT_NAME}-${function}: vulnerabilities"
202+
echo -e "=== ${RED}${SEVERITY}${RESET} Vulnerabilities list ==="
203+
# If grep finds nothing, we print a fallback message
204+
echo "$SCAN_RESULTS" | grep -i "$SEVERITY" || echo "No $SEVERITY vulnerabilities found."
205+
} | tee -a "$VULNERABILITIES_SUMMARY_LOGFILE"
206+
done
207+
208+
# Remove the image
209+
docker rmi "${repo_name}:${COMMIT_HASH_TAG}"
210+
docker rmi "${repo_name}:${PR_NUM_TAG}"
211+
docker rmi "${repo_name}:${ENVIRONMENT_TAG}"
212+
docker rmi ${PROJECT_NAME}-${function}:latest
213+
214+
- name: Compress SBOM report
215+
shell: bash
216+
run: |
217+
echo SBOM_REPOSITORY_REPORT: ${SBOM_REPOSITORY_REPORT}
218+
zip "${SBOM_REPOSITORY_REPORT}.json.zip" "${SBOM_REPOSITORY_REPORT}.json"
219+
220+
- name: Upload SBOM report as an artefact
221+
uses: actions/upload-artifact@v4
222+
with:
223+
name: ${{ env.SBOM_REPOSITORY_REPORT }}.json.zip
224+
path: ./${{ env.SBOM_REPOSITORY_REPORT }}.json.zip
225+
retention-days: 21
226+
227+
- name: Compress vulnerabilities report
228+
shell: bash
229+
run: |
230+
echo VULNERABILITIES_REPOSITORY_REPORT: ${VULNERABILITIES_REPOSITORY_REPORT}
231+
zip ${VULNERABILITIES_REPOSITORY_REPORT}.json.zip ${VULNERABILITIES_REPOSITORY_REPORT}.json
232+
233+
- name: Upload vulnerabilities report as an artefact
234+
uses: actions/upload-artifact@v4
235+
with:
236+
name: ${{ env.VULNERABILITIES_REPOSITORY_REPORT }}.json.zip
237+
path: ./${{ env.VULNERABILITIES_REPOSITORY_REPORT }}.json.zip
238+
retention-days: 21
239+
240+
- name: Upload vulnerabilities summary report as an artefact
241+
uses: actions/upload-artifact@v4
242+
with:
243+
name: ${{ env.VULNERABILITIES_SUMMARY_LOGFILE }}
244+
path: ./${{ env.VULNERABILITIES_SUMMARY_LOGFILE }}
245+
retention-days: 21
246+
247+
aggregate-json:
248+
runs-on: ubuntu-latest
249+
needs: build-and-push
250+
steps:
251+
- name: Download SBOM JSON artifacts
252+
uses: actions/download-artifact@v4
253+
with:
254+
path: ./downloaded-artifacts
255+
256+
- name: Combine sbom report JSON files
257+
run: |
258+
zip sbom-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip downloaded-artifacts/**/sbom*.json.zip
259+
260+
- name: Combine vulnerabilities report JSON files
261+
run: |
262+
zip vulnerabilities-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip downloaded-artifacts/**/vulnerabilities*.json.zip
263+
zip vulnerabilities-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip downloaded-artifacts/**/*vulnerabilities-summary*.txt
264+
265+
- name: Upload sbom zip file
266+
uses: actions/upload-artifact@v4
267+
with:
268+
name: aggregated-sbom-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip
269+
path: sbom-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip
270+
271+
- name: Upload repository zip file
272+
uses: actions/upload-artifact@v4
273+
with:
274+
name: aggregated-vulnerabilities-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip
275+
path: vulnerabilities-repository-report-${{ needs.build-and-push.outputs.PR_NUM_TAG }}.zip

0 commit comments

Comments
 (0)