#!/usr/bin/env bash # shellcheck source=arches.sh 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 platform="$(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 "${platform}" \ "${tag_args[@]}" \ --push \ - < ./docker/Dockerfile.buildx