From 440d22007c56f46ceffdc0fbe085db875b9eb4c9 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Tue, 14 Apr 2026 15:46:52 +0000 Subject: [PATCH 01/15] fix build --- .github/workflows/build_multi_arch_image.yml | 1 + .github/workflows/ci.yml | 4 ++ .github/workflows/pull_request.yml | 4 ++ .github/workflows/release.yml | 4 ++ Makefile | 11 ++++- .../.devcontainer/Dockerfile | 2 + .../.devcontainer/Dockerfile.tflint | 13 +++++ .../.devcontainer/scripts/install_tflint.sh | 48 +++++++++++++++++++ .../.devcontainer/scripts/root_install.sh | 2 +- 9 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint create mode 100755 src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index eaaac733..2684c278 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -92,6 +92,7 @@ jobs: BASE_FOLDER: "${{ inputs.base_folder }}" NO_CACHE: '${{ inputs.NO_CACHE }}' BUILDX_NO_DEFAULT_ATTESTATIONS: "1" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check docker vulnerabilities - json output run: | make scan-image-json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dfc278e..c0238585 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,10 @@ jobs: uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f2d4d6942115472d3f08316cd25f400b02a9dc69 needs: - get_config_values + permissions: + contents: read + packages: read + id-token: write with: pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} secrets: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b2baa767..f0be6f4b 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -28,6 +28,10 @@ jobs: - get_config_values with: pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + permissions: + contents: read + packages: read + id-token: write secrets: SONAR_TOKEN: '${{ secrets.SONAR_TOKEN }}' pr_title_format_check: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e0de532..9518633b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,10 @@ jobs: uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f2d4d6942115472d3f08316cd25f400b02a9dc69 needs: - get_config_values + permissions: + contents: read + packages: read + id-token: write with: pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} secrets: diff --git a/Makefile b/Makefile index e2aed6a9..8b46c9e6 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ build-node-24-python-3-14-image: CONTAINER_NAME=node_24_python_3_14 BASE_VERSION_TAG=local-build BASE_FOLDER=languages IMAGE_TAG=local-build $(MAKE) build-image build-eps-storage-terraform-image: - CONTAINER_NAME=eps_storage_terraform BASE_VERSION_TAG=local-build BASE_FOLDER=projects IMAGE_TAG=local-build $(MAKE) build-image + CONTAINER_NAME=eps-storage-terraform BASE_VERSION_TAG=local-build BASE_FOLDER=projects IMAGE_TAG=local-build $(MAKE) build-image build-eps-data-extract-image: CONTAINER_NAME=eps_data_extract BASE_VERSION_TAG=local-build BASE_FOLDER=projects IMAGE_TAG=local-build $(MAKE) build-image @@ -72,7 +72,14 @@ build-grype: build-grant: docker build -f src/base/.devcontainer/Dockerfile.grant --tag local_grant:latest src/base/.devcontainer/ -build-image: build-syft build-grype build-grant guard-CONTAINER_NAME guard-BASE_VERSION_TAG guard-BASE_FOLDER guard-IMAGE_TAG +build-tflint: + docker buildx build \ + --secret id=GH_TOKEN,env=GITHUB_TOKEN \ + -f src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint \ + --tag local_tflint:latest \ + src/projects/eps-storage-terraform/.devcontainer/ + +build-image: build-syft build-grype build-grant build-tflint guard-CONTAINER_NAME guard-BASE_VERSION_TAG guard-BASE_FOLDER guard-IMAGE_TAG workspace_folder="$${CONTAINER_NAME}"; \ case "$${CONTAINER_NAME}" in \ eps_*) workspace_folder="$$(printf '%s' "$${CONTAINER_NAME}" | tr '_' '-')" ;; \ diff --git a/src/projects/eps-storage-terraform/.devcontainer/Dockerfile b/src/projects/eps-storage-terraform/.devcontainer/Dockerfile index 8226af24..c64245dd 100644 --- a/src/projects/eps-storage-terraform/.devcontainer/Dockerfile +++ b/src/projects/eps-storage-terraform/.devcontainer/Dockerfile @@ -1,6 +1,7 @@ ARG BASE_VERSION_TAG=latest ARG BASE_IMAGE=ghcr.io/nhsdigital/eps-devcontainers/node_24_python_3_13:${BASE_VERSION_TAG} +FROM local_tflint:latest AS tflint-build FROM ${BASE_IMAGE} ARG SCRIPTS_DIR=/usr/local/share/eps @@ -26,6 +27,7 @@ USER root COPY --chmod=755 scripts ${SCRIPTS_DIR}/${CONTAINER_NAME} WORKDIR ${SCRIPTS_DIR}/${CONTAINER_NAME} RUN ./root_install.sh +COPY --from=tflint-build /tflint /usr/local/bin/tflint USER vscode diff --git a/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint b/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint new file mode 100644 index 00000000..d6a1e223 --- /dev/null +++ b/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint @@ -0,0 +1,13 @@ +FROM serversideup/github-cli:2.89.0 AS build +ARG TARGETARCH +ARG TFLINT_VERSION="v0.61.0" +COPY --chmod=755 scripts/install_tflint.sh /tmp/install_tflint.sh +RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \ + INSTALL_DIR=/tmp/tflint/ \ + ARCH="${TARGETARCH}" \ + VERSION="${TFLINT_VERSION}" \ + /tmp/install_tflint.sh + +FROM scratch +COPY --from=build /tmp/tflint/tflint /tflint +ENTRYPOINT ["/tflint"] diff --git a/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh b/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh new file mode 100755 index 00000000..1f62116e --- /dev/null +++ b/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -euo pipefail +export DEBIAN_FRONTEND=noninteractive + +DEFAULT_INSTALL_DIR="/usr/local/bin" +INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" + +case "${TARGETARCH:-}" in + amd64|arm64) + TFLINT_ARCH="${TARGETARCH}" + ;; + *) + echo "Unsupported or missing TARGETARCH: '${TARGETARCH:-}'" + echo "Expected one of: amd64, arm64" + exit 1 + ;; +esac + +if ! command -v curl >/dev/null 2>&1 || ! command -v unzip >/dev/null 2>&1; then + apt-get update + apt-get install -y --no-install-recommends curl unzip ca-certificates +fi + +if ! command -v gh >/dev/null 2>&1; then + echo "GitHub CLI (gh) is required for attestation verification but was not found" + exit 1 +fi + +TFLINT_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}/tflint_linux_${TFLINT_ARCH}.zip" +TFLINT_ASSET_NAME="tflint_linux_${TFLINT_ARCH}.zip" +CHECKSUMS_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}/checksums.txt" +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT + +curl -fsSL "${CHECKSUMS_URL}" -o "${tmp_dir}/checksums.txt" +gh attestation verify "${tmp_dir}/checksums.txt" -R terraform-linters/tflint + +curl -fsSL "${TFLINT_URL}" -o "${tmp_dir}/${TFLINT_ASSET_NAME}" +( + cd "${tmp_dir}" + sha256sum --ignore-missing -c checksums.txt +) + +unzip -q "${tmp_dir}/${TFLINT_ASSET_NAME}" -d "${tmp_dir}" + +mkdir -p "$INSTALL_DIR" +install -m 0755 "$tmp_dir/tflint" "${INSTALL_DIR}/tflint" diff --git a/src/projects/eps-storage-terraform/.devcontainer/scripts/root_install.sh b/src/projects/eps-storage-terraform/.devcontainer/scripts/root_install.sh index 474c45b0..0201843b 100755 --- a/src/projects/eps-storage-terraform/.devcontainer/scripts/root_install.sh +++ b/src/projects/eps-storage-terraform/.devcontainer/scripts/root_install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +set -euo pipefail # clean up apt-get clean From f5d589c04722675d4f5d91b90058b4553dfe9d6a Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Tue, 14 Apr 2026 15:59:57 +0000 Subject: [PATCH 02/15] get rid of 3rd party action --- .github/workflows/build_multi_arch_image.yml | 91 +++++++++++++++++--- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index 2684c278..9e7a754d 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -41,19 +41,84 @@ jobs: - arch: arm64 runner: ubuntu-22.04-arm steps: - - name: Free Disk Space for Docker - uses: endersonmenezes/free-disk-space@7901478139cff6e9d44df5972fd8ab8fcade4db1 - with: - remove_android: true - remove_dotnet: true - remove_haskell: true - remove_tool_cache: true - rm_cmd: rm - remove_packages: >- - azure-cli google-cloud-cli microsoft-edge-stable - google-chrome-stable firefox postgresql* temurin-* *llvm* mysql* - dotnet-sdk-* - remove_packages_one_command: true + - name: remove android + run: | + df -h + echo "Freeing up disk space for docker build..." + + rm -rf /usr/local/lib/android || true + rm -rf /opt/android || true + rm -rf /usr/local/android-sdk || true + rm -rf /home/runner/Android || true + + # Remove Android packages if they exist + ANDROID_PACKAGES=$(dpkg -l | grep -E "^ii.*(android|adb)" | awk '{print $2}' | tr '\n' ' ' || true) + if [[ -n "${ANDROID_PACKAGES}" ]]; then + echo "Removing Android packages: ${ANDROID_PACKAGES}" + sudo apt-get remove -y "${ANDROID_PACKAGES}" --fix-missing > /dev/null 2>&1 || true + sudo apt-get autoremove -y > /dev/null 2>&1 || true + sudo apt-get clean > /dev/null 2>&1 || true + fi + df -h + - name: remove haskell + run: | + df -h + echo "Freeing up disk space for docker build..." + + rm -rf /opt/ghc || true + rm -rf /usr/local/.ghcup || true + rm -rf /opt/cabal || true + rm -rf /home/runner/.ghcup || true + rm -rf /home/runner/.cabal || true + + # Remove Haskell packages if they exist + HASKELL_PACKAGES=$(dpkg -l | grep -E "^ii.*(ghc|haskell|cabal)" | awk '{print $2}' | tr '\n' ' ' || true) + if [[ -n "${HASKELL_PACKAGES}" ]]; then + echo "Removing Haskell packages: ${HASKELL_PACKAGES}" + sudo apt-get remove -y "${HASKELL_PACKAGES}" --fix-missing > /dev/null 2>&1 || true + sudo apt-get autoremove -y > /dev/null 2>&1 || true + sudo apt-get clean > /dev/null 2>&1 || true + fi + + df -h + - name: remove haskell + run: | + df -h + echo "Freeing up disk space for docker build..." + + # Remove .NET installation directories + rm -rf /usr/share/dotnet || true + + # Remove .NET documentation directories + rm -rf /usr/share/doc/dotnet-* || true + + # Remove .NET packages if they exist + DOTNET_PACKAGES=$(dpkg -l | grep -E "^ii.*dotnet" | awk '{print $2}' | tr '\n' ' ' || true) + if [[ -n "${DOTNET_PACKAGES}" ]]; then + echo "Removing .NET packages: ${DOTNET_PACKAGES}" + sudo apt-get remove -y "${DOTNET_PACKAGES}" --fix-missing > /dev/null 2>&1 || true + sudo apt-get autoremove -y > /dev/null 2>&1 || true + sudo apt-get clean > /dev/null 2>&1 || true + fi + df -h + - name: remove packages + run: | + df -h + echo "Freeing up disk space for docker build..." + + # Remove specific packages if they exist + PACKAGES_TO_REMOVE="azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox postgresql* temurin-* *llvm* mysql* dotnet-sdk-*" + for pkg in $PACKAGES_TO_REMOVE; do + if dpkg -l | grep -q "^ii.*${pkg}"; then + echo "Removing package: ${pkg}" + sudo apt-get remove -y "${pkg}" --fix-missing > /dev/null 2>&1 || true + fi + done + + sudo apt-get autoremove -y > /dev/null 2>&1 || true + sudo apt-get clean > /dev/null 2>&1 || true + + df -h - name: Login to github container registry uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with: From 4e9d9828f9e6f1b6bee859564ce0c44309960d3a Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:05:54 +0000 Subject: [PATCH 03/15] less permissions --- .github/workflows/build_all_images.yml | 38 ++++++++++++++++++++ .github/workflows/build_multi_arch_image.yml | 23 ++++++++++-- Makefile | 32 ++++++++++++----- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 8887abdf..07eb5e9f 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -39,6 +39,44 @@ jobs: echo "projects=$project_folders" } >> "$GITHUB_OUTPUT" + build_tool_images: + runs-on: ubuntu-latest + permissions: + contents: read + attestations: read + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + fetch-depth: 0 + persist-credentials: false + - name: build_grype + run: + make build-grype + docker save "local_grype:latest" -o grype_image.tar + - name: build_syft + run: + make build-syft + docker save "local_syft:latest" -o syft_image.tar + - name: build_grant + run: + make build-grant + docker save "local_grant:latest" -o grant_image.tar + + - name: build_tflint + run: + make build-tflint + docker save "local_tflint:latest" -o tflint_image.tar + + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f + name: Upload docker images + with: + name: docker_artifact + path: | + grype_image.tar + syft_image.tar + grant_image.tar + tflint_image.tar package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml with: diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index 9e7a754d..43357d18 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -134,11 +134,30 @@ jobs: uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f with: node-version: '24.14.0' + - name: docker_artifact download + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: docker_artifact + path: images/ + - name: extract docker images + run: | + for image in images/*.tar; do + docker load -i "$image" + done + rm -rf images - name: setup syft and grype run: | mkdir -p "$RUNNER_TEMP/bin" - docker build --output="$RUNNER_TEMP/bin" -f "src/base/.devcontainer/Dockerfile.syft" src/base/.devcontainer/ - docker build --output="$RUNNER_TEMP/bin" -f "src/base/.devcontainer/Dockerfile.grype" src/base/.devcontainer/ + id=$(docker create local_grype:latest) + docker cp "$id":/grype - | tar -xOf - grype > "$RUNNER_TEMP/bin/grype" + chmod +x "$RUNNER_TEMP/bin/grype" + docker rm -v "$id" + + mkdir -p "$RUNNER_TEMP/bin" + id=$(docker create local_syft:latest) + docker cp "$id":/syft - | tar -xOf - syft > "$RUNNER_TEMP/bin/syft" + chmod +x "$RUNNER_TEMP/bin/syft" + docker rm -v "$id" echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH" - name: make install run: | diff --git a/Makefile b/Makefile index 8b46c9e6..7bcf9adf 100644 --- a/Makefile +++ b/Makefile @@ -65,19 +65,35 @@ build-all: build-base-image build-node-24-image build-node-24-python-3-10-image build-regression-tests-image build-syft: - docker build -f src/base/.devcontainer/Dockerfile.syft --tag local_syft:latest src/base/.devcontainer/ + @if docker image inspect local_syft:latest >/dev/null 2>&1; then \ + echo "Image local_syft:latest already exists. Skipping build."; \ + else \ + docker build -f src/base/.devcontainer/Dockerfile.syft --tag local_syft:latest src/base/.devcontainer/; \ + fi build-grype: - docker build -f src/base/.devcontainer/Dockerfile.grype --tag local_grype:latest src/base/.devcontainer/ + @if docker image inspect local_grype:latest >/dev/null 2>&1; then \ + echo "Image local_grype:latest already exists. Skipping build."; \ + else \ + docker build -f src/base/.devcontainer/Dockerfile.grype --tag local_grype:latest src/base/.devcontainer/; \ + fi build-grant: - docker build -f src/base/.devcontainer/Dockerfile.grant --tag local_grant:latest src/base/.devcontainer/ + @if docker image inspect local_grant:latest >/dev/null 2>&1; then \ + echo "Image local_grant:latest already exists. Skipping build."; \ + else \ + docker build -f src/base/.devcontainer/Dockerfile.grant --tag local_grant:latest src/base/.devcontainer/; \ + fi build-tflint: - docker buildx build \ - --secret id=GH_TOKEN,env=GITHUB_TOKEN \ - -f src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint \ - --tag local_tflint:latest \ - src/projects/eps-storage-terraform/.devcontainer/ + @if docker image inspect local_tflint:latest >/dev/null 2>&1; then \ + echo "Image local_tflint:latest already exists. Skipping build."; \ + else \ + docker buildx build \ + --secret id=GH_TOKEN,env=GITHUB_TOKEN \ + -f src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint \ + --tag local_tflint:latest \ + src/projects/eps-storage-terraform/.devcontainer/; \ + fi build-image: build-syft build-grype build-grant build-tflint guard-CONTAINER_NAME guard-BASE_VERSION_TAG guard-BASE_FOLDER guard-IMAGE_TAG workspace_folder="$${CONTAINER_NAME}"; \ From a9193c155e3a7ef714dbd6383f7efe47ef0a9b7b Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:13:06 +0000 Subject: [PATCH 04/15] fix --- .github/workflows/build_all_images.yml | 1 + Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 07eb5e9f..337f4863 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -79,6 +79,7 @@ jobs: tflint_image.tar package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml + needs: [build_tool_images] with: tag_latest: ${{ inputs.tag_latest }} docker_tag: ${{ inputs.docker_tag }} diff --git a/Makefile b/Makefile index 7bcf9adf..604a14eb 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ guard-%: .PHONY: install install-python install-node install-hooks build-base-image build-node-24-image build-node-24-python-3-10-image build-node-24-python-3-12-image build-node-24-python-3-13-image build-node-24-python-3-14-image \ build-eps-storage-terraform-image build-eps-data-extract-image build-fhir-facade-image build-node-24-python-3-14-golang-1-24-image build-node-24-python-3-14-java-24-image \ - build-regression-tests-image build-all build-image build-githubactions-image scan-image scan-image-json shell-image lint test lint-githubactions lint-githubaction-scripts clean + build-regression-tests-image build-all build-image build-githubactions-image scan-image scan-image-json shell-image lint test lint-githubactions lint-githubaction-scripts clean \ + build-syft build-grype build-grant build-tflint install: install-python install-node install-hooks install-python: From 636d0552a32634ff6e8b7608b506a19ef999dedd Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:36:12 +0000 Subject: [PATCH 05/15] fix --- .github/workflows/build_all_images.yml | 42 +++++++++++++++++++++----- .github/workflows/ci.yml | 2 ++ .github/workflows/pull_request.yml | 2 ++ .github/workflows/release.yml | 2 ++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 337f4863..f8bcf315 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -11,14 +11,13 @@ name: build_all_images NO_CACHE: required: true type: boolean -permissions: - attestations: write - contents: read - packages: write - id-token: write + pinned_image: + type: string + required: true +permissions: {} jobs: discover_folders: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: base_node_folders: ${{ steps.find-folders.outputs.base_node }} node_24_language_folders: ${{ steps.find-folders.outputs.node_24_languages }} @@ -40,11 +39,20 @@ jobs: } >> "$GITHUB_OUTPUT" build_tool_images: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 + container: + image: ${{ inputs.pinned_image }} + options: --user 1001:1001 --group-add 128 + defaults: + run: + shell: bash permissions: contents: read attestations: read steps: + - name: copy .tool-versions + run: | + cp /home/vscode/.tool-versions "$HOME/.tool-versions" - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: @@ -79,6 +87,11 @@ jobs: tflint_image.tar package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml + permissions: + attestations: write + contents: read + packages: write + id-token: write needs: [build_tool_images] with: tag_latest: ${{ inputs.tag_latest }} @@ -90,6 +103,11 @@ jobs: needs: - package_base_docker_image - discover_folders + permissions: + attestations: write + contents: read + packages: write + id-token: write strategy: fail-fast: false matrix: @@ -107,6 +125,11 @@ jobs: - package_base_docker_image - package_base_node_images - discover_folders + permissions: + attestations: write + contents: read + packages: write + id-token: write strategy: fail-fast: false matrix: @@ -123,6 +146,11 @@ jobs: needs: - package_node_24_language_docker_images - discover_folders + permissions: + attestations: write + contents: read + packages: write + id-token: write strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 974e4eda..d7d630b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: build_all_images: needs: - tag_release + - get_config_values uses: ./.github/workflows/build_all_images.yml permissions: attestations: write @@ -49,3 +50,4 @@ jobs: docker_tag: 'ci-${{ needs.tag_release.outputs.version_tag }}' tag_latest: false NO_CACHE: false + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 912248ff..794a189e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -84,6 +84,7 @@ jobs: needs: - get_issue_number - get_commit_id + - get_config_values uses: ./.github/workflows/build_all_images.yml permissions: attestations: write @@ -94,3 +95,4 @@ jobs: docker_tag: 'pr-${{ needs.get_issue_number.outputs.issue_number }}-${{ needs.get_commit_id.outputs.sha_short }}' tag_latest: false NO_CACHE: false + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 919abb81..f69e72f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,6 +42,7 @@ jobs: build_all_images: needs: - tag_release + - get_config_values uses: ./.github/workflows/build_all_images.yml permissions: attestations: write @@ -52,3 +53,4 @@ jobs: docker_tag: '${{ needs.tag_release.outputs.version_tag }}' tag_latest: true NO_CACHE: false + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} From 08771924aeffce212b316b56a02a13d151b19787 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:36:57 +0000 Subject: [PATCH 06/15] fix --- zizmor.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 zizmor.yml diff --git a/zizmor.yml b/zizmor.yml new file mode 100644 index 00000000..7bab1b9b --- /dev/null +++ b/zizmor.yml @@ -0,0 +1,5 @@ +rules: + unpinned-images: + # these workflows use unpinned images because they are using a full image passed in that contains the tag + ignore: + - build_all_images.yml:44:7 From 4fc4b74ddccc91cf0de49636d2a34d14f7e0436e Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:43:50 +0000 Subject: [PATCH 07/15] fix it --- .github/workflows/build_all_images.yml | 22 ++++++-------------- .github/workflows/build_multi_arch_image.yml | 1 - .github/workflows/ci.yml | 1 - .github/workflows/pull_request.yml | 1 - .github/workflows/release.yml | 1 - 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index f8bcf315..ca99256a 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -11,9 +11,6 @@ name: build_all_images NO_CACHE: required: true type: boolean - pinned_image: - type: string - required: true permissions: {} jobs: discover_folders: @@ -39,40 +36,33 @@ jobs: } >> "$GITHUB_OUTPUT" build_tool_images: + # build common tool images with a lower scoped github token as it uses a 3rd party docker image with github cli installed to verify attestation of tflint binary. + # token needs attestation read so it can verify attestation of tflint binary runs-on: ubuntu-22.04 - container: - image: ${{ inputs.pinned_image }} - options: --user 1001:1001 --group-add 128 - defaults: - run: - shell: bash permissions: contents: read attestations: read steps: - - name: copy .tool-versions - run: | - cp /home/vscode/.tool-versions "$HOME/.tool-versions" - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 persist-credentials: false - name: build_grype - run: + run: | make build-grype docker save "local_grype:latest" -o grype_image.tar - name: build_syft - run: + run: | make build-syft docker save "local_syft:latest" -o syft_image.tar - name: build_grant - run: + run: | make build-grant docker save "local_grant:latest" -o grant_image.tar - name: build_tflint - run: + run: | make build-tflint docker save "local_tflint:latest" -o tflint_image.tar diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index 43357d18..77b8f041 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -176,7 +176,6 @@ jobs: BASE_FOLDER: "${{ inputs.base_folder }}" NO_CACHE: '${{ inputs.NO_CACHE }}' BUILDX_NO_DEFAULT_ATTESTATIONS: "1" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check docker vulnerabilities - json output run: | make scan-image-json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7d630b4..146941ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,4 +50,3 @@ jobs: docker_tag: 'ci-${{ needs.tag_release.outputs.version_tag }}' tag_latest: false NO_CACHE: false - pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 794a189e..1a3324f1 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -95,4 +95,3 @@ jobs: docker_tag: 'pr-${{ needs.get_issue_number.outputs.issue_number }}-${{ needs.get_commit_id.outputs.sha_short }}' tag_latest: false NO_CACHE: false - pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f69e72f0..85154435 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,4 +53,3 @@ jobs: docker_tag: '${{ needs.tag_release.outputs.version_tag }}' tag_latest: true NO_CACHE: false - pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} From 55d554e47b40c853ac9f13471e570ac955fc552f Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:45:25 +0000 Subject: [PATCH 08/15] pass github token to tflint --- .github/workflows/build_all_images.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index ca99256a..584cc3af 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -36,7 +36,9 @@ jobs: } >> "$GITHUB_OUTPUT" build_tool_images: - # build common tool images with a lower scoped github token as it uses a 3rd party docker image with github cli installed to verify attestation of tflint binary. + # build common tool images with a lower scoped github token + # as it uses a 3rd party docker image with github cli installed to verify attestation of tflint binary + # and we dont want to make a high scoped token available to that image # token needs attestation read so it can verify attestation of tflint binary runs-on: ubuntu-22.04 permissions: @@ -65,6 +67,8 @@ jobs: run: | make build-tflint docker save "local_tflint:latest" -o tflint_image.tar + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f name: Upload docker images From 4098d86e8e9c62281137d027d74aeed5d649111c Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:48:50 +0000 Subject: [PATCH 09/15] latest qc --- .github/workflows/ci.yml | 2 +- .github/workflows/pull_request.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 146941ea..aebd81ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: contents: read packages: read quality_checks: - uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f2d4d6942115472d3f08316cd25f400b02a9dc69 + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@e798d5aee897de6f7dc387dd5623fcd9ba4c8929 needs: - get_config_values permissions: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1a3324f1..9ce6472e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -23,7 +23,7 @@ jobs: contents: read packages: read quality_checks: - uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f2d4d6942115472d3f08316cd25f400b02a9dc69 + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@e798d5aee897de6f7dc387dd5623fcd9ba4c8929 needs: - get_config_values with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 85154435..8af6a0e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: contents: read packages: read quality_checks: - uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f2d4d6942115472d3f08316cd25f400b02a9dc69 + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@e798d5aee897de6f7dc387dd5623fcd9ba4c8929 needs: - get_config_values permissions: From f304a05bc370cc5a74a03c699ed7377f43beea1a Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:51:55 +0000 Subject: [PATCH 10/15] better names --- .github/workflows/build_multi_arch_image.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index 77b8f041..8ab62756 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -41,7 +41,7 @@ jobs: - arch: arm64 runner: ubuntu-22.04-arm steps: - - name: remove android + - name: remove android tools run: | df -h echo "Freeing up disk space for docker build..." @@ -60,7 +60,7 @@ jobs: sudo apt-get clean > /dev/null 2>&1 || true fi df -h - - name: remove haskell + - name: remove haskell tools run: | df -h echo "Freeing up disk space for docker build..." @@ -81,7 +81,7 @@ jobs: fi df -h - - name: remove haskell + - name: remove dotnet tools run: | df -h echo "Freeing up disk space for docker build..." @@ -101,7 +101,7 @@ jobs: sudo apt-get clean > /dev/null 2>&1 || true fi df -h - - name: remove packages + - name: remove unused packages run: | df -h echo "Freeing up disk space for docker build..." From 3306be3a883ff1e311d7006d7ef1d01e7c558a30 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 08:56:30 +0000 Subject: [PATCH 11/15] multi arch tools --- .github/workflows/build_all_images.yml | 13 +++++++++-- .github/workflows/build_multi_arch_image.yml | 24 ++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 584cc3af..98484a0c 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -40,7 +40,16 @@ jobs: # as it uses a 3rd party docker image with github cli installed to verify attestation of tflint binary # and we dont want to make a high scoped token available to that image # token needs attestation read so it can verify attestation of tflint binary - runs-on: ubuntu-22.04 + name: Build tool images for on ${{ matrix.arch }} + runs-on: '${{ matrix.runner }}' + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + runner: ubuntu-22.04 + - arch: arm64 + runner: ubuntu-22.04-arm permissions: contents: read attestations: read @@ -73,7 +82,7 @@ jobs: - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f name: Upload docker images with: - name: docker_artifact + name: docker_artifact_${{ matrix.arch }} path: | grype_image.tar syft_image.tar diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index 8ab62756..bdba3d2f 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -46,10 +46,10 @@ jobs: df -h echo "Freeing up disk space for docker build..." - rm -rf /usr/local/lib/android || true - rm -rf /opt/android || true - rm -rf /usr/local/android-sdk || true - rm -rf /home/runner/Android || true + sudo rm -rf /usr/local/lib/android || true + sudo rm -rf /opt/android || true + sudo rm -rf /usr/local/android-sdk || true + sudo rm -rf /home/runner/Android || true # Remove Android packages if they exist ANDROID_PACKAGES=$(dpkg -l | grep -E "^ii.*(android|adb)" | awk '{print $2}' | tr '\n' ' ' || true) @@ -65,11 +65,11 @@ jobs: df -h echo "Freeing up disk space for docker build..." - rm -rf /opt/ghc || true - rm -rf /usr/local/.ghcup || true - rm -rf /opt/cabal || true - rm -rf /home/runner/.ghcup || true - rm -rf /home/runner/.cabal || true + sudo rm -rf /opt/ghc || true + sudo rm -rf /usr/local/.ghcup || true + sudo rm -rf /opt/cabal || true + sudo rm -rf /home/runner/.ghcup || true + sudo rm -rf /home/runner/.cabal || true # Remove Haskell packages if they exist HASKELL_PACKAGES=$(dpkg -l | grep -E "^ii.*(ghc|haskell|cabal)" | awk '{print $2}' | tr '\n' ' ' || true) @@ -87,10 +87,10 @@ jobs: echo "Freeing up disk space for docker build..." # Remove .NET installation directories - rm -rf /usr/share/dotnet || true + sudo rm -rf /usr/share/dotnet || true # Remove .NET documentation directories - rm -rf /usr/share/doc/dotnet-* || true + sudo rm -rf /usr/share/doc/dotnet-* || true # Remove .NET packages if they exist DOTNET_PACKAGES=$(dpkg -l | grep -E "^ii.*dotnet" | awk '{print $2}' | tr '\n' ' ' || true) @@ -137,7 +137,7 @@ jobs: - name: docker_artifact download uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: - name: docker_artifact + name: docker_artifact_${{ matrix.arch }} path: images/ - name: extract docker images run: | From 567bfe2bd7a425971af08e9e12d1d6dbfa453f7b Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 09:05:34 +0000 Subject: [PATCH 12/15] try without cleaning space --- .github/workflows/build_multi_arch_image.yml | 78 -------------------- 1 file changed, 78 deletions(-) diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index bdba3d2f..498fc4f1 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -41,84 +41,6 @@ jobs: - arch: arm64 runner: ubuntu-22.04-arm steps: - - name: remove android tools - run: | - df -h - echo "Freeing up disk space for docker build..." - - sudo rm -rf /usr/local/lib/android || true - sudo rm -rf /opt/android || true - sudo rm -rf /usr/local/android-sdk || true - sudo rm -rf /home/runner/Android || true - - # Remove Android packages if they exist - ANDROID_PACKAGES=$(dpkg -l | grep -E "^ii.*(android|adb)" | awk '{print $2}' | tr '\n' ' ' || true) - if [[ -n "${ANDROID_PACKAGES}" ]]; then - echo "Removing Android packages: ${ANDROID_PACKAGES}" - sudo apt-get remove -y "${ANDROID_PACKAGES}" --fix-missing > /dev/null 2>&1 || true - sudo apt-get autoremove -y > /dev/null 2>&1 || true - sudo apt-get clean > /dev/null 2>&1 || true - fi - df -h - - name: remove haskell tools - run: | - df -h - echo "Freeing up disk space for docker build..." - - sudo rm -rf /opt/ghc || true - sudo rm -rf /usr/local/.ghcup || true - sudo rm -rf /opt/cabal || true - sudo rm -rf /home/runner/.ghcup || true - sudo rm -rf /home/runner/.cabal || true - - # Remove Haskell packages if they exist - HASKELL_PACKAGES=$(dpkg -l | grep -E "^ii.*(ghc|haskell|cabal)" | awk '{print $2}' | tr '\n' ' ' || true) - if [[ -n "${HASKELL_PACKAGES}" ]]; then - echo "Removing Haskell packages: ${HASKELL_PACKAGES}" - sudo apt-get remove -y "${HASKELL_PACKAGES}" --fix-missing > /dev/null 2>&1 || true - sudo apt-get autoremove -y > /dev/null 2>&1 || true - sudo apt-get clean > /dev/null 2>&1 || true - fi - - df -h - - name: remove dotnet tools - run: | - df -h - echo "Freeing up disk space for docker build..." - - # Remove .NET installation directories - sudo rm -rf /usr/share/dotnet || true - - # Remove .NET documentation directories - sudo rm -rf /usr/share/doc/dotnet-* || true - - # Remove .NET packages if they exist - DOTNET_PACKAGES=$(dpkg -l | grep -E "^ii.*dotnet" | awk '{print $2}' | tr '\n' ' ' || true) - if [[ -n "${DOTNET_PACKAGES}" ]]; then - echo "Removing .NET packages: ${DOTNET_PACKAGES}" - sudo apt-get remove -y "${DOTNET_PACKAGES}" --fix-missing > /dev/null 2>&1 || true - sudo apt-get autoremove -y > /dev/null 2>&1 || true - sudo apt-get clean > /dev/null 2>&1 || true - fi - df -h - - name: remove unused packages - run: | - df -h - echo "Freeing up disk space for docker build..." - - # Remove specific packages if they exist - PACKAGES_TO_REMOVE="azure-cli google-cloud-cli microsoft-edge-stable google-chrome-stable firefox postgresql* temurin-* *llvm* mysql* dotnet-sdk-*" - for pkg in $PACKAGES_TO_REMOVE; do - if dpkg -l | grep -q "^ii.*${pkg}"; then - echo "Removing package: ${pkg}" - sudo apt-get remove -y "${pkg}" --fix-missing > /dev/null 2>&1 || true - fi - done - - sudo apt-get autoremove -y > /dev/null 2>&1 || true - sudo apt-get clean > /dev/null 2>&1 || true - - df -h - name: Login to github container registry uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with: From 1c1a353aafb438781e7e9e4a3fd0908e3990a3ee Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 09:07:52 +0000 Subject: [PATCH 13/15] update some versions --- src/base/.devcontainer/.tool-versions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/.devcontainer/.tool-versions b/src/base/.devcontainer/.tool-versions index 7aaf5f8b..20eb3b3d 100644 --- a/src/base/.devcontainer/.tool-versions +++ b/src/base/.devcontainer/.tool-versions @@ -1,5 +1,5 @@ shellcheck 0.11.0 direnv 2.37.1 -actionlint 1.7.11 +actionlint 1.7.12 ruby 3.3.0 -yq 4.52.4 +yq 4.52.5 From c46f3b265591a998494864329fb76d8c4c5c3cf1 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 09:56:04 +0000 Subject: [PATCH 14/15] no script --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 604a14eb..140fdba4 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ install-python: poetry install install-node: - npm install + npm ci --ignore-scripts install-hooks: install-python poetry run pre-commit install --install-hooks --overwrite From 11f14ca099ce30d5b3119493deaba4e0548ca752 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 15 Apr 2026 10:31:32 +0000 Subject: [PATCH 15/15] do not need zizmor file --- zizmor.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 zizmor.yml diff --git a/zizmor.yml b/zizmor.yml deleted file mode 100644 index 7bab1b9b..00000000 --- a/zizmor.yml +++ /dev/null @@ -1,5 +0,0 @@ -rules: - unpinned-images: - # these workflows use unpinned images because they are using a full image passed in that contains the tag - ignore: - - build_all_images.yml:44:7