#!/bin/bash set -euo pipefail SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" REPO_ROOT_PATH=$SCRIPTPATH/../ MAKE_FILE_PATH=$REPO_ROOT_PATH/Makefile VERSION=$(make -s -f $MAKE_FILE_PATH version) PLATFORMS=("linux/amd64") MANIFEST_IMAGES=() MANIFEST="" DOCKER_CLI_CONFIG="$HOME/.docker/config.json" USAGE=$(cat << 'EOM' Usage: push-docker-images [-p ] Pushes docker images for the platform pairs passed in w/ a manifest list Example: push-docker-images -p "linux/amd64,linux/arm" Optional: -p Platform pair list (os/architecture) [DEFAULT: linux/amd64] -r IMAGE REPO: set the docker image repo -v VERSION: The application version of the docker image [DEFAULT: output of `make version`] -m Create a docker manifest EOM ) # Process our input arguments while getopts "mp:r:v:" opt; do case ${opt} in p ) # Platform Pairs IFS=',' read -ra PLATFORMS <<< "$OPTARG" ;; r ) # Image Repo IMAGE_REPO="$OPTARG" ;; v ) # Image Version VERSION="$OPTARG" ;; m ) # Docker manifest MANIFEST="true" ;; \? ) echo "$USAGE" 1>&2 exit ;; esac done if [[ ${#PLATFORMS[@]} -gt 1 && $MANIFEST != "true" ]]; then echo "Only one platform can be pushed if you do not create a manifest." echo "Try again with the -m option" exit 1 fi # Existing manifests cannot be updated only overwritten; therefore, # if manifest exists already, fetch existing platforms so "updated" manifest includes images # that were there previously if [[ $MANIFEST == "true" ]]; then if [[ ! -f $DOCKER_CLI_CONFIG ]]; then echo '{"experimental":"enabled"}' > $DOCKER_CLI_CONFIG echo "Created docker config file" fi cat <<< "$(jq '.+{"experimental":"enabled"}' $DOCKER_CLI_CONFIG)" > $DOCKER_CLI_CONFIG echo "Enabled experimental CLI features to execute docker manifest commands" # Delete the local version of the manifest so we rely solely on the remote manifest docker manifest rm $IMAGE_REPO:$VERSION || : manifest_exists=$(docker manifest inspect $IMAGE_REPO:$VERSION > /dev/null ; echo $?) if [[ manifest_exists -eq 0 ]]; then echo "manifest already exists" EXISTING_IMAGES=() while IFS='' read -r line; do EXISTING_IMAGES+=("$line"); done < <(docker manifest inspect $IMAGE_REPO:$VERSION | jq -r '.manifests[] | "\(.platform.os)-\(.platform.architecture)"') # treat separate from PLATFORMS because existing images don't need to be tagged and pushed for os_arch in "${EXISTING_IMAGES[@]}"; do img_tag="$IMAGE_REPO:$VERSION-$os_arch" MANIFEST_IMAGES+=("$img_tag") done echo "images already in manifest: ${MANIFEST_IMAGES[*]}" fi fi for os_arch in "${PLATFORMS[@]}"; do os=$(echo $os_arch | cut -d'/' -f1) arch=$(echo $os_arch | cut -d'/' -f2) img_tag_w_platform="$IMAGE_REPO:$VERSION-$os-$arch" if [[ $MANIFEST == "true" ]]; then img_tag=$img_tag_w_platform else img_tag="$IMAGE_REPO:$VERSION" docker tag $img_tag_w_platform $img_tag fi docker push $img_tag MANIFEST_IMAGES+=("$img_tag") done if [[ $MANIFEST == "true" ]]; then current_os=$(uname) # Windows will append '\r' to the end of $img which # results in docker failing to create the manifest due to invalid reference format. # However, MacOS does not recognize '\r' as carriage return # and attempts to remove literal 'r' chars; therefore, made this so portable for img in "${MANIFEST_IMAGES[@]}"; do if [[ $current_os == "Darwin" ]]; then updated_img=$img else updated_img=$(echo $img | sed -e 's/\r$//') fi echo "creating manifest for $updated_img" docker manifest create $IMAGE_REPO:$VERSION $updated_img --amend # Theoretically, this will not be necessary if we move all our builds to docker buildx. # (The Windows build is the only one not using it at the moment.) The manifest create --amend command # should figure out the OS and architecture automatically if the container was built properly. # However, our builds in the past required this explicit annotation, and it doesn't hurt to keep it for now. os_arch=$(echo ${updated_img//$IMAGE_REPO:$VERSION-/}) os=$(echo $os_arch | cut -d'-' -f1) arch=$(echo $os_arch | cut -d'-' -f2) echo "annotating manifest" docker manifest annotate $IMAGE_REPO:$VERSION $updated_img --arch $arch --os $os done echo "pushing manifest" docker manifest push --purge $IMAGE_REPO:$VERSION fi