Last active
March 19, 2026 17:19
-
-
Save stuft2/0df80e139d965bdab8652542d5d2d595 to your computer and use it in GitHub Desktop.
Mirror a multiarch image from docker hub to quay.byu.edu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| usage() { | |
| cat <<'EOF' | |
| Usage: | |
| scripts/mirror-multiarch-image.sh [source-image] [destination-image] | |
| Defaults: | |
| source-image docker.io/library/golang:1.26 | |
| destination-image quay.byu.edu/docker-cache/library/golang:1.26 | |
| Environment: | |
| PLATFORM_FILTER jq expression applied to each manifest's .platform object. | |
| Default keeps Linux platform manifests and excludes | |
| attestation manifests with unknown platform: | |
| .os == "linux" and .architecture != "unknown" | |
| TMP_TAG_PREFIX Prefix for intermediate tags pushed to the destination repo. | |
| Default: mirror-tmp | |
| Prerequisites: | |
| - docker buildx | |
| - jq | |
| - skopeo | |
| - authenticated access to the destination registry | |
| Example: | |
| scripts/mirror-multiarch-image.sh | |
| Linux-only example: | |
| PLATFORM_FILTER='.os == "linux" and .architecture != "unknown"' \ | |
| scripts/mirror-multiarch-image.sh | |
| All non-attestation platforms example: | |
| PLATFORM_FILTER='.os != "unknown" and .architecture != "unknown"' \ | |
| scripts/mirror-multiarch-image.sh | |
| EOF | |
| } | |
| require_cmd() { | |
| if ! command -v "$1" >/dev/null 2>&1; then | |
| echo "missing required command: $1" >&2 | |
| exit 1 | |
| fi | |
| } | |
| if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then | |
| usage | |
| exit 0 | |
| fi | |
| require_cmd docker | |
| require_cmd jq | |
| require_cmd skopeo | |
| SRC_IMAGE="${1:-docker.io/library/golang:1.26}" | |
| DST_IMAGE="${2:-quay.byu.edu/docker-cache/library/golang:1.26}" | |
| PLATFORM_FILTER="${PLATFORM_FILTER:-.os == \"linux\" and .architecture != \"unknown\"}" | |
| TMP_TAG_PREFIX="${TMP_TAG_PREFIX:-mirror-tmp}" | |
| STAMP="$(date +%Y%m%d%H%M%S)" | |
| if [[ "${SRC_IMAGE}" == *@* ]]; then | |
| echo "source image must not include a digest: ${SRC_IMAGE}" >&2 | |
| exit 1 | |
| fi | |
| if [[ "${DST_IMAGE}" != *:* ]]; then | |
| echo "destination image must include a tag: ${DST_IMAGE}" >&2 | |
| exit 1 | |
| fi | |
| SRC_REPO="${SRC_IMAGE%:*}" | |
| DST_REPO="${DST_IMAGE%:*}" | |
| DST_TAG="${DST_IMAGE##*:}" | |
| TMP_REFS=() | |
| cleanup_tmp_refs() { | |
| if [[ "${#TMP_REFS[@]}" -eq 0 ]]; then | |
| return | |
| fi | |
| echo "Deleting temporary tags" >&2 | |
| for tmp_ref in "${TMP_REFS[@]}"; do | |
| echo "Deleting ${tmp_ref}" >&2 | |
| skopeo delete "docker://${tmp_ref}" || true | |
| done | |
| } | |
| trap cleanup_tmp_refs EXIT | |
| echo "Inspecting source manifest list: ${SRC_IMAGE}" >&2 | |
| mapfile -t MANIFEST_ROWS < <( | |
| docker buildx imagetools inspect --raw "${SRC_IMAGE}" | jq -r " | |
| .manifests[] | |
| | select(.platform != null) | |
| | select(.platform | (${PLATFORM_FILTER})) | |
| | [ | |
| .digest, | |
| .platform.os, | |
| .platform.architecture, | |
| (.platform.variant // \"\"), | |
| (.platform[\"os.version\"] // \"\") | |
| ] | |
| | @tsv | |
| " | |
| ) | |
| if [[ "${#MANIFEST_ROWS[@]}" -eq 0 ]]; then | |
| echo "no platform manifests matched filter: ${PLATFORM_FILTER}" >&2 | |
| exit 1 | |
| fi | |
| for row in "${MANIFEST_ROWS[@]}"; do | |
| IFS=$'\t' read -r digest os arch variant os_version <<<"${row}" | |
| digest_short="${digest#sha256:}" | |
| digest_short="${digest_short:0:12}" | |
| platform="${os}/${arch}" | |
| if [[ -n "${variant}" ]]; then | |
| platform="${platform}/${variant}" | |
| fi | |
| if [[ -n "${os_version}" ]]; then | |
| platform="${platform} os.version=${os_version}" | |
| fi | |
| tmp_tag="${TMP_TAG_PREFIX}-${DST_TAG}-${STAMP}-${digest_short}" | |
| tmp_ref="${DST_REPO}:${tmp_tag}" | |
| echo "Copying ${platform} (${digest}) -> ${tmp_ref}" >&2 | |
| skopeo copy \ | |
| "docker://${SRC_REPO}@${digest}" \ | |
| "docker://${tmp_ref}" | |
| TMP_REFS+=("${tmp_ref}") | |
| done | |
| echo "Creating manifest list: ${DST_IMAGE}" >&2 | |
| docker buildx imagetools create \ | |
| -t "${DST_IMAGE}" \ | |
| "${TMP_REFS[@]}" | |
| echo "Inspecting destination manifest list: ${DST_IMAGE}" >&2 | |
| docker buildx imagetools inspect "${DST_IMAGE}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment