aboutsummaryrefslogtreecommitdiff
path: root/hooks/push
blob: 4b64fc3f674b55b9259cc16b6225a9f0d8ab7903 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/bin/bash

echo ">>> Pushing images..."

export DOCKER_CLI_EXPERIMENTAL=enabled

declare -A annotations=(
    [amd64]="--os linux --arch amd64"
    [arm32v6]="--os linux --arch arm --variant v6"
    [arm32v7]="--os linux --arch arm --variant v7"
    [arm64v8]="--os linux --arch arm64 --variant v8"
)

source ./hooks/arches.sh

set -ex

declare -A images
for arch in ${arches[@]}; do
    images[$arch]="${DOCKER_REPO}:${DOCKER_TAG}-${arch}"
done

# Push the images that were just built; manifest list creation fails if the
# images (manifests) referenced don't already exist in the Docker registry.
for image in "${images[@]}"; do
    docker push "${image}"
done

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

# If the Docker tag starts with a version number, assume the latest release is
# being pushed. Add an extra manifest (`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
        manifest_lists+=(${DOCKER_REPO}:alpine)
    else
        manifest_lists+=(${DOCKER_REPO}:latest)
    fi
fi

for manifest_list in "${manifest_lists[@]}"; do
    # Create the (multi-arch) manifest list of arch-specific images.
    docker manifest create ${manifest_list} ${images[@]}

    # Make sure each image manifest is annotated with the correct arch info.
    # Docker does not auto-detect the arch of each cross-compiled image, so
    # everything would appear as `linux/amd64` otherwise.
    for arch in "${arches[@]}"; do
        docker manifest annotate ${annotations[$arch]} ${manifest_list} ${images[$arch]}
    done

    # Push the manifest list.
    docker manifest push --purge ${manifest_list}
done

# Avoid logging credentials and tokens.
set +ex

# Delete the arch-specific tags, if credentials for doing so are available.
# Note that `DOCKER_PASSWORD` must be the actual user password. Passing a JWT
# obtained using a personal access token results in a 403 error with
# {"detail": "access to the resource is forbidden with personal access token"}
if [[ -z "${DOCKER_USERNAME}" || -z "${DOCKER_PASSWORD}" ]]; then
    exit 0
fi

# Given a JSON input on stdin, extract the string value associated with the
# specified key. This avoids an extra dependency on a tool like `jq`.
extract() {
    local key="$1"
    # Extract "<key>":"<val>" (assumes key/val won't contain double quotes).
    # The colon may have whitespace on either side.
    grep -o "\"${key}\"[[:space:]]*:[[:space:]]*\"[^\"]\+\"" |
    # Extract just <val> by deleting the last '"', and then greedily deleting
    # everything up to '"'.
    sed -e 's/"$//' -e 's/.*"//'
}

echo ">>> Getting API token..."
jwt=$(curl -sS -X POST \
           -H "Content-Type: application/json" \
           -d "{\"username\":\"${DOCKER_USERNAME}\",\"password\": \"${DOCKER_PASSWORD}\"}" \
           "https://hub.docker.com/v2/users/login" |
      extract 'token')

# Strip the registry portion from `index.docker.io/user/repo`.
repo="${DOCKER_REPO#*/}"

for arch in ${arches[@]}; do
    # Don't delete the `arm32v6` tag; Docker can't seem to properly
    # auto-select that image on Armv6 platforms like Raspberry Pi 1 and Zero
    # (https://github.com/moby/moby/issues/41017).
    if [[ ${arch} == 'arm32v6' ]]; then
        continue
    fi
    tag="${DOCKER_TAG}-${arch}"
    echo ">>> Deleting '${repo}:${tag}'..."
    curl -sS -X DELETE \
         -H "Authorization: Bearer ${jwt}" \
         "https://hub.docker.com/v2/repositories/${repo}/tags/${tag}/"
done