Skip to content

Commit fac3252

Browse files
committed
Refactor: Shell scripts and CI/CD pipeline
1 parent bced9b0 commit fac3252

File tree

10 files changed

+109
-51
lines changed

10 files changed

+109
-51
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: "CI/CD Pull Request"
1+
name: "CI/CD pull request"
22

33
# The total recommended execution time for the "CI/CD Pull Request" workflow is around 20 minutes.
44

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,19 @@ jobs:
7777
# asset_path: ./*
7878
# asset_name: repository-template-${{ needs.metadata.outputs.version }}.tar.gz
7979
# asset_content_type: "application/gzip"
80+
success:
81+
runs-on: ubuntu-latest
82+
needs: [publish]
83+
steps:
84+
- name: "Check prerequisites for notification"
85+
id: check
86+
run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT
87+
- name: "Notify on build completion"
88+
if: steps.check.outputs.secret_exist == 'true'
89+
uses: nhs-england-tools/[email protected]
90+
with:
91+
github-token: ${{ secrets.GITHUB_TOKEN }}
92+
teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }}
93+
message-title: "Notification title"
94+
message-text: "This is a notification body"
95+
link: ${{ github.event.pull_request.html_url }}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,19 @@ jobs:
5656
- name: "Checkout code"
5757
uses: actions/checkout@v4
5858
# TODO: More jobs or/and steps here
59+
# success:
60+
# runs-on: ubuntu-latest
61+
# needs: [deploy]
62+
# steps:
63+
# - name: "Check prerequisites for notification"
64+
# id: check
65+
# run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT
66+
# - name: "Notify on build completion"
67+
# if: steps.check.outputs.secret_exist == 'true'
68+
# uses: nhs-england-tools/[email protected]
69+
# with:
70+
# github-token: ${{ secrets.GITHUB_TOKEN }}
71+
# teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }}
72+
# message-title: "Notification title"
73+
# message-text: "This is a notification body"
74+
# link: ${{ github.event.pull_request.html_url }}

.github/workflows/stage-3-build.yaml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,3 @@ jobs:
6565
run: |
6666
echo "Uploading artefact 2 ..."
6767
# TODO: Use either action/cache or action/upload-artifact
68-
success:
69-
runs-on: ubuntu-latest
70-
needs: [artefact-1, artefact-2]
71-
steps:
72-
- name: "Check prerequisites for notification"
73-
id: check
74-
run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT
75-
- name: "Notify on build completion"
76-
if: steps.check.outputs.secret_exist == 'true'
77-
uses: nhs-england-tools/[email protected]
78-
with:
79-
github-token: ${{ secrets.GITHUB_TOKEN }}
80-
teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }}
81-
message-title: "Notification title"
82-
message-text: "This is a notification body"
83-
link: ${{ github.event.pull_request.html_url }}

.github/workflows/stage-4-acceptance.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ jobs:
5353
needs: environment-set-up
5454
timeout-minutes: 10
5555
steps:
56-
- uses: actions/checkout@v4
56+
- name: "Checkout code"
57+
uses: actions/checkout@v4
5758
- name: "Run contract test"
5859
run: |
5960
make test-contract
@@ -65,7 +66,8 @@ jobs:
6566
needs: environment-set-up
6667
timeout-minutes: 10
6768
steps:
68-
- uses: actions/checkout@v4
69+
- name: "Checkout code"
70+
uses: actions/checkout@v4
6971
- name: "Run security test"
7072
run: |
7173
make test-security
@@ -77,7 +79,8 @@ jobs:
7779
needs: environment-set-up
7880
timeout-minutes: 10
7981
steps:
80-
- uses: actions/checkout@v4
82+
- name: "Checkout code"
83+
uses: actions/checkout@v4
8184
- name: "Run UI test"
8285
run: |
8386
make test-ui
@@ -89,7 +92,8 @@ jobs:
8992
needs: environment-set-up
9093
timeout-minutes: 10
9194
steps:
92-
- uses: actions/checkout@v4
95+
- name: "Checkout code"
96+
uses: actions/checkout@v4
9397
- name: "Run UI performance test"
9498
run: |
9599
make test-ui-performance

scripts/docker/docker.lib.sh

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/bin/bash
2+
# shellcheck disable=SC2155
23

34
# WARNING: Please, DO NOT edit this file! It is maintained in the Repository Template (https://github.com/nhs-england-tools/repository-template). Raise a PR instead.
45

@@ -22,6 +23,7 @@ set -euo pipefail
2223
function docker-build() {
2324

2425
local dir=${dir:-$PWD}
26+
2527
_create-effective-dockerfile
2628
version-create-effective-file
2729
docker build \
@@ -35,14 +37,14 @@ function docker-build() {
3537
--build-arg GIT_BRANCH="$(_get-git-branch-name)" \
3638
--build-arg GIT_COMMIT_HASH="$(git rev-parse --short HEAD)" \
3739
--build-arg BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%S%z")" \
38-
--build-arg BUILD_VERSION="$(_get-version)" \
39-
--tag "${DOCKER_IMAGE}:$(_get-version)" \
40+
--build-arg BUILD_VERSION="$(_get-effective-version)" \
41+
--tag "${DOCKER_IMAGE}:$(_get-effective-version)" \
4042
--rm \
4143
--file "${dir}/Dockerfile.effective" \
4244
.
4345
# Tag the image with all the stated versions, see the documentation for more details
44-
for version in $(_get-all-versions) latest; do
45-
docker tag "${DOCKER_IMAGE}:$(_get-version)" "${DOCKER_IMAGE}:${version}"
46+
for version in $(_get-all-effective-versions) latest; do
47+
docker tag "${DOCKER_IMAGE}:$(_get-effective-version)" "${DOCKER_IMAGE}:${version}"
4648
done
4749
docker rmi --force "$(docker images | grep "<none>" | awk '{print $3}')" 2> /dev/null ||:
4850
}
@@ -56,10 +58,11 @@ function docker-build() {
5658
function docker-check-test() {
5759

5860
local dir=${dir:-$PWD}
61+
5962
# shellcheck disable=SC2086,SC2154
6063
docker run --rm --platform linux/amd64 \
6164
${args:-} \
62-
"${DOCKER_IMAGE}:$(_get-version)" 2>/dev/null \
65+
"${DOCKER_IMAGE}:$(_get-effective-version)" 2>/dev/null \
6366
${cmd:-} \
6467
| grep -q "${check}" && echo PASS || echo FAIL
6568
}
@@ -72,10 +75,11 @@ function docker-check-test() {
7275
function docker-run() {
7376

7477
local dir=${dir:-$PWD}
78+
7579
# shellcheck disable=SC2086
7680
docker run --rm --platform linux/amd64 \
7781
${args:-} \
78-
"${DOCKER_IMAGE}:$(dir="$dir" _get-version)" \
82+
"${DOCKER_IMAGE}:$(dir="$dir" _get-effective-version)" \
7983
${cmd:-}
8084
}
8185

@@ -85,8 +89,9 @@ function docker-run() {
8589
function docker-push() {
8690

8791
local dir=${dir:-$PWD}
92+
8893
# Push all the image tags based on the stated versions, see the documentation for more details
89-
for version in $(dir="$dir" _get-all-versions) latest; do
94+
for version in $(dir="$dir" _get-all-effective-versions) latest; do
9095
docker push "${DOCKER_IMAGE}:${version}"
9196
done
9297
}
@@ -97,7 +102,8 @@ function docker-push() {
97102
function docker-clean() {
98103

99104
local dir=${dir:-$PWD}
100-
for version in $(dir="$dir" _get-all-versions) latest; do
105+
106+
for version in $(dir="$dir" _get-all-effective-versions) latest; do
101107
docker rmi "${DOCKER_IMAGE}:${version}" > /dev/null 2>&1 ||:
102108
done
103109
rm -f \
@@ -112,10 +118,12 @@ function docker-clean() {
112118
function version-create-effective-file() {
113119

114120
local dir=${dir:-$PWD}
115-
build_datetime=${BUILD_DATETIME:-$(date -u +'%Y-%m-%dT%H:%M:%S%z')}
116-
if [ -f "$dir/VERSION" ]; then
121+
local version_file="$dir/VERSION"
122+
local build_datetime=${BUILD_DATETIME:-$(date -u +'%Y-%m-%dT%H:%M:%S%z')}
123+
124+
if [ -f "$version_file" ]; then
117125
# shellcheck disable=SC2002
118-
cat "$dir/VERSION" | \
126+
cat "$version_file" | \
119127
sed "s/\(\${yyyy}\|\$yyyy\)/$(date --date="${build_datetime}" -u +"%Y")/g" | \
120128
sed "s/\(\${mm}\|\$mm\)/$(date --date="${build_datetime}" -u +"%m")/g" | \
121129
sed "s/\(\${dd}\|\$dd\)/$(date --date="${build_datetime}" -u +"%d")/g" | \
@@ -132,18 +140,18 @@ function version-create-effective-file() {
132140

133141
# Retrieve the Docker image version from the '.tool-versions' file and pull the
134142
# image if required. This function is to be used in conjunction with the
135-
# external images and it prevents Docker from downloading an image each time it
136-
# is used, since the digest is not stored locally for compressed images. To
137-
# optimise, the solution is to pull the image using its digest and then tag it,
138-
# checking this tag for existence for any subsequent use.
143+
# external images and it prevents Docker from downloading an image each time it
144+
# is used, since the digest is not stored locally for compressed images. To
145+
# optimise, the solution is to pull the image using its digest and then tag it,
146+
# checking this tag for existence for any subsequent use.
139147
# Arguments (provided as environment variables):
140148
# name=[full name of the Docker image]
141149
# shellcheck disable=SC2001
142150
function docker-get-image-version-and-pull() {
143151

144152
# Get the image full version from the '.tool-versions' file
145-
versions_file="$(git rev-parse --show-toplevel)/.tool-versions"
146-
version="latest"
153+
local versions_file="$(git rev-parse --show-toplevel)/.tool-versions"
154+
local version="latest"
147155
if [ -f "$versions_file" ]; then
148156
line=$(grep "docker/${name} " "$versions_file" | sed "s/^#\s*//; s/\s*#.*$//")
149157
[ -n "$line" ] && version=$(echo "$line" | awk '{print $2}')
@@ -155,8 +163,8 @@ function docker-get-image-version-and-pull() {
155163
# version="1.2.3@sha256:hash"
156164
# tag="1.2.3"
157165
# digest="sha256:hash"
158-
tag="$(echo "$version" | sed 's/@.*$//')"
159-
digest="$(echo "$version" | sed 's/^.*@//')"
166+
local tag="$(echo "$version" | sed 's/@.*$//')"
167+
local digest="$(echo "$version" | sed 's/^.*@//')"
160168

161169
# Check if the image exists locally already
162170
if ! docker images | awk '{ print $1 ":" $2 }' | grep "^${name}:${tag}$"; then
@@ -188,6 +196,7 @@ function docker-get-image-version-and-pull() {
188196
function _create-effective-dockerfile() {
189197

190198
local dir=${dir:-$PWD}
199+
191200
cp "${dir}/Dockerfile" "${dir}/Dockerfile.effective"
192201
_replace-image-latest-by-specific-version
193202
_append-metadata
@@ -199,7 +208,10 @@ function _create-effective-dockerfile() {
199208
function _replace-image-latest-by-specific-version() {
200209

201210
local dir=${dir:-$PWD}
202-
versions_file=$(git rev-parse --show-toplevel)/.tool-versions
211+
local versions_file=$(git rev-parse --show-toplevel)/.tool-versions
212+
local dockerfile="${dir}/Dockerfile.effective"
213+
local build_datetime=${BUILD_DATETIME:-$(date -u +'%Y-%m-%dT%H:%M:%S%z')}
214+
203215
if [ -f "$versions_file" ]; then
204216
# First, list the entries specific for Docker to take precedence, then the rest
205217
content=$(grep " docker/" "$versions_file"; grep -v " docker/" "$versions_file")
@@ -208,9 +220,24 @@ function _replace-image-latest-by-specific-version() {
208220
line=$(echo "$line" | sed "s/^#\s*//; s/\s*#.*$//" | sed "s;docker/;;")
209221
name=$(echo "$line" | awk '{print $1}')
210222
version=$(echo "$line" | awk '{print $2}')
211-
sed -i "s;FROM ${name}:latest;FROM ${name}:${version};g" "${dir}/Dockerfile.effective"
223+
sed -i "s;\(FROM .*\) ${name}:latest;\1 ${name}:${version};g" "$dockerfile"
212224
done
213225
fi
226+
227+
if [ -f "$dockerfile" ]; then
228+
# shellcheck disable=SC2002
229+
cat "$dockerfile" | \
230+
sed "s/\(\${yyyy}\|\$yyyy\)/$(date --date="${build_datetime}" -u +"%Y")/g" | \
231+
sed "s/\(\${mm}\|\$mm\)/$(date --date="${build_datetime}" -u +"%m")/g" | \
232+
sed "s/\(\${dd}\|\$dd\)/$(date --date="${build_datetime}" -u +"%d")/g" | \
233+
sed "s/\(\${HH}\|\$HH\)/$(date --date="${build_datetime}" -u +"%H")/g" | \
234+
sed "s/\(\${MM}\|\$MM\)/$(date --date="${build_datetime}" -u +"%M")/g" | \
235+
sed "s/\(\${SS}\|\$SS\)/$(date --date="${build_datetime}" -u +"%S")/g" | \
236+
sed "s/\(\${hash}\|\$hash\)/$(git rev-parse --short HEAD)/g" \
237+
> "$dockerfile.tmp"
238+
mv "$dockerfile.tmp" "$dockerfile"
239+
fi
240+
214241
# Do not ignore the issue if 'latest' is used in the effective image
215242
sed -Ei "/# hadolint ignore=DL3007$/d" "${dir}/Dockerfile.effective"
216243
}
@@ -221,6 +248,7 @@ function _replace-image-latest-by-specific-version() {
221248
function _append-metadata() {
222249

223250
local dir=${dir:-$PWD}
251+
224252
cat \
225253
"$dir/Dockerfile.effective" \
226254
"$(git rev-parse --show-toplevel)/scripts/docker/Dockerfile.metadata" \
@@ -231,26 +259,29 @@ function _append-metadata() {
231259
# Print top Docker image version.
232260
# Arguments (provided as environment variables):
233261
# dir=[path to the image directory where the Dockerfile is located, default is '.']
234-
function _get-version() {
262+
function _get-effective-version() {
235263

236264
local dir=${dir:-$PWD}
265+
237266
head -n 1 "${dir}/.version" 2> /dev/null ||:
238267
}
239268

240269
# Print all Docker image versions.
241270
# Arguments (provided as environment variables):
242271
# dir=[path to the image directory where the Dockerfile is located, default is '.']
243-
function _get-all-versions() {
272+
function _get-all-effective-versions() {
244273

245274
local dir=${dir:-$PWD}
275+
246276
cat "${dir}/.version" 2> /dev/null ||:
247277
}
248278

249279
# Print Git branch name. Check the GitHub variables first and then the local Git
250-
# repo.
280+
# repo.
251281
function _get-git-branch-name() {
252282

253-
branch_name=$(git rev-parse --abbrev-ref HEAD)
283+
local branch_name=$(git rev-parse --abbrev-ref HEAD)
284+
254285
if [ -n "${GITHUB_HEAD_REF:-}" ]; then
255286
branch_name=$GITHUB_HEAD_REF
256287
elif [ -n "${GITHUB_REF:-}" ]; then

scripts/docker/docker.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
docker-build: # Build Docker image - optional: dir=[path to the Dockerfile to use, default is '.']
88
make _docker cmd="build"
99

10+
docker-push: # Push Docker image - optional: dir=[path to the image directory where the Dockerfile is located, default is '.']
11+
make _docker cmd="push"
12+
1013
clean:: # Remove Docker resources - optional: dir=[path to the image directory where the Dockerfile is located, default is '.']
1114
make _docker cmd="clean"
1215

@@ -68,5 +71,6 @@ ${VERBOSE}.SILENT: \
6871
docker-example-clean \
6972
docker-example-lint \
7073
docker-example-run \
74+
docker-push \
7175
docker-shellscript-lint \
7276
docker-test-suite-run \

scripts/docker/tests/docker.test.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ function main() {
3232
test-docker-run \
3333
test-docker-clean \
3434
)
35-
status=0
35+
local status=0
3636
for test in "${tests[@]}"; do
3737
{
3838
echo -n "$test"
39+
# shellcheck disable=SC2015
3940
$test && echo " PASS" || { echo " FAIL"; ((status++)); }
4041
}
4142
done
@@ -65,7 +66,7 @@ function test-docker-build() {
6566
# Act
6667
docker-build > /dev/null 2>&1
6768
# Assert
68-
docker image inspect "${DOCKER_IMAGE}:$(_get-version)" > /dev/null 2>&1 && return 0 || return 1
69+
docker image inspect "${DOCKER_IMAGE}:$(_get-effective-version)" > /dev/null 2>&1 && return 0 || return 1
6970
}
7071

7172
function test-docker-version() {
@@ -75,6 +76,7 @@ function test-docker-version() {
7576
# Act
7677
version-create-effective-file
7778
# Assert
79+
# shellcheck disable=SC2002
7880
(
7981
cat .version | grep -q "20230904-" &&
8082
cat .version | grep -q "2023.09.04-" &&
@@ -106,7 +108,7 @@ function test-docker-run() {
106108
function test-docker-clean() {
107109

108110
# Arrange
109-
version="$(_get-version)"
111+
version="$(_get-effective-version)"
110112
# Act
111113
docker-clean
112114
# Assert

scripts/shellscript-linter.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ function docker-run-shellcheck() {
4646
# shellcheck disable=SC1091
4747
source ./scripts/docker/docker.lib.sh
4848

49-
image=$(name=koalaman/shellcheck docker-get-image-version-and-pull)
49+
# shellcheck disable=SC2155
50+
local image=$(name=koalaman/shellcheck docker-get-image-version-and-pull)
5051
# shellcheck disable=SC2001
5152
docker run --rm --platform linux/amd64 \
5253
--volume "$PWD:/workdir" \

0 commit comments

Comments
 (0)