#!/bin/sh -e MANIFEST_TYPE='application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json;q=0.5, application/vnd.oci.image.index.v1+json;q=0.3' CACHE_DIR="$HOME/.cache/d-utils/" mkdir -p "$CACHE_DIR" if [ $# -lt 1 ] || [ "$1" = '-h' ]; then echo "usage: d-pull [-h] [registry/]name[:tag] [dir]" exit 1 fi if echo "$1" | grep -q '/'; then reg=$(echo "$1" | rev | cut -d/ -f1-2 --complement | rev) lib=$(echo "$1" | rev | cut -d/ -f2 | rev) img=$(echo "$1" | rev | cut -d/ -f1 | rev) else reg='' lib='library' img="$1" fi if [ -z "$reg" ]; then reg='registry-1.docker.io' fi if echo "$img" | grep -q ':'; then tag=$(echo "$img" | cut -d: -f2) img=$(echo "$img" | cut -d: -f1) else tag='latest' fi if [ $# -gt 1 ]; then dir=$(realpath -m "$2") else dir=$(realpath -m "$img") fi if [ -e "$dir" ]; then echo "$dir already exists" exit 1 fi echo "pulling $reg/$lib/$img:$tag to $dir" url="https://$reg/v2/$lib/$img" echo " fetching token" auth_url=$(curl -s --head "https://$reg/v2/" | grep -i www-authenticate | sed 's/.*realm="\([^"]*\)",service="\([^"]*\)".*/\1?service=\2/') if [ -z "$auth_url" ]; then auth_url=$(curl -s --head "$url/manifests/$tag" | grep -i www-authenticate | sed 's/.*realm="\([^"]*\)",service="\([^"]*\)".*/\1?service=\2/') fi auth_token=$(curl -s "$auth_url&scope=repository:$lib/$img:pull" | jq -r '.token') auth="Authorization: Bearer $auth_token" # fail if server reports error curl --head -f --no-progress-meter -o /dev/null -H "$auth" -H "Accept: $MANIFEST_TYPE" "$url/manifests/$tag" mkdir -p "$dir/rootfs" echo "$lib/$img:$tag" > "$dir/image.txt" echo " fetching manifest" curl -s -H "$auth" -H "Accept: $MANIFEST_TYPE" "$url/manifests/$tag" -o "$dir/manifest.json" while grep -q 'application\\\?/vnd.oci.image.index.v1+json' "$dir/manifest.json"; do query="first(.manifests[] | select(.platform.os | test(\"${GOOS:-linux}\")) | select(.platform.architecture | test(\"${GOARCH:-amd64}\"))).digest" if jq -e "$query" "$dir/manifest.json" > /dev/null; then echo " fetching manifest for ${GOOS:-linux}/${GOARCH:-amd64}" digest=$(jq -r "$query" "$dir/manifest.json") else echo " fetching first available manifest" digest=$(jq -r "first(.manifests[]).digest" "$dir/manifest.json") fi curl -s -L -H "$auth" -H "Accept: $MANIFEST_TYPE" "$url/manifests/$digest" -o "$dir/manifest.json" done echo " fetching config" config=$(jq -r '.config.digest' "$dir/manifest.json") curl -s -L -H "$auth" "$url/blobs/$config" | jq '.config' > "$dir/config.json" echo " fetching layers" jq -r '.layers|map(.digest)|.[]' "$dir/manifest.json" | while read -r blob; do echo " $blob" if [ -e "$CACHE_DIR/$blob" ]; then touch "$CACHE_DIR/$blob" else curl -s -L -H "$auth" "$url/blobs/$blob" -o "$CACHE_DIR/$blob" fi tar -C "$dir/rootfs" -xf "$CACHE_DIR/$blob" done echo " cleanup" rm "$dir/manifest.json" find "$CACHE_DIR" -type f -mtime +30 -exec rm -f {} + echo "successfully pulled $reg/$lib/$img:$tag to $dir"