#!/bin/bash

source ./hooks/arches.sh

export DOCKER_CLI_EXPERIMENTAL=enabled

# Join a list of args with a single char.
# Ref: https://stackoverflow.com/a/17841619
join() { local IFS="$1"; shift; echo "$*"; }

set -ex

echo ">>> Starting local Docker registry when needed..."

# Docker Buildx's `docker-container` driver is needed for multi-platform
# builds, but it can't access existing images on the Docker host (like the
# cross-compiled ones we just built). Those images first need to be pushed to
# a registry -- Docker Hub could be used, but since it's not trivial to clean
# up those intermediate images on Docker Hub, it's easier to just run a local
# Docker registry, which gets cleaned up automatically once the build job ends.
#
# https://docs.docker.com/registry/deploying/
# https://hub.docker.com/_/registry
#
# Use host networking so the buildx container can access the registry via
# localhost.
#
# First check if there already is a registry container running, else skip it.
# This will only happen either locally or running it via Github Actions
#
if ! timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5000'; then
    # defaults to port 5000
    docker run -d --name registry --network host registry:2
fi

# Docker Hub sets a `DOCKER_REPO` env var with the format `index.docker.io/user/repo`.
# Strip the registry portion to construct a local repo path for use in `Dockerfile.buildx`.
LOCAL_REGISTRY="localhost:5000"
REPO="${DOCKER_REPO#*/}"
LOCAL_REPO="${LOCAL_REGISTRY}/${REPO}"

echo ">>> Pushing images to local registry..."

for arch in ${arches[@]}; do
    docker_image="${DOCKER_REPO}:${DOCKER_TAG}-${arch}"
    local_image="${LOCAL_REPO}:${DOCKER_TAG}-${arch}"
    docker tag "${docker_image}" "${local_image}"
    docker push "${local_image}"
done

echo ">>> Setting up Docker Buildx..."

# Same as earlier, use host networking so the buildx container can access the
# registry via localhost.
#
# Ref: https://github.com/docker/buildx/issues/94#issuecomment-534367714
#
# Check if there already is a builder running, else skip this and use the existing.
# This will only happen either locally or running it via Github Actions
#
if ! docker buildx inspect builder > /dev/null 2>&1 ; then
    docker buildx create --name builder --use --driver-opt network=host
fi

echo ">>> Running Docker Buildx..."

tags=("${DOCKER_REPO}:${DOCKER_TAG}")

# If the Docker tag starts with a version number, assume the latest release
# is being pushed. Add an extra tag (`latest` or `alpine`, as appropriate)
# to make it easier for users to track the latest release.
if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
    if [[ "${DOCKER_TAG}" == *alpine ]]; then
        tags+=(${DOCKER_REPO}:alpine)
    else
        tags+=(${DOCKER_REPO}:latest)
    fi
fi

tag_args=()
for tag in "${tags[@]}"; do
    tag_args+=(--tag "${tag}")
done

# Docker Buildx takes a list of target platforms (OS/arch/variant), so map
# the arch list to a platform list (assuming the OS is always `linux`).
declare -A arch_to_platform=(
    [amd64]="linux/amd64"
    [armv6]="linux/arm/v6"
    [armv7]="linux/arm/v7"
    [arm64]="linux/arm64"
)
platforms=()
for arch in ${arches[@]}; do
    platforms+=("${arch_to_platform[$arch]}")
done
platforms="$(join "," "${platforms[@]}")"

# Run the build, pushing the resulting images and multi-arch manifest list to
# Docker Hub. The Dockerfile is read from stdin to avoid sending any build
# context, which isn't needed here since the actual cross-compiled images
# have already been built.
docker buildx build \
       --network host \
       --build-arg LOCAL_REPO="${LOCAL_REPO}" \
       --build-arg DOCKER_TAG="${DOCKER_TAG}" \
       --platform "${platforms}" \
       "${tag_args[@]}" \
       --push \
       - < ./docker/Dockerfile.buildx