# Common code for building pushing CI images.

# shellcheck shell=sh

# Set up docker(1) alias if needed. We assume not on gitlab.com because
# “docker info” talks to the Docker daemon which we don’t always need.
# WARNING: This apparently must come before any use of “docker” in functions.
printf 'docker alias:  ' 1>&2
if [ -n "$GITLAB_CI" ] || docker info > /dev/null 2>&1; then
    echo 'not needed' 1>&2
else
    alias docker='sudo docker'
    alias docker 1>&2
fi

# Registry path.
if [ -n "$CI_REGISTRY_IMAGE" ]; then
    regy=$CI_REGISTRY_IMAGE
else
    regy=registry.gitlab.com/charliecloud/charliecloud
fi
echo "registry:      $regy" 1>&2

# Print the digest of the named base image. Note this consumes a pull unit
# from your quota if Docker Hub.
#
#shellcheck disable=SC2016
basedigest () {
    arch=$1
    name=$2
    case $name in
    scratch)     # scratch is special
        digest=$(printf '0%.0s' $(seq 1 64))     # all zeroes
        ;;
    *ci_*)       # image we made e.g. “${regy}ci_debian:$branch”
        df=$(echo "$name" | sed -E 's/^\$\{.+\}ci_(.+):\$branch$/\1.df/')
        echo "$df, recursing" 1>&2
        digest=$(fulldigest "$arch" "$(dirname "$0")/$df")
        ;;
    *)           # image we didn’t make
        # OK this is pretty nasty/confusing because there are so many digests
        # floating around in OCI and often they are not present and/or you
        # don’t get enough information to compute them. I want to compute some
        # identifer for the base image that’s available both with and without
        # pulling the image. This is the best I could do. I think it is the
        # digest of the manifest list.
        #
        # In CI, we assume an ephemeral builder VM; thus, (1) there is no
        # already-pulled image that might be out of date and (2) we don’t want
        # to pull any base image unless we really need it. Therefore, we let
        # “build” pull it later, here obtaining the base image digest directly
        # from the registry. “buildx imagetools inspect” seems faster than
        # “docker manifest inspect” but I haven’t tested in detail; its output
        # is also easier to deal with.
        #
        # Otherwise, we assume a persistent builder; thus (1) we might have an
        # out-of-date local image and (2) it’s OK to spend resources pulling
        # since we might need it again. Therefore, we need to pull to ensure
        # the local image is up to date (we can’t specify --pull to build).
        if [ -n "$CI" ]; then
            # don’t pipeline so we exit on fail b/c -e
            text=$(docker buildx imagetools inspect "$name")
            digest=$(  echo "$text" \
                     | sed -En 's/^Digest:\s+sha256:([0-9a-f]+)$/\1/p')
        else
            docker pull "$name" 1>&2
            # Nasty format docs: https://docs.docker.com/engine/cli/formatting/
            # but it saved us a separate sed(1) call.
            digest=$(docker image inspect \
                     --format='{{index (split (index .RepoDigests 0) ":") 1}}' \
                     "$name")
        fi
        ;;
    esac
    echo "$digest"
}

# Function to pass through proxy variables if needed.
build () {
    if [ -z "$HTTP_PROXY" ]; then
        docker build "$@" 1>&2
    else
        docker build --build-arg HTTP_PROXY="${HTTP_PROXY:?}" \
                     --build-arg HTTPS_PROXY="${HTTPS_PROXY:?}" \
                     --build-arg NO_PROXY="${NO_PROXY:?}" \
                     --build-arg http_proxy="${http_proxy:?}" \
                     --build-arg https_proxy="${https_proxy:?}" \
                     --build-arg no_proxy="${no_proxy:?}" \
                     "$@" 1>&2
    fi
}

# Print the full digest of the named file on given architecture.
fulldigest () {
    arch=$1
    df=$2
    base=$(sed -En 's/^FROM (.+)/\1/p' "$df")
    echo "  base image:  $base" 1>&2
    #if [ -t 2 ]; then
    #    printf '(waiting for registry)' 1>&2
    #    printf '\033[%dD' 22 1>&2             # back up length of above msg
    #fi
    base_digest=$(basedigest "$arch" "$base")
    echo "  base:        $base_digest" 1>&2
    content_digest=$(md5 "$df")
    echo "  content:     $content_digest" 1>&2
    echo "$arch" "$content_digest" "$base_digest" | md5 -
}

# Convenience function for computing MD5 of a file.
md5 () {
    md5sum "$1" | cut -d' ' -f1
}

# Return successfully if the given image exists, unsuccessfully otherwise. If
# the registry call fails, exit with error message.
registry_exists () {
    ref=$1
    if out=$(docker manifest inspect "$ref" 2>&1); then
        return 0  # present
    elif ! ( echo "$out" | grep -Eq '^no such manifest:' ); then
        echo "bad response from registry:" "$out" 1>&2
        exit 1
    else
        return 1  # not present
    fi
}

tag_from_filename () {
    basename "$1" | sed -E 's/^(.+)\.df$/ci_\1/g'
}

