Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
f19fc9c
build dev container
anthony-nhs Oct 22, 2025
4e3d8af
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Oct 22, 2025
5666353
build the dev container
anthony-nhs Oct 22, 2025
9dcc3f2
build macos on seperate image
anthony-nhs Oct 22, 2025
3872a50
install docker on macos
anthony-nhs Oct 22, 2025
34a511a
try colima
anthony-nhs Oct 22, 2025
1b19e00
try lima
anthony-nhs Oct 22, 2025
cbea80e
build on ubuntu
anthony-nhs Oct 22, 2025
be59973
upload image
anthony-nhs Oct 22, 2025
accee98
correct dev container
anthony-nhs Oct 22, 2025
61fa254
try cross compile again
anthony-nhs Oct 22, 2025
8d18a9d
try pushing image
anthony-nhs Oct 22, 2025
2ced8e0
add id_token permissions
anthony-nhs Oct 22, 2025
d01fe8a
use a different role
anthony-nhs Oct 22, 2025
3cf0087
checkout scanning
anthony-nhs Oct 22, 2025
bfcc986
move scan earlier
anthony-nhs Oct 22, 2025
c02c04e
set account id
anthony-nhs Oct 22, 2025
186b451
upgrade node
anthony-nhs Oct 22, 2025
82f6f08
upgrade to node 22
anthony-nhs Oct 22, 2025
f84df0a
tag latest
anthony-nhs Oct 22, 2025
3c4ada4
trigger build
anthony-nhs Oct 22, 2025
a027a79
fix check python licences
anthony-nhs Oct 22, 2025
b74c25e
remove python licence check
anthony-nhs Oct 22, 2025
67a7108
checkout local scripts
anthony-nhs Oct 22, 2025
630f5f5
checkout for arm64
anthony-nhs Oct 22, 2025
0fdd658
use action locations
anthony-nhs Oct 22, 2025
491b0ed
debug
anthony-nhs Oct 22, 2025
b059f46
another try
anthony-nhs Oct 22, 2025
bbd7f8e
download script
anthony-nhs Oct 22, 2025
ccbdfda
fix path
anthony-nhs Oct 22, 2025
dc4ff92
fix problems
anthony-nhs Oct 22, 2025
ddc04e4
use hash
anthony-nhs Oct 22, 2025
8c2c53c
apply latest tag
anthony-nhs Oct 22, 2025
c16e098
fix name
anthony-nhs Oct 22, 2025
0f8dc72
appy tag to image
anthony-nhs Oct 23, 2025
4662310
build nhsd_git_secrets
anthony-nhs Oct 23, 2025
c7d49db
correct build
anthony-nhs Oct 23, 2025
6ddd57d
try to not push
anthony-nhs Oct 23, 2025
03a0bd7
use different commands to tag
anthony-nhs Oct 23, 2025
61ae19f
tag image a different way
anthony-nhs Oct 23, 2025
14ca962
tag in right place
anthony-nhs Oct 24, 2025
a7af104
remove unused file
anthony-nhs Oct 24, 2025
88acd30
update readme
anthony-nhs Oct 24, 2025
fe6a798
more updates to readme
anthony-nhs Oct 24, 2025
cd69436
update readme
anthony-nhs Oct 24, 2025
f99b3f5
update readme
anthony-nhs Oct 24, 2025
7a7a5bd
trigger build
anthony-nhs Oct 24, 2025
fc891d8
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Oct 24, 2025
f19a2b8
use local script for build git secrets
anthony-nhs Oct 24, 2025
66ee78b
better cfn guard init
anthony-nhs Oct 24, 2025
87c1df8
Merge branch 'main' into dev_container_build
anthony-nhs Oct 28, 2025
bb609cd
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Nov 12, 2025
7d1908d
changes following feedback
anthony-nhs Nov 12, 2025
63f1dbe
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Nov 21, 2025
1081c77
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Nov 21, 2025
ca0b67d
add it back in
anthony-nhs Nov 21, 2025
78af61a
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Nov 21, 2025
3bdce19
fix ecr name
anthony-nhs Nov 21, 2025
e048827
use correct path
anthony-nhs Nov 21, 2025
1e06c91
do not need input
anthony-nhs Nov 21, 2025
0d90bb4
Revert "do not need input"
anthony-nhs Nov 21, 2025
229d9dc
Revert "use correct path"
anthony-nhs Nov 21, 2025
e233a8a
use correct ecr
anthony-nhs Nov 22, 2025
98b1b95
Merge remote-tracking branch 'origin/main' into dev_container_build
anthony-nhs Nov 22, 2025
44746cc
add workflow to push to github
anthony-nhs Nov 25, 2025
fcdd04d
call publish to github
anthony-nhs Nov 25, 2025
4a14f7d
common workflow for build and push
anthony-nhs Nov 25, 2025
7e18204
fix image name
anthony-nhs Nov 25, 2025
74e04d1
fix step name
anthony-nhs Nov 25, 2025
8b6283c
force image name to lowercase
anthony-nhs Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu

# provide DOCKER_GID via build args if you need to force group id to match host
ARG DOCKER_GID
ARG TARGETARCH
ENV TARGETARCH=${TARGETARCH}

ARG ASDF_VERSION
COPY .tool-versions.asdf /tmp/.tool-versions.asdf

# specify DOCKER_GID to force container docker group id to match host
RUN if [ -n "${DOCKER_GID}" ]; then \
Expand All @@ -27,12 +32,35 @@ RUN apt-get update \
libreadline-dev libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev liblzma-dev netcat-traditional libyaml-dev

USER vscode
# Download correct AWS CLI for arch
RUN if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then \
wget -O /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip"; \
else \
wget -O /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"; \
fi && \
unzip /tmp/awscliv2.zip -d /tmp/aws-cli && \
/tmp/aws-cli/aws/install && \
rm /tmp/awscliv2.zip && rm -rf /tmp/aws-cli

# Download correct SAM CLI for arch
RUN if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then \
wget -O /tmp/aws-sam-cli.zip "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-arm64.zip"; \
else \
wget -O /tmp/aws-sam-cli.zip "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip"; \
fi && \
unzip /tmp/aws-sam-cli.zip -d /tmp/aws-sam-cli && \
/tmp/aws-sam-cli/install && \
rm /tmp/aws-sam-cli.zip && rm -rf /tmp/aws-sam-cli

# Install ASDF
RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.3 && \
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc && \
echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc
RUN ASDF_VERSION=$(awk '!/^#/ && NF {print $1; exit}' /tmp/.tool-versions.asdf) && \
if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" = "aarch64" ]; then \
wget -O /tmp/asdf.tar.gz https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-arm64.tar.gz; \
else \
wget -O /tmp/asdf.tar.gz https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-amd64.tar.gz; \
fi && \
tar -xvzf /tmp/asdf.tar.gz && \
mv asdf /usr/bin

ENV PATH="$PATH:/home/vscode/.asdf/bin/:/workspaces/eps-prescription-tracker-ui/node_modules/.bin:/workspaces/eps-common-workflows/.venv/bin"

Expand All @@ -49,5 +77,4 @@ ADD .tool-versions /workspaces/eps-common-workflows/.tool-versions
ADD .tool-versions /home/vscode/.tool-versions

RUN asdf install python && \
asdf install && \
asdf reshim nodejs
asdf install
1 change: 1 addition & 0 deletions .gitallowed
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
token: ?"?\$\{\{\s*secrets\.GITHUB_TOKEN\s*\}\}"?
.*\.gitallowed.*
id-token: write
password: \${{ secrets\.GITHUB_TOKEN }}
138 changes: 138 additions & 0 deletions .github/scripts/check_ecr_image_scan_results.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env bash
set -e

AWS_MAX_ATTEMPTS=20
export AWS_MAX_ATTEMPTS

if [ -z "${REPOSITORY_NAME}" ]; then
echo "REPOSITORY_NAME not set"
exit 1
fi

if [ -z "${IMAGE_TAG}" ]; then
echo "IMAGE_TAG not set"
exit 1
fi

if [ -z "${AWS_REGION}" ]; then
echo "AWS_REGION not set"
exit 1
fi

if [ -z "${ACCOUNT_ID}" ]; then
echo "ACCOUNT_ID not set"
exit 1
fi

IMAGE_DIGEST=$(aws ecr describe-images \
--repository-name "$REPOSITORY_NAME" \
--image-ids imageTag="$IMAGE_TAG" \
--query 'imageDetails[0].imageDigest' \
--output text)

RESOURCE_ARN="arn:aws:ecr:${AWS_REGION}:${ACCOUNT_ID}:repository/${REPOSITORY_NAME}/${IMAGE_DIGEST}"

echo "Monitoring scan for ${REPOSITORY_NAME}:${IMAGE_TAG}"
echo "Resource ARN: ${RESOURCE_ARN}"
echo

# Wait for ECR scan to reach COMPLETE
STATUS=""
echo "Waiting for ECR scan to complete..."
for i in {1..30}; do
echo "Checking scan status. Attempt ${i}"
STATUS=$(aws ecr describe-image-scan-findings \
--repository-name "$REPOSITORY_NAME" \
--image-id imageDigest="$IMAGE_DIGEST" \
--query 'imageScanStatus.status' \
--output text 2>/dev/null || echo "NONE")

if [[ "$STATUS" == "COMPLETE" ]]; then
echo "ECR scan completed."
break
fi

if [[ "$STATUS" == "FAILED" ]]; then
echo "Scan failed."
exit 1
fi

echo "SCAN IS NOT YET COMPLETE. Waiting 10 seconds before checking again..."
sleep 10
done

if [[ "$STATUS" != "COMPLETE" ]]; then
echo "Timeout waiting for ECR scan to complete."
exit 1
fi

# Wait for Inspector2 findings to appear & stabilize
# this is in place as scan may show as complete but findings have not yet stabilize
echo
echo "Waiting for Inspector2 findings to stabilize..."

PREV_HASH=""
for i in {1..12}; do # ~2 minutes max
FINDINGS=$(aws inspector2 list-findings \
--filter-criteria "{
\"resourceId\": [{\"comparison\": \"EQUALS\", \"value\": \"${RESOURCE_ARN}\"}],
\"findingStatus\": [{\"comparison\": \"EQUALS\", \"value\": \"ACTIVE\"}]
}" \
--output json 2>/dev/null || echo "{}")

CURR_HASH=$(echo "$FINDINGS" | sha256sum)
COUNT=$(echo "$FINDINGS" | jq '.findings | length')

if [[ "$COUNT" -gt 0 && "$CURR_HASH" == "$PREV_HASH" ]]; then
echo "Findings stabilized ($COUNT findings)."
break
fi

PREV_HASH="$CURR_HASH"
echo "Attempt: ${i}. Still waiting... (${COUNT} findings so far)"
sleep 10
done

# Extract counts and display findings
echo
echo "Final Inspector2 findings with suppressions removed:"
echo

echo "$FINDINGS" | jq '{
findings: [
.findings[]? | {
severity: .severity,
title: .title,
package: .packageVulnerabilityDetails.vulnerablePackages[0].name,
sourceUrl: .packageVulnerabilityDetails.sourceUrl,
recommendation: (.remediation.recommendation.text // "N/A")
}
]
}'

echo

# Check for critical/high severity
CRITICAL_COUNT=$(echo "$FINDINGS" | jq '[.findings[]? | select(.severity=="CRITICAL")] | length')
HIGH_COUNT=$(echo "$FINDINGS" | jq '[.findings[]? | select(.severity=="HIGH")] | length')

if (( CRITICAL_COUNT > 0 || HIGH_COUNT > 0 )); then
echo "${CRITICAL_COUNT} CRITICAL and ${HIGH_COUNT} HIGH vulnerabilities detected!"
echo
echo "Critical/High vulnerabilities:"
echo "$FINDINGS" | jq -r '
.findings[]? |
select(.severity=="CRITICAL" or .severity=="HIGH") |{
severity: .severity,
title: .title,
package: .packageVulnerabilityDetails.vulnerablePackages[0].name,
sourceUrl: .packageVulnerabilityDetails.sourceUrl,
recommendation: (.remediation.recommendation.text // "N/A")
}'
echo
echo "Failing pipeline due to Critical/High vulnerabilities."
exit 2
else
echo "No Critical or High vulnerabilities found."
exit 0
fi
184 changes: 184 additions & 0 deletions .github/workflows/build_and_push_docker_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Build and push docker image

on:
workflow_call:
secrets:
PUSH_IMAGE_ROLE:
required: true
inputs:
container_ecr:
type: string
description: "The name of the ECR repository to push the container image to."
required: true
container_image_tag:
type: string
description: "The tag to use for the container image."
required: true
docker_file:
type: string
description: "The Dockerfile to use for building the container image."
required: true
check_ecr_image_scan_results_script_tag:
type: string
description: "The tag to download check_ecr_image_scan_results.sh script."
required: false
default: "dev_container_build"

jobs:
build_image_amd64:
permissions:
id-token: write
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Build container
run: |
docker build -f "${DOCKER_FILE}" -t amd64-image .
env:
DOCKER_FILE: ${{ inputs.docker_file }}

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8
id: connect-aws-deploy
with:
aws-region: eu-west-2
role-to-assume: ${{ secrets.PUSH_IMAGE_ROLE }}
role-session-name: dev-container-build-amd64
output-credentials: true

- name: Retrieve AWS Account ID
id: retrieve-deploy-account-id
run: |
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "account_id=$ACCOUNT_ID" >> "$GITHUB_OUTPUT"

- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ steps.retrieve-deploy-account-id.outputs.account_id }}.dkr.ecr.eu-west-2.amazonaws.com

- name: Push amd64 image to Amazon ECR
env:
ECR_REPOSITORY: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
run: |
docker tag "amd64-image" "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-amd64"
docker push "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-amd64"

- name: Check dev container scan results
env:
REPOSITORY_NAME: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}-amd64
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
SCRIPT_TAG: ${{ inputs.check_ecr_image_scan_results_script_tag }}
run: |
curl -L "https://raw.githubusercontent.com/NHSDigital/eps-common-workflows/refs/heads/${SCRIPT_TAG}/.github/scripts/check_ecr_image_scan_results.sh" -o /tmp/check_ecr_image_scan_results.sh
chmod +x /tmp/check_ecr_image_scan_results.sh
sleep 30
/tmp/check_ecr_image_scan_results.sh

build_image_arm64:
permissions:
id-token: write
runs-on: ubuntu-22.04-arm
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Build container
run: |
docker build -f "${DOCKER_FILE}" -t arm64-image .
env:
DOCKER_FILE: ${{ inputs.docker_file }}

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8
id: connect-aws-deploy
with:
aws-region: eu-west-2
role-to-assume: ${{ secrets.PUSH_IMAGE_ROLE }}
role-session-name: dev-container-build-arm64
output-credentials: true

- name: Retrieve AWS Account ID
id: retrieve-deploy-account-id
run: |
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "account_id=$ACCOUNT_ID" >> "$GITHUB_OUTPUT"

- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ steps.retrieve-deploy-account-id.outputs.account_id }}.dkr.ecr.eu-west-2.amazonaws.com

- name: Push ARM64 image to Amazon ECR
env:
ECR_REPOSITORY: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
run: |
docker tag "arm64-image" "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-arm64"
docker push "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-arm64"
- name: Check dev container scan results
env:
REPOSITORY_NAME: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}-arm64
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
SCRIPT_TAG: ${{ inputs.check_ecr_image_scan_results_script_tag }}
run: |
curl -L "https://raw.githubusercontent.com/NHSDigital/eps-common-workflows/refs/heads/${SCRIPT_TAG}/.github/scripts/check_ecr_image_scan_results.sh" -o /tmp/check_ecr_image_scan_results.sh
chmod +x /tmp/check_ecr_image_scan_results.sh
sleep 30
/tmp/check_ecr_image_scan_results.sh

create_multi_arch_manifest:
permissions:
id-token: write
runs-on: ubuntu-22.04
needs: [build_image_amd64, build_image_arm64]
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8
with:
aws-region: eu-west-2
role-to-assume: ${{ secrets.PUSH_IMAGE_ROLE }}
role-session-name: multi-arch-manifest
output-credentials: true

- name: Retrieve AWS Account ID
id: retrieve-deploy-account-id
run: |
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "account_id=$ACCOUNT_ID" >> "$GITHUB_OUTPUT"

- name: Login to Amazon ECR
run: |
aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${{ steps.retrieve-deploy-account-id.outputs.account_id }}.dkr.ecr.eu-west-2.amazonaws.com

- name: Create and push multi-architecture manifest for tag
env:
ECR_REPOSITORY: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
run: |
# Create manifest list combining both architectures
docker buildx imagetools create -t "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}" \
"${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-amd64" \
"${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}-arm64"

- name: Verify multi-architecture manifest
env:
ECR_REPOSITORY: ${{ inputs.container_ecr }}
IMAGE_TAG: ${{ inputs.container_image_tag }}
ACCOUNT_ID: ${{ steps.retrieve-deploy-account-id.outputs.account_id }}
run: |
echo "=== Verifying multi-architecture manifest ==="
docker buildx imagetools inspect "${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/${ECR_REPOSITORY}:${IMAGE_TAG}"
Loading