diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41681e5..6bf4488 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,23 +11,27 @@ env: && startsWith(github.ref, 'refs/tags/alpine') }} jobs: - docker: + buildx: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: docker/setup-qemu-action@v1 - uses: docker/setup-buildx-action@v1 - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }} - - run: make docker.image no-cache=no tag=build-${{ github.run_number }} - if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }} + - name: Pre-build fresh Docker images cache + run: make docker.build.cache no-cache=yes - - run: make docker.image no-cache=yes tag=build-${{ github.run_number }} - if: ${{ env.PUBLISH == 'true' || github.ref == 'refs/heads/master' }} + - name: Test Docker images + run: | + # Enable experimental features of Docker Daemon to run multi-arch + # images. + echo "$(cat /etc/docker/daemon.json)" '{"experimental": true}' \ + | jq --slurp 'reduce .[] as $item ({}; . * $item)' \ + | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker - - run: make npm.install - - run: make test.docker tag=build-${{ github.run_number }} + make npm.install + make test.docker platforms=@all build=yes - name: Login to GitHub Container Registry uses: docker/login-action@v1 @@ -50,8 +54,6 @@ jobs: password: ${{ secrets.DOCKERHUB_BOT_PASS }} if: ${{ env.PUBLISH == 'true' }} - - run: make docker.tags of=build-${{ github.run_number }} - if: ${{ env.PUBLISH == 'true' }} - run: make docker.push if: ${{ env.PUBLISH == 'true' }} @@ -74,7 +76,7 @@ jobs: provider: dockerhub destination_container_repo: instrumentisto/rsync-ssh readme_file: README.md - if: ${{ env.PUBLISH == 'true' }}K + if: ${{ env.PUBLISH == 'true' }} - name: Parse release version from Git tag id: release diff --git a/CHANGELOG.md b/CHANGELOG.md index fd4e55a..b51fad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ All user visible changes to this project will be documented in this file. This p +## [alpine3.13-r1] · 2021-03-23 +[alpine3.13-r1]: /../../tree/alpine3.13-r1 + +[Diff](/../../compare/alpine3.13-r0...alpine3.13-r1) + +### Added + +- Support of `linux/arm64`, `linux/arm/v6`, `linux/arm/v7`, `linux/ppc64le` and `linux/s390x` platforms ([#5]). + +[#5]: /../../issues/5 + + + + ## [alpine3.13-r0] · 2021-03-23 [alpine3.13-r0]: /../../tree/alpine3.13-r0 diff --git a/Dockerfile b/Dockerfile index e7f5089..6faa763 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ ARG alpine_ver=3.13 FROM alpine:${alpine_ver} -ARG build_rev=0 +ARG build_rev=1 LABEL org.opencontainers.image.source="\ https://github.com/instrumentisto/rsync-ssh-docker-image" diff --git a/Makefile b/Makefile index 031c216..2a8dee0 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,13 @@ TAGS ?= alpine$(ALPINE_VER)-r$(BUILD_REV) \ alpine \ latest VERSION ?= $(word 1,$(subst $(comma), ,$(TAGS))) +PLATFORMS ?= linux/amd64 \ + linux/arm64 \ + linux/arm/v6 \ + linux/arm/v7 \ + linux/ppc64le \ + linux/s390x +MAIN_PLATFORM ?= $(word 1,$(subst $(comma), ,$(PLATFORMS))) @@ -43,8 +50,6 @@ push: docker.push release: git.release -tags: docker.tags - test: test.docker @@ -58,62 +63,86 @@ docker-namespaces = $(strip $(if $(call eq,$(namespaces),),\ $(NAMESPACES),$(subst $(comma), ,$(namespaces)))) docker-tags = $(strip $(if $(call eq,$(tags),),\ $(TAGS),$(subst $(comma), ,$(tags)))) +docker-platforms = $(strip $(if $(call eq,$(platforms),),\ + $(PLATFORMS),$(subst $(comma), ,$(platforms)))) - -# Build Docker image with the given tag. -# -# Usage: -# make docker.image [tag=($(VERSION)|)]] [no-cache=(no|yes)] -# [ALPINE_VER=] -# [BUILD_REV=] - -docker.image: - docker build --network=host --force-rm \ +# Runs `docker buildx build` command allowing to customize it for the purpose of +# re-tagging or pushing. +define docker.buildx + $(eval namespace := $(strip $(1))) + $(eval tag := $(strip $(2))) + $(eval platform := $(strip $(3))) + $(eval no-cache := $(strip $(4))) + $(eval args := $(strip $(5))) + docker buildx build --force-rm $(args) \ + --platform $(platform) \ $(if $(call eq,$(no-cache),yes),--no-cache --pull,) \ --build-arg alpine_ver=$(ALPINE_VER) \ --build-arg build_rev=$(BUILD_REV) \ - -t instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) ./ + -t $(namespace)/$(NAME):$(tag) . +endef -# Manually push Docker images to container registries. +# Pre-build cache for Docker image builds. +# +# WARNING: This command doesn't apply tag to the built Docker image, just +# creates a build cache. To produce a Docker image with a tag, use +# `docker.tag` command right after running this one. +# +# Usage: +# make docker.build.cache +# [platforms=($(PLATFORMS)|[,...])] +# [no-cache=(no|yes)] +# [ALPINE_VER=] +# [BUILD_REV=] + +docker.build.cache: + $(call docker.buildx,\ + instrumentisto,\ + build-cache,\ + $(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),\ + $(no-cache),\ + --output 'type=image$(comma)push=false') + + +# Build Docker image on the given platform with the given tag. # # Usage: -# make docker.push [tags=($(TAGS)|[,...])] -# [namespaces=($(NAMESPACES)|[,...])] +# make docker.image +# [tag=($(VERSION)|)] +# [platform=($(MAIN_PLATFORM)|)] +# [no-cache=(no|yes)] +# [ALPINE_VER=] +# [BUILD_REV=] -docker.push: - $(foreach tag,$(subst $(comma), ,$(docker-tags)),\ - $(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\ - $(call docker.push.do,$(namespace),$(tag)))) -define docker.push.do - $(eval repo := $(strip $(1))) - $(eval tag := $(strip $(2))) - docker push $(repo)/$(NAME):$(tag) -endef +docker.image: + $(call docker.buildx,\ + instrumentisto,\ + $(if $(call eq,$(tag),),$(VERSION),$(tag)),\ + $(if $(call eq,$(platform),),$(MAIN_PLATFORM),$(platform)),\ + $(no-cache),\ + --load) -# Tag Docker image with the given tags. +# Push Docker images to their repositories (container registries), +# along with the required multi-arch manifests. # # Usage: -# make docker.tags [of=($(VERSION)|)] -# [tags=($(TAGS)|[,...])] -# [namespaces=($(NAMESPACES)|[,...])] - -docker-tags-of = $(if $(call eq,$(of),),$(VERSION),$(of)) - -docker.tags: - $(foreach tag,$(subst $(comma), ,$(docker-tags)),\ - $(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\ - $(call docker.tags.do,$(docker-tags-of),$(namespace),$(tag)))) -define docker.tags.do - $(eval from := $(strip $(1))) - $(eval repo := $(strip $(2))) - $(eval to := $(strip $(3))) - docker tag instrumentisto/$(NAME):$(from) $(repo)/$(NAME):$(to) -endef - +# make docker.push +# [namespaces=($(NAMESPACES)|[,...])] +# [tags=($(TAGS)|[,...])] +# [platforms=($(PLATFORMS)|[,...])] +# [ALPINE_VER=] +# [BUILD_REV=] -docker.test: test.docker +docker.push: + $(foreach namespace,$(docker-namespaces),\ + $(foreach tag,$(docker-tags),\ + $(call docker.buildx,\ + $(namespace),\ + $(tag),\ + $(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),,\ + --push))) @@ -128,16 +157,37 @@ docker.test: test.docker # https://github.com/bats-core/bats-core # # Usage: -# make test.docker [tag=($(VERSION)|)] - +# make test.docker +# [tag=($(VERSION)|)] +# [platforms=($(MAIN_PLATFORM)|@all|[,...])] +# [( [build=no] +# | build=yes [HARAKA_VER=] +# [NODE_VER=] +# [BUILD_REV=] )] + +test-docker-platforms = $(strip $(if $(call eq,$(platforms),),$(MAIN_PLATFORM),\ + $(if $(call eq,$(platforms),@all),$(PLATFORMS),\ + $(docker-platforms)))) test.docker: ifeq ($(wildcard node_modules/.bin/bats),) @make npm.install endif - IMAGE=instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) \ + $(foreach platform,$(test-docker-platforms),\ + $(call test.docker.do,\ + $(if $(call eq,$(tag),),$(VERSION),$(tag)),\ + $(platform))) +define test.docker.do + $(eval tag := $(strip $(1))) + $(eval platform := $(strip $(2))) + $(if $(call eq,$(build),yes),\ + @make docker.image no-cache=no tag=$(tag) platform=$(platform) \ + ALPINE_VER=$(ALPINE_VER) \ + BUILD_REV=$(BUILD_REV) ,) + IMAGE=instrumentisto/$(NAME):$(tag) PLATFORM=$(platform) \ node_modules/.bin/bats \ --timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \ tests/main.bats +endef @@ -188,8 +238,8 @@ endif # .PHONY section # ################## -.PHONY: image push release tags test \ - docker.image docker.push docker.tags docker.test \ +.PHONY: image push release test \ + docker.build.cache docker.image docker.push \ git.release \ npm.install \ test.docker diff --git a/README.md b/README.md index f834fe9..3565e96 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Rsync + SSH Docker image ## Supported tags and respective `Dockerfile` links -- [`alpine3.13-r0`, `alpine3.13`, `alpine`, `latest`][d1] +- [`alpine3.13-r1`, `alpine3.13`, `alpine`, `latest`][d1] diff --git a/tests/main.bats b/tests/main.bats index 37ec912..074ed6e 100644 --- a/tests/main.bats +++ b/tests/main.bats @@ -1,18 +1,39 @@ #!/usr/bin/env bats +@test "Built on correct arch" { + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'uname -m' + [ "$status" -eq 0 ] + if [ "$PLATFORM" = "linux/amd64" ]; then + [ "$output" = "x86_64" ] + elif [ "$PLATFORM" = "linux/arm64" ]; then + [ "$output" = "aarch64" ] + elif [ "$PLATFORM" = "linux/arm/v6" ]; then + [ "$output" = "armv7l" ] + elif [ "$PLATFORM" = "linux/arm/v7" ]; then + [ "$output" = "armv7l" ] + else + [ "$output" = "$(echo $PLATFORM | cut -d '/' -f2-)" ] + fi +} + + @test "SSH is installed" { - run docker run --rm --entrypoint sh $IMAGE -c 'which ssh' + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'which ssh' [ "$status" -eq 0 ] } @test "rsync is installed" { - run docker run --rm --entrypoint sh $IMAGE -c 'which rsync' + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'which rsync' [ "$status" -eq 0 ] } @test "rsync runs ok" { - run docker run --rm --entrypoint sh $IMAGE -c 'rsync --help' + run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ + 'rsync --help' [ "$status" -eq 0 ] }