Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
60 changes: 58 additions & 2 deletions .github/scripts/delete_unused_images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
DRY_RUN=false
DELETE_PR=false
DELETE_CI=false
DELETE_UNTAGGED=false

while [[ $# -gt 0 ]]; do
case "$1" in
Expand All @@ -18,13 +19,17 @@ while [[ $# -gt 0 ]]; do
DELETE_CI=true
shift
;;
--delete-untagged)
DELETE_UNTAGGED=true
shift
;;
--help|-h)
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci]"
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci] [--delete-untagged]"
exit 0
;;
*)
echo "Unknown option: $1" >&2
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci]" >&2
echo "Usage: $0 [--dry-run] [--delete-pr] [--delete-ci] [--delete-untagged]" >&2
exit 1
;;
esac
Expand Down Expand Up @@ -166,7 +171,37 @@ delete_ci_images() {
done <<<"${tags}"
}

delete_untagged_images() {
local container_name=$1
local package_name
local versions_json

if [[ -z "${container_name}" ]]; then
echo "Container name is required" >&2
return 1
fi

package_name=$(get_container_package_name "${container_name}")
versions_json=$(get_container_versions_json "${container_name}")

jq -r '.[] | select(((.metadata.container.tags // []) | length) == 0) | .id' \
<<<"${versions_json}" \
| while IFS= read -r version_id; do
if [[ -n "${version_id}" ]]; then
if [[ "${DRY_RUN}" == "true" ]]; then
echo "[DRY RUN] Would delete untagged image version ID ${version_id} from container ${container_name}."
else
echo "Deleting untagged image version ID ${version_id} from container ${container_name}..."
gh api \
-H "Accept: application/vnd.github+json" \
-X DELETE \
"/orgs/nhsdigital/packages/container/${package_name}/versions/${version_id}"
fi
fi
done
}

base_node_folders=$(find src/base_node -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
language_folders=$(find src/languages -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
project_folders=$(find src/projects -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')

Expand All @@ -177,6 +212,21 @@ for container_name in $(jq -r '.[]' <<<"${project_folders}"); do
if [[ "${DELETE_CI}" == "true" ]]; then
delete_ci_images "${container_name}"
fi
if [[ "${DELETE_UNTAGGED}" == "true" ]]; then
delete_untagged_images "${container_name}"
fi
done

for container_name in $(jq -r '.[]' <<<"${base_node_folders}"); do
if [[ "${DELETE_PR}" == "true" ]]; then
delete_pr_images "${container_name}"
fi
if [[ "${DELETE_CI}" == "true" ]]; then
delete_ci_images "${container_name}"
fi
if [[ "${DELETE_UNTAGGED}" == "true" ]]; then
delete_untagged_images "${container_name}"
fi
done

for container_name in $(jq -r '.[]' <<<"${language_folders}"); do
Expand All @@ -186,6 +236,9 @@ for container_name in $(jq -r '.[]' <<<"${language_folders}"); do
if [[ "${DELETE_CI}" == "true" ]]; then
delete_ci_images "${container_name}"
fi
if [[ "${DELETE_UNTAGGED}" == "true" ]]; then
delete_untagged_images "${container_name}"
fi
done

if [[ "${DELETE_PR}" == "true" ]]; then
Expand All @@ -194,3 +247,6 @@ fi
if [[ "${DELETE_CI}" == "true" ]]; then
delete_ci_images "base"
fi
if [[ "${DELETE_UNTAGGED}" == "true" ]]; then
delete_untagged_images "base"
fi
198 changes: 198 additions & 0 deletions .github/workflows/build_multi_arch_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ jobs:
IMAGE_TAG: "${{ inputs.docker_tag }}-${{ matrix.arch }}"
BASE_FOLDER: "${{ inputs.base_folder }}"
NO_CACHE: '${{ inputs.NO_CACHE }}'
BUILDX_NO_DEFAULT_ATTESTATIONS: "1"
- name: Check docker vulnerabilities - json output
run: |
make scan-image-json
Expand Down Expand Up @@ -136,6 +137,55 @@ jobs:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
BUILDX_NO_DEFAULT_ATTESTATIONS: "1"
- name: Resolve image digest
id: resolve_arch_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:${DOCKER_TAG}-${ARCHITECTURE}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for ${DOCKER_TAG}-${ARCHITECTURE}"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
- name: Attest image
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_arch_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested image
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:${DOCKER_TAG}-${ARCHITECTURE}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
DIGEST: ${{ steps.resolve_arch_digest.outputs.digest }}
- name: Resolve github actions image digest
id: resolve_githubactions_arch_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-${DOCKER_TAG}-${ARCHITECTURE}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for githubactions-${DOCKER_TAG}-${ARCHITECTURE}"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
- name: Attest github actions image
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_githubactions_arch_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested github actions image
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-${DOCKER_TAG}-${ARCHITECTURE}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
DIGEST: ${{ steps.resolve_githubactions_arch_digest.outputs.digest }}
- name: Push latest image
if: ${{ inputs.tag_latest }}
run: |
Expand All @@ -152,6 +202,56 @@ jobs:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
- name: Resolve github actions latest image digest
if: ${{ inputs.tag_latest }}
id: resolve_githubactions_latest_arch_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-latest-${ARCHITECTURE}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for githubactions-latest-${ARCHITECTURE}"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
- name: Attest github actions latest image
if: ${{ inputs.tag_latest }}
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_githubactions_latest_arch_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested github actions latest image
if: ${{ inputs.tag_latest }}
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-latest-${ARCHITECTURE}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
DIGEST: ${{ steps.resolve_githubactions_latest_arch_digest.outputs.digest }}
- name: Resolve latest image digest
if: ${{ inputs.tag_latest }}
id: resolve_latest_arch_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:latest-${ARCHITECTURE}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for latest-${ARCHITECTURE}"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
- name: Attest latest image
if: ${{ inputs.tag_latest }}
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_latest_arch_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested latest image
if: ${{ inputs.tag_latest }}
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:latest-${ARCHITECTURE}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
ARCHITECTURE: '${{ matrix.arch }}'
DIGEST: ${{ steps.resolve_latest_arch_digest.outputs.digest }}
publish_combined_image:
name: Publish combined image for ${{ inputs.container_name }}
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -222,3 +322,101 @@ jobs:
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'

- name: Resolve combined image digest
id: resolve_combined_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:${DOCKER_TAG}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for ${DOCKER_TAG}"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'

- name: Attest combined image
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_combined_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested combined image
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:${DOCKER_TAG}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
DIGEST: ${{ steps.resolve_combined_digest.outputs.digest }}

- name: Resolve combined github actions image digest
id: resolve_githubactions_combined_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-${DOCKER_TAG}" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for githubactions-${DOCKER_TAG}"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'

- name: Attest combined github actions image
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_githubactions_combined_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested combined github actions image
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-${DOCKER_TAG}@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
DOCKER_TAG: ${{ inputs.docker_tag }}
CONTAINER_NAME: '${{ inputs.container_name }}'
DIGEST: ${{ steps.resolve_githubactions_combined_digest.outputs.digest }}

- name: Resolve latest github actions image digest
if: ${{ inputs.tag_latest }}
id: resolve_githubactions_latest_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-latest" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for githubactions-latest"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'

- name: Attest latest github actions image
if: ${{ inputs.tag_latest }}
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_githubactions_latest_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested latest github actions image
if: ${{ inputs.tag_latest }}
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:githubactions-latest@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
DIGEST: ${{ steps.resolve_githubactions_latest_digest.outputs.digest }}

- name: Resolve latest image digest
if: ${{ inputs.tag_latest }}
id: resolve_latest_digest
run: |
DIGEST=$(docker buildx imagetools inspect "ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:latest" | awk '/^Digest:/ {print $2; exit}')
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved digest ${DIGEST} for latest"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'

- name: Attest latest image
if: ${{ inputs.tag_latest }}
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a
with:
subject-name: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}
subject-digest: ${{ steps.resolve_latest_digest.outputs.digest }}
push-to-registry: false
- name: Summarise attested latest image
if: ${{ inputs.tag_latest }}
run: |
echo "## ATTESTED IMAGE : ghcr.io/nhsdigital/eps-devcontainers/${CONTAINER_NAME}:latest@${DIGEST}" >> "$GITHUB_STEP_SUMMARY"
env:
CONTAINER_NAME: '${{ inputs.container_name }}'
DIGEST: ${{ steps.resolve_latest_digest.outputs.digest }}
4 changes: 2 additions & 2 deletions .github/workflows/delete_old_images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: "Delete old images"
on:
workflow_dispatch:
schedule:
- cron: "0 1,13 * * *"
- cron: "0 1 * * 6"
push:
branches: [main]

Expand All @@ -30,7 +30,7 @@ jobs:
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
.github/scripts/delete_unused_images.sh --delete-pr
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
.github/scripts/delete_unused_images.sh --delete-ci
.github/scripts/delete_unused_images.sh --delete-ci --delete-untagged
else
.github/scripts/delete_unused_images.sh
fi
Expand Down
Loading