diff --git a/.dockerignore b/.dockerignore index 0cace7a..8854504 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,4 @@ dumpcerts.sh acme.json acme-backup.json traefik-certs-dumper +build-docker.sh diff --git a/.travis.yml b/.travis.yml index b46a85b..dd201cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,8 @@ go: - 1.11.x - 1.x -sudo: false +services: + - docker env: - GO111MODULE=on @@ -23,6 +24,13 @@ install: - echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION" - go mod download +before_deploy: + - > + if ! [ "$BEFORE_DEPLOY_RUN" ]; then + export BEFORE_DEPLOY_RUN=1; + echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin + fi + deploy: - provider: script skip_cleanup: true @@ -30,3 +38,9 @@ deploy: on: tags: true condition: $TRAVIS_GO_VERSION =~ ^1\.x$ + - provider: script + skip_cleanup: true + script: make publish-images + on: + tags: true + condition: $TRAVIS_GO_VERSION =~ ^1\.x$ diff --git a/Dockerfile b/Dockerfile index 7d1ac0a..5c18d4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,10 @@ FROM golang:1-alpine as builder +ARG RUNTIME_HASH +ARG GOARCH +ARG GOARM +ARG GOOS + RUN apk --update upgrade \ && apk --no-cache --no-progress add git make gcc musl-dev @@ -10,9 +15,9 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN make build +RUN GOARCH=${GOARCH} GOARM=${GOARM} GOOS=${GOOS} make build -FROM alpine:3.9 +FROM alpine:3.9${RUNTIME_HASH} RUN apk --update upgrade \ && apk --no-cache --no-progress add ca-certificates diff --git a/Makefile b/Makefile index 00be093..796401a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,11 @@ clean: build: clean @echo Version: $(VERSION) $(BUILD_DATE) - go build -v -ldflags '-X "github.com/ldez/traefik-certs-dumper/cmd.version=${VERSION}" -X "github.com/ldez/traefik-certs-dumper/cmd.commit=${SHA}" -X "github.com/ldez/traefik-certs-dumper/cmd.date=${BUILD_DATE}"' + go build -v -ldflags '-X "github.com/ldez/traefik-certs-dumper/cmd.version=${VERSION}" -X "github.com/ldez/traefik-certs-dumper/cmd.commit=${SHA}" -X "github.com/ldez/traefik-certs-dumper/cmd.date=${BUILD_DATE}"' -o traefik-certs-dumper checks: golangci-lint run + +publish-images: + VERSION=$(TAG_NAME) ./build-docker.sh + VERSION="latest" ./build-docker.sh diff --git a/build-docker.sh b/build-docker.sh new file mode 100755 index 0000000..d3bc4ca --- /dev/null +++ b/build-docker.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +set -o errexit +set -o pipefail + +# safe guard +if [ -n "$TRAVIS_TAG" ] && [ -n "$VERSION" ]; then + echo "Deploying..." +else + echo "Skipping deploy" + exit 0 +fi + +# base docker image name +IMAGE_NAME="ldez/traefik-certs-dumper" + +# only linux for now +OS=linux + +# target platforms in docker manifest notation +declare -a PLATFORMS=( "amd64" "arm.v6" "arm.v7") + +# images from Dockerfile +FROM_IMAGE=$(grep "{RUNTIME_HASH}" < Dockerfile | sed "s/FROM //" | sed 's/\$.*//') + +# manifest cache file +MANIFEST_FILE=/tmp/tcd-manifest.${FROM_IMAGE}.json + +# get platform image hash from docker manifest +function platformHash () { + local ARCHITECTURE VARIANT HASH + read -r ARCHITECTURE VARIANT <<< "$@" + + if [ -z "$VARIANT" ]; then + HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\") | .digest" < "$MANIFEST_FILE") + else + HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\" and .platform.variant == \"$VARIANT\") | .digest" < "$MANIFEST_FILE") + fi + + echo "$HASH" +} + +# get manifest +if [ ! -f "$MANIFEST_FILE" ]; then + docker pull "$FROM_IMAGE" + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$FROM_IMAGE" > "$MANIFEST_FILE" +fi + +# create and push images +for platform in "${PLATFORMS[@]}"; do + # split architecture.version + IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform" + + # add xargs to trim whitespace + RUNTIME_HASH=$(platformHash "$ARCHITECTURE" "$VARIANT") + + # arm architectures flavors, strip "v" prefix + GOARM=${VARIANT:1} + + # build for target runtime image and architecture + docker build --build-arg="RUNTIME_HASH=@${RUNTIME_HASH}" --build-arg="GOARCH=${ARCHITECTURE}" --build-arg="GOARM=${GOARM}" -t "$IMAGE_NAME:${VERSION}-$platform" . + + # push images + docker push "$IMAGE_NAME:${VERSION}-$platform" +done + +# create manifest +TAG_LIST=$(printf "$IMAGE_NAME:${VERSION}-%s " "${PLATFORMS[@]}") +# shellcheck disable=SC2086 +DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create --amend "$IMAGE_NAME:${VERSION}" $TAG_LIST + +for platform in "${PLATFORMS[@]}"; do + # split architecture.version + IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform" + + # docker and go architectures don't match + if [ "arm" == "$ARCHITECTURE" ] && [ -n "$VARIANT" ]; then + VARIANT="$ARCHITECTURE$VARIANT" + fi + + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate "$IMAGE_NAME:${VERSION}" "$IMAGE_NAME:${VERSION}-$platform" --os "$OS" --arch "$ARCHITECTURE" --variant "$VARIANT" +done + +# push manifest +DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$IMAGE_NAME:${VERSION}"