Skip to content

Commit b6c09cb

Browse files
committed
Support multi-arch builds (#5)
1 parent 3344d58 commit b6c09cb

6 files changed

Lines changed: 154 additions & 67 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,27 @@ env:
1111
&& startsWith(github.ref, 'refs/tags/alpine') }}
1212

1313
jobs:
14-
docker:
14+
buildx:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@v2
18+
- uses: docker/setup-qemu-action@v1
1819
- uses: docker/setup-buildx-action@v1
1920

20-
- uses: satackey/action-docker-layer-caching@v0.0.11
21-
continue-on-error: true
22-
if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }}
23-
- run: make docker.image no-cache=no tag=build-${{ github.run_number }}
24-
if: ${{ env.PUBLISH != 'true' && github.ref != 'refs/heads/master' }}
21+
- name: Pre-build fresh Docker images cache
22+
run: make docker.build.cache no-cache=yes
2523

26-
- run: make docker.image no-cache=yes tag=build-${{ github.run_number }}
27-
if: ${{ env.PUBLISH == 'true' || github.ref == 'refs/heads/master' }}
24+
- name: Test Docker images
25+
run: |
26+
# Enable experimental features of Docker Daemon to run multi-arch
27+
# images.
28+
echo "$(cat /etc/docker/daemon.json)" '{"experimental": true}' \
29+
| jq --slurp 'reduce .[] as $item ({}; . * $item)' \
30+
| sudo tee /etc/docker/daemon.json
31+
sudo systemctl restart docker
2832
29-
- run: make npm.install
30-
- run: make test.docker tag=build-${{ github.run_number }}
33+
make npm.install
34+
make test.docker platforms=@all build=yes
3135
3236
- name: Login to GitHub Container Registry
3337
uses: docker/login-action@v1
@@ -50,8 +54,6 @@ jobs:
5054
password: ${{ secrets.DOCKERHUB_BOT_PASS }}
5155
if: ${{ env.PUBLISH == 'true' }}
5256

53-
- run: make docker.tags of=build-${{ github.run_number }}
54-
if: ${{ env.PUBLISH == 'true' }}
5557
- run: make docker.push
5658
if: ${{ env.PUBLISH == 'true' }}
5759

@@ -74,7 +76,7 @@ jobs:
7476
provider: dockerhub
7577
destination_container_repo: instrumentisto/rsync-ssh
7678
readme_file: README.md
77-
if: ${{ env.PUBLISH == 'true' }}K
79+
if: ${{ env.PUBLISH == 'true' }}
7880

7981
- name: Parse release version from Git tag
8082
id: release

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ All user visible changes to this project will be documented in this file. This p
66

77

88

9+
## [alpine3.13-r1] · 2021-03-23
10+
[alpine3.13-r1]: /../../tree/alpine3.13-r1
11+
12+
[Diff](/../../compare/alpine3.13-r0...alpine3.13-r1)
13+
14+
### Added
15+
16+
- Support of `linux/arm64`, `linux/arm/v6`, `linux/arm/v7`, `linux/ppc64le` and `linux/s390x` platforms ([#5]).
17+
18+
[#5]: /../../issues/5
19+
20+
21+
22+
923
## [alpine3.13-r0] · 2021-03-23
1024
[alpine3.13-r0]: /../../tree/alpine3.13-r0
1125

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
ARG alpine_ver=3.13
33
FROM alpine:${alpine_ver}
44

5-
ARG build_rev=0
5+
ARG build_rev=1
66

77
LABEL org.opencontainers.image.source="\
88
https://github.com/instrumentisto/rsync-ssh-docker-image"

Makefile

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ TAGS ?= alpine$(ALPINE_VER)-r$(BUILD_REV) \
2929
alpine \
3030
latest
3131
VERSION ?= $(word 1,$(subst $(comma), ,$(TAGS)))
32+
PLATFORMS ?= linux/amd64 \
33+
linux/arm64 \
34+
linux/arm/v6 \
35+
linux/arm/v7 \
36+
linux/ppc64le \
37+
linux/s390x
38+
MAIN_PLATFORM ?= $(word 1,$(subst $(comma), ,$(PLATFORMS)))
3239

3340

3441

@@ -43,8 +50,6 @@ push: docker.push
4350

4451
release: git.release
4552

46-
tags: docker.tags
47-
4853
test: test.docker
4954

5055

@@ -58,62 +63,86 @@ docker-namespaces = $(strip $(if $(call eq,$(namespaces),),\
5863
$(NAMESPACES),$(subst $(comma), ,$(namespaces))))
5964
docker-tags = $(strip $(if $(call eq,$(tags),),\
6065
$(TAGS),$(subst $(comma), ,$(tags))))
66+
docker-platforms = $(strip $(if $(call eq,$(platforms),),\
67+
$(PLATFORMS),$(subst $(comma), ,$(platforms))))
6168

62-
63-
# Build Docker image with the given tag.
64-
#
65-
# Usage:
66-
# make docker.image [tag=($(VERSION)|<docker-tag>)]] [no-cache=(no|yes)]
67-
# [ALPINE_VER=<alpine-version>]
68-
# [BUILD_REV=<build-revision>]
69-
70-
docker.image:
71-
docker build --network=host --force-rm \
69+
# Runs `docker buildx build` command allowing to customize it for the purpose of
70+
# re-tagging or pushing.
71+
define docker.buildx
72+
$(eval namespace := $(strip $(1)))
73+
$(eval tag := $(strip $(2)))
74+
$(eval platform := $(strip $(3)))
75+
$(eval no-cache := $(strip $(4)))
76+
$(eval args := $(strip $(5)))
77+
docker buildx build --force-rm $(args) \
78+
--platform $(platform) \
7279
$(if $(call eq,$(no-cache),yes),--no-cache --pull,) \
7380
--build-arg alpine_ver=$(ALPINE_VER) \
7481
--build-arg build_rev=$(BUILD_REV) \
75-
-t instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) ./
82+
-t $(namespace)/$(NAME):$(tag) .
83+
endef
7684

7785

78-
# Manually push Docker images to container registries.
86+
# Pre-build cache for Docker image builds.
87+
#
88+
# WARNING: This command doesn't apply tag to the built Docker image, just
89+
# creates a build cache. To produce a Docker image with a tag, use
90+
# `docker.tag` command right after running this one.
91+
#
92+
# Usage:
93+
# make docker.build.cache
94+
# [platforms=($(PLATFORMS)|<platform-1>[,<platform-2>...])]
95+
# [no-cache=(no|yes)]
96+
# [ALPINE_VER=<alpine-version>]
97+
# [BUILD_REV=<build-revision>]
98+
99+
docker.build.cache:
100+
$(call docker.buildx,\
101+
instrumentisto,\
102+
build-cache,\
103+
$(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),\
104+
$(no-cache),\
105+
--output 'type=image$(comma)push=false')
106+
107+
108+
# Build Docker image on the given platform with the given tag.
79109
#
80110
# Usage:
81-
# make docker.push [tags=($(TAGS)|<docker-tag-1>[,<docker-tag-2>...])]
82-
# [namespaces=($(NAMESPACES)|<prefix-1>[,<prefix-2>...])]
111+
# make docker.image
112+
# [tag=($(VERSION)|<tag>)]
113+
# [platform=($(MAIN_PLATFORM)|<platform>)]
114+
# [no-cache=(no|yes)]
115+
# [ALPINE_VER=<alpine-version>]
116+
# [BUILD_REV=<build-revision>]
83117

84-
docker.push:
85-
$(foreach tag,$(subst $(comma), ,$(docker-tags)),\
86-
$(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\
87-
$(call docker.push.do,$(namespace),$(tag))))
88-
define docker.push.do
89-
$(eval repo := $(strip $(1)))
90-
$(eval tag := $(strip $(2)))
91-
docker push $(repo)/$(NAME):$(tag)
92-
endef
118+
docker.image:
119+
$(call docker.buildx,\
120+
instrumentisto,\
121+
$(if $(call eq,$(tag),),$(VERSION),$(tag)),\
122+
$(if $(call eq,$(platform),),$(MAIN_PLATFORM),$(platform)),\
123+
$(no-cache),\
124+
--load)
93125

94126

95-
# Tag Docker image with the given tags.
127+
# Push Docker images to their repositories (container registries),
128+
# along with the required multi-arch manifests.
96129
#
97130
# Usage:
98-
# make docker.tags [of=($(VERSION)|<docker-tag>)]
99-
# [tags=($(TAGS)|<docker-tag-1>[,<docker-tag-2>...])]
100-
# [namespaces=($(NAMESPACES)|<prefix-1>[,<prefix-2>...])]
101-
102-
docker-tags-of = $(if $(call eq,$(of),),$(VERSION),$(of))
103-
104-
docker.tags:
105-
$(foreach tag,$(subst $(comma), ,$(docker-tags)),\
106-
$(foreach namespace,$(subst $(comma), ,$(docker-namespaces)),\
107-
$(call docker.tags.do,$(docker-tags-of),$(namespace),$(tag))))
108-
define docker.tags.do
109-
$(eval from := $(strip $(1)))
110-
$(eval repo := $(strip $(2)))
111-
$(eval to := $(strip $(3)))
112-
docker tag instrumentisto/$(NAME):$(from) $(repo)/$(NAME):$(to)
113-
endef
114-
131+
# make docker.push
132+
# [namespaces=($(NAMESPACES)|<prefix-1>[,<prefix-2>...])]
133+
# [tags=($(TAGS)|<tag-1>[,<tag-2>...])]
134+
# [platforms=($(PLATFORMS)|<platform-1>[,<platform-2>...])]
135+
# [ALPINE_VER=<alpine-version>]
136+
# [BUILD_REV=<build-revision>]
115137

116-
docker.test: test.docker
138+
docker.push:
139+
$(foreach namespace,$(docker-namespaces),\
140+
$(foreach tag,$(docker-tags),\
141+
$(call docker.buildx,\
142+
$(namespace),\
143+
$(tag),\
144+
$(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),,\
145+
--push)))
117146

118147

119148

@@ -128,16 +157,37 @@ docker.test: test.docker
128157
# https://github.com/bats-core/bats-core
129158
#
130159
# Usage:
131-
# make test.docker [tag=($(VERSION)|<tag>)]
132-
160+
# make test.docker
161+
# [tag=($(VERSION)|<tag>)]
162+
# [platforms=($(MAIN_PLATFORM)|@all|<platform-1>[,<platform-2>...])]
163+
# [( [build=no]
164+
# | build=yes [HARAKA_VER=<haraka-version>]
165+
# [NODE_VER=<node-version>]
166+
# [BUILD_REV=<build-revision>] )]
167+
168+
test-docker-platforms = $(strip $(if $(call eq,$(platforms),),$(MAIN_PLATFORM),\
169+
$(if $(call eq,$(platforms),@all),$(PLATFORMS),\
170+
$(docker-platforms))))
133171
test.docker:
134172
ifeq ($(wildcard node_modules/.bin/bats),)
135173
@make npm.install
136174
endif
137-
IMAGE=instrumentisto/$(NAME):$(if $(call eq,$(tag),),$(VERSION),$(tag)) \
175+
$(foreach platform,$(test-docker-platforms),\
176+
$(call test.docker.do,\
177+
$(if $(call eq,$(tag),),$(VERSION),$(tag)),\
178+
$(platform)))
179+
define test.docker.do
180+
$(eval tag := $(strip $(1)))
181+
$(eval platform := $(strip $(2)))
182+
$(if $(call eq,$(build),yes),\
183+
@make docker.image no-cache=no tag=$(tag) platform=$(platform) \
184+
ALPINE_VER=$(ALPINE_VER) \
185+
BUILD_REV=$(BUILD_REV) ,)
186+
IMAGE=instrumentisto/$(NAME):$(tag) PLATFORM=$(platform) \
138187
node_modules/.bin/bats \
139188
--timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \
140189
tests/main.bats
190+
endef
141191

142192

143193

@@ -188,8 +238,8 @@ endif
188238
# .PHONY section #
189239
##################
190240

191-
.PHONY: image push release tags test \
192-
docker.image docker.push docker.tags docker.test \
241+
.PHONY: image push release test \
242+
docker.build.cache docker.image docker.push \
193243
git.release \
194244
npm.install \
195245
test.docker

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Rsync + SSH Docker image
1616

1717
## Supported tags and respective `Dockerfile` links
1818

19-
- [`alpine3.13-r0`, `alpine3.13`, `alpine`, `latest`][d1]
19+
- [`alpine3.13-r1`, `alpine3.13`, `alpine`, `latest`][d1]
2020

2121

2222

tests/main.bats

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,39 @@
11
#!/usr/bin/env bats
22

33

4+
@test "Built on correct arch" {
5+
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \
6+
'uname -m'
7+
[ "$status" -eq 0 ]
8+
if [ "$PLATFORM" = "linux/amd64" ]; then
9+
[ "$output" = "x86_64" ]
10+
elif [ "$PLATFORM" = "linux/arm64" ]; then
11+
[ "$output" = "aarch64" ]
12+
elif [ "$PLATFORM" = "linux/arm/v6" ]; then
13+
[ "$output" = "armv7l" ]
14+
elif [ "$PLATFORM" = "linux/arm/v7" ]; then
15+
[ "$output" = "armv7l" ]
16+
else
17+
[ "$output" = "$(echo $PLATFORM | cut -d '/' -f2-)" ]
18+
fi
19+
}
20+
21+
422
@test "SSH is installed" {
5-
run docker run --rm --entrypoint sh $IMAGE -c 'which ssh'
23+
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \
24+
'which ssh'
625
[ "$status" -eq 0 ]
726
}
827

928

1029
@test "rsync is installed" {
11-
run docker run --rm --entrypoint sh $IMAGE -c 'which rsync'
30+
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \
31+
'which rsync'
1232
[ "$status" -eq 0 ]
1333
}
1434

1535
@test "rsync runs ok" {
16-
run docker run --rm --entrypoint sh $IMAGE -c 'rsync --help'
36+
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \
37+
'rsync --help'
1738
[ "$status" -eq 0 ]
1839
}

0 commit comments

Comments
 (0)