Skip to content

Commit 150a8d0

Browse files
committed
feat: DevTest workflows set, triggered by a commit with devtest tag
1 parent b5f90c9 commit 150a8d0

5 files changed

Lines changed: 479 additions & 11 deletions

File tree

Lines changed: 154 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,164 @@
1-
name: CI/CD pull request - devtest
1+
name: 'CI/CD pull request - devtest'
22

33
on:
44
push:
55
tags:
66
- 'devtest'
77

8+
permissions:
9+
contents: read
10+
id-token: write
11+
pull-requests: write
12+
813
jobs:
9-
print-debug-info:
14+
metadata:
15+
name: "Set CI/CD metadata"
1016
runs-on: ubuntu-latest
17+
timeout-minutes: 1
18+
permissions:
19+
pull-requests: read
20+
outputs:
21+
build_datetime_london: ${{ steps.variables.outputs.build_datetime_london }}
22+
build_datetime: ${{ steps.variables.outputs.build_datetime }}
23+
build_timestamp: ${{ steps.variables.outputs.build_timestamp }}
24+
build_epoch: ${{ steps.variables.outputs.build_epoch }}
25+
nodejs_version: ${{ steps.variables.outputs.nodejs_version }}
26+
python_version: ${{ steps.variables.outputs.python_version }}
27+
terraform_version: ${{ steps.variables.outputs.terraform_version }}
28+
environment_tag: ${{ steps.variables.outputs.environment_tag }}
29+
version: ${{ steps.variables.outputs.version }}
30+
does_pull_request_exist: ${{ steps.pr_exists.outputs.does_pull_request_exist }}
1131
steps:
12-
- name: Display Information
32+
- name: "Checkout code"
33+
uses: actions/checkout@v4
34+
with:
35+
submodules: 'true'
36+
- name: "Set CI/CD variables"
37+
id: variables
38+
run: |
39+
datetime=$(date -u +'%Y-%m-%dT%H:%M:%S%z')
40+
BUILD_DATETIME=$datetime make version-create-effective-file
41+
echo "build_datetime_london=$(TZ=Europe/London date --date=$datetime +'%Y-%m-%dT%H:%M:%S%z')" >> $GITHUB_OUTPUT
42+
echo "build_datetime=$datetime" >> $GITHUB_OUTPUT
43+
echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
44+
echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT
45+
echo "nodejs_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
46+
echo "python_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
47+
echo "terraform_version=$(grep "^terraform" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
48+
echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT
49+
echo "environment_tag=development" >> $GITHUB_OUTPUT
50+
- name: "Check if pull request exists for this branch"
51+
id: pr_exists
52+
env:
53+
GH_TOKEN: ${{ github.token }}
54+
run: |
55+
branch_name=${GITHUB_HEAD_REF:-$(echo $GITHUB_REF | sed 's#refs/heads/##')}
56+
echo "Current branch is '$branch_name'"
57+
if gh pr list --head $branch_name | grep -q .; then
58+
echo "Pull request exists"
59+
echo "does_pull_request_exist=true" >> $GITHUB_OUTPUT
60+
else
61+
echo "Pull request doesn't exist"
62+
echo "does_pull_request_exist=false" >> $GITHUB_OUTPUT
63+
fi
64+
- name: "List variables"
1365
run: |
14-
echo "Workflow triggered by tag!"
15-
echo "--------------------------------------"
16-
echo "Tag/Ref Name : $GITHUB_REF"
17-
echo "Commit Hash : $GITHUB_SHA"
18-
echo "Triggered By : $GITHUB_ACTOR"
19-
echo "Event Name : $GITHUB_EVENT_NAME"
20-
echo "--------------------------------------"
66+
export BUILD_DATETIME_LONDON="${{ steps.variables.outputs.build_datetime_london }}"
67+
export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}"
68+
export BUILD_TIMESTAMP="${{ steps.variables.outputs.build_timestamp }}"
69+
export BUILD_EPOCH="${{ steps.variables.outputs.build_epoch }}"
70+
export NODEJS_VERSION="${{ steps.variables.outputs.nodejs_version }}"
71+
export PYTHON_VERSION="${{ steps.variables.outputs.python_version }}"
72+
export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}"
73+
export ENVIRONMENT_TAG="${{ steps.variables.outputs.environment_tag }}"
74+
export VERSION="${{ steps.variables.outputs.version }}"
75+
export DOES_PULL_REQUEST_EXIST="${{ steps.pr_exists.outputs.does_pull_request_exist }}"
76+
make list-variables
77+
commit-stage: # Recommended maximum execution time is 2 minutes
78+
name: "Commit stage"
79+
needs: [metadata]
80+
uses: ./.github/workflows/stage-1-commit.yaml
81+
with:
82+
build_datetime: "${{ needs.metadata.outputs.build_datetime }}"
83+
build_timestamp: "${{ needs.metadata.outputs.build_timestamp }}"
84+
build_epoch: "${{ needs.metadata.outputs.build_epoch }}"
85+
nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}"
86+
python_version: "${{ needs.metadata.outputs.python_version }}"
87+
terraform_version: "${{ needs.metadata.outputs.terraform_version }}"
88+
version: "${{ needs.metadata.outputs.version }}"
89+
test-stage: # Recommended maximum execution time is 5 minutes
90+
name: 'Test stage'
91+
needs: [metadata]
92+
uses: ./.github/workflows/stage-2-test.yaml
93+
with:
94+
unit_test_dir: tests/UnitTests
95+
app_dir: application/CohortManager
96+
build_datetime: '${{ needs.metadata.outputs.build_datetime }}'
97+
build_timestamp: '${{ needs.metadata.outputs.build_timestamp }}'
98+
build_epoch: '${{ needs.metadata.outputs.build_epoch }}'
99+
nodejs_version: '${{ needs.metadata.outputs.nodejs_version }}'
100+
python_version: '${{ needs.metadata.outputs.python_version }}'
101+
terraform_version: '${{ needs.metadata.outputs.terraform_version }}'
102+
version: '${{ needs.metadata.outputs.version }}'
103+
secrets: inherit
104+
analysis-stage: # Recommended maximum execution time is 5 minutes
105+
name: "Analysis stage"
106+
needs: [metadata, commit-stage, test-stage]
107+
uses: ./.github/workflows/stage-2-analyse.yaml
108+
secrets:
109+
sonar_token: ${{ secrets.SONAR_TOKEN }}
110+
with:
111+
unit_test_dir: tests/UnitTests
112+
build_datetime: "${{ needs.metadata.outputs.build_datetime }}"
113+
build_timestamp: "${{ needs.metadata.outputs.build_timestamp }}"
114+
build_epoch: "${{ needs.metadata.outputs.build_epoch }}"
115+
nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}"
116+
python_version: "${{ needs.metadata.outputs.python_version }}"
117+
terraform_version: "${{ needs.metadata.outputs.terraform_version }}"
118+
version: "${{ needs.metadata.outputs.version }}"
119+
build-image-stage: # Recommended maximum execution time is 3 minutes
120+
name: "Image build stage"
121+
needs: [metadata, commit-stage, test-stage, analysis-stage]
122+
uses: ./.github/workflows/stage-3-build-images-devtest.yaml
123+
secrets:
124+
client_id: ${{ secrets.AZURE_CLIENT_ID }}
125+
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
126+
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
127+
acr_name: ${{ secrets.ACR_NAME }}
128+
with:
129+
docker_compose_file: application/CohortManager/compose.yaml
130+
excluded_containers_csv_list: azurite,azurite-setup,sql-server
131+
environment_tag: ${{ needs.metadata.outputs.environment_tag }}
132+
function_app_source_code_path: application/CohortManager/src
133+
project_name: cohort-manager
134+
build_all_images: true
135+
deploy-stage:
136+
if: github.event_name == 'push'
137+
name: Deploy DevTest environment for commit ${{ github.sha }}
138+
needs: [metadata, build-image-stage]
139+
permissions:
140+
id-token: write
141+
contents: read
142+
uses: ./.github/workflows/stage-4-deploy-devtest.yaml
143+
secrets:
144+
client_id: ${{ secrets.AZURE_CLIENT_ID }}
145+
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
146+
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
147+
with:
148+
environments: "[\"development\"]"
149+
commit_sha: ${{ github.sha }}
150+
name: Validate PR title
151+
runs-on: ubuntu-latest
152+
permissions:
153+
pull-requests: write
154+
env:
155+
GITHUB_TOKEN: ${{ github.token }}
156+
if: github.event_name == 'push'
157+
steps:
158+
- uses: amannn/action-semantic-pull-request@v5
159+
id: validate
160+
- uses: thollander/actions-comment-pull-request@v2
161+
if: ${{ failure() && steps.validate.conclusion == 'failure' }}
162+
with:
163+
message: |
164+
Your Pull Request title must meet the conventional commit standards, please see the following documentation - https://www.conventionalcommits.org/en/v1.0.0/#specification
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
name: 'Docker Image CI - devtest'
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+
secrets:
34+
client_id:
35+
description: 'The Azure Client ID.'
36+
required: true
37+
tenant_id:
38+
description: 'The Azure Tenant ID.'
39+
required: true
40+
subscription_id:
41+
description: 'The Azure Subscription ID.'
42+
required: true
43+
acr_name:
44+
description: 'The name of the Azure Container Registry.'
45+
required: true
46+
47+
jobs:
48+
get-functions:
49+
runs-on: ubuntu-latest
50+
permissions:
51+
contents: read
52+
pull-requests: read
53+
id-token: write
54+
outputs:
55+
FUNC_NAMES: ${{ steps.get-function-names.outputs.FUNC_NAMES }}
56+
DOCKER_COMPOSE_DIR: ${{ steps.get-function-names.outputs.DOCKER_COMPOSE_DIR }}
57+
steps:
58+
- uses: actions/checkout@v4
59+
with:
60+
fetch-depth: 2
61+
token: ${{ secrets.GITHUB_TOKEN }}
62+
63+
- name: Checkout dtos-devops-templates repository
64+
uses: actions/checkout@v4
65+
with:
66+
repository: NHSDigital/dtos-devops-templates
67+
path: templates
68+
ref: main
69+
70+
- name: Determine which Docker container(s) to build
71+
id: get-function-names
72+
env:
73+
COMPOSE_FILES_CSV: ${{ inputs.docker_compose_file }}
74+
EXCLUDED_CONTAINERS_CSV: ${{ inputs.excluded_containers_csv_list }}
75+
SOURCE_CODE_PATH: ${{ inputs.function_app_source_code_path }}
76+
MANUAL_BUILD_ALL: ${{ inputs.build_all_images || false }}
77+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78+
run: bash scripts/deployment/get-docker-names.sh
79+
80+
build-and-push:
81+
runs-on: ubuntu-latest
82+
permissions:
83+
id-token: write
84+
contents: read
85+
pull-requests: read
86+
needs: get-functions
87+
strategy:
88+
matrix:
89+
function: ${{ fromJSON(needs.get-functions.outputs.FUNC_NAMES) }}
90+
if: needs.get-functions.outputs.FUNC_NAMES != '[]'
91+
outputs:
92+
pr_num_tag: ${{ env.PR_NUM_TAG }}
93+
short_commit_hash: ${{ env.COMMIT_HASH_TAG }}
94+
devtest_commit_hash: ${{ env.DEVTEST_HASH_TAG }}
95+
steps:
96+
- uses: actions/checkout@v4
97+
with:
98+
token: ${{ secrets.GITHUB_TOKEN }}
99+
fetch-depth: 1
100+
submodules: 'true'
101+
102+
- name: Checkout dtos-devops-templates repository
103+
uses: actions/checkout@v4
104+
with:
105+
repository: NHSDigital/dtos-devops-templates
106+
path: templates
107+
ref: main
108+
109+
- name: Az CLI login
110+
uses: azure/login@v2
111+
with:
112+
client-id: ${{ secrets.client_id }}
113+
tenant-id: ${{ secrets.tenant_id }}
114+
subscription-id: ${{ secrets.subscription_id }}
115+
116+
- name: Azure Container Registry login
117+
env:
118+
ACR_NAME: ${{ secrets.acr_name }}
119+
run: az acr login --name ${ACR_NAME}
120+
121+
- name: Create Tags
122+
env:
123+
GH_TOKEN: ${{ github.token }}
124+
ENVIRONMENT_TAG: ${{ inputs.environment_tag }}
125+
continue-on-error: false
126+
run: |
127+
echo "The branch is: ${GITHUB_REF}"
128+
129+
if [[ "${GITHUB_REF}" == refs/pull/*/merge ]]; then
130+
PR_NUM_TAG=$(echo "${GITHUB_REF}" | sed 's/refs\/pull\/\([0-9]*\)\/merge/\1/')
131+
else
132+
PULLS_JSON=$(gh api /repos/{owner}/{repo}/commits/${GITHUB_SHA}/pulls)
133+
ORIGINATING_BRANCH=$(echo ${PULLS_JSON} | jq -r '.[].head.ref' | python3 -c "import sys, urllib.parse; print(urllib.parse.quote_plus(sys.stdin.read().strip()))")
134+
echo "ORIGINATING_BRANCH: ${ORIGINATING_BRANCH}"
135+
PR_NUM_TAG=$(echo ${PULLS_JSON} | jq -r '.[].number')
136+
fi
137+
138+
echo "PR_NUM_TAG: pr${PR_NUM_TAG}"
139+
echo "PR_NUM_TAG=pr${PR_NUM_TAG}" >> ${GITHUB_ENV}
140+
141+
SHORT_COMMIT_HASH=$(git rev-parse --short ${GITHUB_SHA})
142+
echo "Commit hash tag: ${SHORT_COMMIT_HASH}"
143+
echo "COMMIT_HASH_TAG=${SHORT_COMMIT_HASH}" >> ${GITHUB_ENV}
144+
145+
DEVTEST_COMMIT_HASH="devtest_${SHORT_COMMIT_HASH}"
146+
echo "Commit devtest hash tag: ${DEVTEST_COMMIT_HASH}"
147+
echo "DEVTEST_HASH_TAG=${DEVTEST_COMMIT_HASH}" >> ${GITHUB_ENV}
148+
149+
echo "ENVIRONMENT_TAG=${ENVIRONMENT_TAG}" >> ${GITHUB_ENV}
150+
151+
- name: Build and Push Image
152+
working-directory: ${{ steps.get-function-names.outputs.DOCKER_COMPOSE_DIR }}
153+
continue-on-error: false
154+
env:
155+
COMPOSE_FILE: ${{ inputs.docker_compose_file }}
156+
PROJECT_NAME: ${{ inputs.project_name }}
157+
ACR_NAME: ${{ secrets.acr_name }}
158+
run: |
159+
function=${{ matrix.function }}
160+
161+
echo PROJECT_NAME: ${PROJECT_NAME}
162+
163+
if [ -z "${function}" ]; then
164+
echo "Function variable is empty. Skipping Docker build."
165+
exit 0
166+
fi
167+
168+
# Build the image
169+
docker compose -f ${COMPOSE_FILE//,/ -f } -p ${PROJECT_NAME} --profile "*" build --no-cache --pull ${function}
170+
171+
repo_name="${ACR_NAME}.azurecr.io/${PROJECT_NAME}-${function}"
172+
echo $(repo_name)
173+
174+
# Tag the image
175+
echo "Tag the image:"
176+
docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${DEVTEST_HASH_TAG}"
177+
# docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${PR_NUM_TAG}"
178+
# docker tag ${PROJECT_NAME}-${function}:latest "$repo_name:${ENVIRONMENT_TAG}"
179+
180+
# If this variable is set, the create-sbom-report.sh script will scan this docker image instead.
181+
export CHECK_DOCKER_IMAGE=${PROJECT_NAME}-${function}:latest
182+
export FORCE_USE_DOCKER=true
183+
184+
export PR_NUM_TAG=${PR_NUM_TAG}
185+
echo "PR_NUM_TAG=${PR_NUM_TAG}" >> ${GITHUB_ENV}
186+
187+
# Push the image to the repository
188+
docker push "${repo_name}:${DEVTEST_HASH_TAG}"
189+
190+
- name: Cleanup the docker images
191+
env:
192+
PROJECT_NAME: ${{ inputs.project_name }}
193+
ACR_NAME: ${{ secrets.acr_name }}
194+
run: |
195+
function=${{ matrix.function }}
196+
repo_name="${ACR_NAME}.azurecr.io/${PROJECT_NAME}-${function}"
197+
198+
# Remove the images
199+
docker rmi "${repo_name}:${DEVTEST_HASH_TAG}"
200+
# docker rmi "${repo_name}:${PR_NUM_TAG}"
201+
# docker rmi "${repo_name}:${ENVIRONMENT_TAG}"
202+
docker rmi ${PROJECT_NAME}-${function}:latest

0 commit comments

Comments
 (0)