Skip to content
Open
Show file tree
Hide file tree
Changes from 16 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
83 changes: 83 additions & 0 deletions .ci/check_image_size
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/sh
Comment thread
wdconinc marked this conversation as resolved.
Comment thread
wdconinc marked this conversation as resolved.
# check_image_size IMAGE_REFERENCE LIMIT_GIB
#
# Inspects a single-arch OCI/Docker image manifest (or the first image
# manifest in a multi-arch OCI/Docker image) that has already been pushed
Comment thread
wdconinc marked this conversation as resolved.
# to a registry and fails with a clear message if the total compressed layer
# size exceeds LIMIT_GIB gibibytes.
#
# Requires: docker (with buildx), jq, bc
# Usage:
# .ci/check_image_size ghcr.io/eic/eic_xl@sha256:abc... 10
# .ci/check_image_size ghcr.io/eic/eic_xl:master 10
Comment thread
wdconinc marked this conversation as resolved.
#
# The caller is responsible for being logged in to the registry before calling
# this script.

set -e

IMAGE_REF="${1:?Usage: $0 IMAGE_REFERENCE LIMIT_GIB}"
LIMIT_GIB="${2:?Usage: $0 IMAGE_REFERENCE LIMIT_GIB}"

echo "Checking compressed image size for ${IMAGE_REF} (limit: ${LIMIT_GIB} GiB) ..."

MANIFEST=$(docker buildx imagetools inspect --raw "${IMAGE_REF}")

# docker/build-push-action (v4+) wraps even single-platform pushes in an OCI
# Image Index when provenance attestations are enabled (the default). Detect
# that case and resolve to the real per-platform Image Manifest before summing
# layer sizes.
MEDIA_TYPE=$(printf '%s' "${MANIFEST}" | jq -r '.mediaType // ""')
case "${MEDIA_TYPE}" in
*index* | *manifest.list*)
DIGEST=$(printf '%s' "${MANIFEST}" | jq -r '
.manifests[]
| select(
.artifactType == null
Comment thread
wdconinc marked this conversation as resolved.
and ((.annotations["vnd.docker.reference.type"] // "") != "attestation-manifest")
and ((.annotations["vnd.docker.reference.digest"] // "") == "")
and .platform != null
and (.platform.os // "") != ""
and (.platform.architecture // "") != ""
and (.platform.os // "") != "unknown"
and (.platform.architecture // "") != "unknown"
)
| .digest' | head -1)
Comment thread
wdconinc marked this conversation as resolved.
Comment thread
wdconinc marked this conversation as resolved.
if [ -z "${DIGEST}" ]; then
echo "ERROR: OCI Image Index contains no platform manifest."
exit 1
fi
IMAGE_BASE="${IMAGE_REF%@*}"
Comment thread
wdconinc marked this conversation as resolved.
Outdated
MANIFEST=$(docker buildx imagetools inspect --raw "${IMAGE_BASE}@${DIGEST}")
;;
esac

SIZE_BYTES=$(printf '%s' "${MANIFEST}" | jq '[.layers[].size] | add // 0')

Comment thread
wdconinc marked this conversation as resolved.
if [ -z "${SIZE_BYTES}" ] || [ "${SIZE_BYTES}" = "null" ] || [ "${SIZE_BYTES}" -eq 0 ]; then
echo "ERROR: Could not determine image size from manifest (got: ${SIZE_BYTES})."
echo " Manifest snippet: $(printf '%s' "${MANIFEST}" | head -c 500)"
exit 1
fi

SIZE_CENTI_GIB=$(echo "(${SIZE_BYTES} * 100 + 1073741824 - 1) / 1073741824" | bc)
SIZE_GIB="$(echo "${SIZE_CENTI_GIB} / 100" | bc).$(printf '%02d' "$(echo "${SIZE_CENTI_GIB} % 100" | bc)")"
LIMIT_BYTES=$(echo "${LIMIT_GIB} * 1073741824 / 1" | bc)

echo " Compressed size : ${SIZE_GIB} GiB (${SIZE_BYTES} bytes)"
echo " Limit : ${LIMIT_GIB} GiB (${LIMIT_BYTES} bytes)"

if [ "${SIZE_BYTES}" -gt "${LIMIT_BYTES}" ]; then
echo ""
echo "ERROR: Image ${IMAGE_REF} exceeds the size cap!"
echo " ${SIZE_GIB} GiB > ${LIMIT_GIB} GiB"
echo ""
echo "To update the high-water mark after an intentional size increase:"
echo " 1. Re-run this script to inspect the published image size:"
echo " .ci/check_image_size \"${IMAGE_REF}\" \"${LIMIT_GIB}\""
echo " 2. Add ~15% buffer, and optionally round up to the next whole GiB"
echo " 3. Update SIZE_LIMIT_*_GIB in .github/workflows/build-push.yml and .gitlab-ci.yml"
exit 1
fi

echo " OK: size is within the ${LIMIT_GIB} GiB limit."
35 changes: 35 additions & 0 deletions .github/workflows/build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ env:
## Internal tag used for the CI
INTERNAL_TAG: pipeline-${{ github.run_id }}

## Compressed image size caps (GiB). Fail the build if a final image exceeds
## these limits to catch regressions early (e.g. accidental Spack-built LLVM).
## To update: measure the compressed sizes of the published master images,
## add ~15% headroom, round up, and update here and in .gitlab-ci.yml
## variables.
SIZE_LIMIT_DEBIAN_STABLE_BASE_GIB: 0.8
SIZE_LIMIT_EIC_CI_GIB: 3
SIZE_LIMIT_EIC_XL_GIB: 4.5

jobs:
env:
## The env context is not available in matrix, only in steps,
Expand Down Expand Up @@ -249,6 +258,18 @@ jobs:
run: |
mkdir -p /tmp/digests
echo "${{ steps.meta.outputs.tags }}@${{ steps.build.outputs.digest }}" > /tmp/digests/${{ matrix.BUILD_IMAGE }}-${{ matrix.arch }}.digest
- name: Check image size
run: |
FULLNAME="${{ matrix.BUILD_IMAGE }}"
LIMIT_VAR="SIZE_LIMIT_$(printf '%s' "${FULLNAME}" | tr '[:lower:]' '[:upper:]')_GIB"
LIMIT="${!LIMIT_VAR}"
if [ -n "${LIMIT}" ]; then
.ci/check_image_size \
"${{ env.GH_REGISTRY }}/${{ env.GH_REGISTRY_USER }}/${FULLNAME}@${{ steps.build.outputs.digest }}" \
"${LIMIT}"
else
echo "No size limit configured for ${FULLNAME}, skipping check."
fi
- name: Upload digest as artifact
uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -570,6 +591,20 @@ jobs:
run: |
mkdir -p /tmp/digests
echo "${{ steps.meta.outputs.tags }}@${{ steps.build.outputs.digest }}" > /tmp/digests/${{ matrix.arch }}.digest
- name: Check image size
# Only check final images; concretization-only targets (cuda, tf) don't produce a full image
if: ${{ matrix.target == 'final' }}
run: |
FULLNAME="${{ matrix.BUILD_IMAGE }}${{ matrix.ENV }}"
LIMIT_VAR="SIZE_LIMIT_$(printf '%s' "${FULLNAME}" | tr '[:lower:]' '[:upper:]')_GIB"
LIMIT="${!LIMIT_VAR}"
if [ -n "${LIMIT}" ]; then
.ci/check_image_size \
"${{ env.GH_REGISTRY }}/${{ env.GH_REGISTRY_USER }}/${FULLNAME}@${{ steps.build.outputs.digest }}" \
"${LIMIT}"
else
echo "No size limit configured for ${FULLNAME}, skipping check."
fi
- name: Upload digest as artifact
uses: actions/upload-artifact@v7
with:
Expand Down
29 changes: 29 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ variables:
## Number of jobs to start during container builds
JOBS: 32

## Compressed image size caps (GiB). Fail the build if a final image exceeds
## these limits to catch regressions early (e.g. accidental Spack-built LLVM).
## To update: measure the current master-tag compressed image sizes using
## registry or CI tooling, add ~15% headroom, round up, and update here and
## in .github/workflows/build-push.yml env.
SIZE_LIMIT_DEBIAN_STABLE_BASE_GIB: "0.8"
Comment thread
wdconinc marked this conversation as resolved.
SIZE_LIMIT_EIC_CI_GIB: "3"
SIZE_LIMIT_EIC_XL_GIB: "4.5"

## is this nightly or not?
NIGHTLY: ""
## Add to tag
Expand Down Expand Up @@ -238,6 +247,7 @@ status:pending:
- docker-new
before_script:
- !reference [.docker, before_script]
- apk add bc git jq
- if [ "$SKIP_BINFMT" != "true" ]; then
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc ;
for arch in aarch64 ; do
Expand Down Expand Up @@ -355,6 +365,15 @@ base:
--provenance false
containers/debian
2>&1 | tee build.log
- LIMIT_VAR="SIZE_LIMIT_$(printf '%s' "${BUILD_IMAGE}" | tr '[:lower:]' '[:upper:]')_GIB" ;
eval "LIMIT=\${${LIMIT_VAR}:-}" ;
if [ -n "${LIMIT}" ]; then
Comment thread
wdconinc marked this conversation as resolved.
Comment thread
wdconinc marked this conversation as resolved.
.ci/check_image_size
"${CI_REGISTRY}/${CI_PROJECT_PATH}/${BUILD_IMAGE}:${INTERNAL_TAG}"
"${LIMIT}" ;
Comment thread
wdconinc marked this conversation as resolved.
else
echo "No size limit configured for ${BUILD_IMAGE}, skipping check." ;
fi

eic:
parallel:
Expand Down Expand Up @@ -503,6 +522,16 @@ eic:
--provenance false
containers/eic
2>&1 | tee build.log
- FULLNAME="${BUILD_IMAGE}${ENV}" ;
LIMIT_VAR="SIZE_LIMIT_$(printf '%s' "${FULLNAME}" | tr '[:lower:]' '[:upper:]')_GIB" ;
eval "LIMIT=\${${LIMIT_VAR}:-}" ;
Comment thread
wdconinc marked this conversation as resolved.
Outdated
if [ -n "${LIMIT}" ]; then
.ci/check_image_size
"${CI_REGISTRY}/${CI_PROJECT_PATH}/${FULLNAME}:${INTERNAL_TAG}-${BUILD_TYPE}"
"${LIMIT}" ;
Comment thread
wdconinc marked this conversation as resolved.
else
echo "No size limit configured for ${FULLNAME}, skipping check." ;
fi

user_spack_environment:
stage: benchmarks
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ repos:
- id: shellcheck
types: [shell]
args: [--severity=warning, -x]
files: (^[^/]+\.sh$|^\.ci/resolve_git_ref$)
files: (^[^/]+\.sh$|^\.ci/resolve_git_ref$|^\.ci/check_image_size$)
Loading