Skip to content

Commit c1f9221

Browse files
feat: docker multiplatform
1 parent 06a67a4 commit c1f9221

File tree

3 files changed

+69
-56
lines changed

3 files changed

+69
-56
lines changed

.github/workflows/build.yml

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ jobs:
410410
args: --workspace --all-targets --all-features --no-deps
411411

412412
docker-build:
413-
name: Build Docker container for ${{ matrix.platform.docker }}-${{ matrix.platform.rust }}
413+
name: Build Docker container
414414
runs-on: ubuntu-latest
415415
needs:
416416
- calculate-version
@@ -419,27 +419,13 @@ jobs:
419419
env:
420420
APPLICATION_NAME: PLACEHOLDER # overridden in step 'Set application name', this is merely to satisfy the linter
421421
PATH_TO_TAR: PLACEHOLDER # same ^
422-
PLATFORM_PAIR: PLACEHOLDER # same ^
423-
PLATFORM_UNIQUE_TAG: PLACEHOLDER # same ^
424-
strategy:
425-
fail-fast: false
426-
matrix:
427-
platform:
428-
- docker: linux/amd64
429-
rust: x86_64-unknown-linux-musl
430-
- docker: linux/arm64
431-
rust: aarch64-unknown-linux-musl
422+
UNIQUE_TAG: PLACEHOLDER # same ^
432423
steps:
433424
- name: Checkout
434425
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
435426
with:
436427
show-progress: false
437428

438-
- name: Prepare name
439-
run: |
440-
platform=${{ matrix.platform.docker }}-${{ matrix.platform.rust }}
441-
echo "PLATFORM_PAIR=${platform//\//-}" >> ${GITHUB_ENV}
442-
443429
- name: Set the Cargo.toml version before we copy in the data into the Docker container
444430
shell: bash
445431
run: |
@@ -458,8 +444,8 @@ jobs:
458444
- name: Set Docker tag
459445
shell: bash
460446
run: |
461-
PLATFORM_UNIQUE_TAG=pr-${{ github.event.pull_request.base.sha }}-${{ github.event.pull_request.head.sha }}-${{ env.PLATFORM_PAIR }}
462-
echo "PLATFORM_UNIQUE_TAG=${PLATFORM_UNIQUE_TAG##*/}" >> ${GITHUB_ENV}
447+
UNIQUE_TAG=pr-${{ github.event.pull_request.base.sha }}-${{ github.event.pull_request.head.sha }}
448+
echo "UNIQUE_TAG=${UNIQUE_TAG##*/}" >> ${GITHUB_ENV}
463449
464450
# Extract metadata (tags, labels) for Docker
465451
# https://github.com/docker/metadata-action
@@ -469,7 +455,7 @@ jobs:
469455
with:
470456
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
471457
tags: |
472-
type=raw,value=${{ env.PLATFORM_UNIQUE_TAG }}
458+
type=raw,value=${{ env.UNIQUE_TAG }}
473459
labels: |
474460
org.opencontainers.image.version=pr-${{ github.event.number }}
475461
org.opencontainers.image.source=${{ github.event.pull_request.html_url }}
@@ -492,38 +478,35 @@ jobs:
492478
with:
493479
build-args: |
494480
APPLICATION_NAME=${{ env.APPLICATION_NAME }}
495-
TARGET=${{ matrix.platform.rust }}
496481
context: .
497482
# this container is THE PR's artifact, and we will re-tag it
498483
# once the PR has been accepted
499484
tags: ${{ steps.meta.outputs.tags }}
500485
labels: ${{ steps.meta.outputs.labels }}
501-
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ env.PLATFORM_PAIR }}
502-
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max
503-
platforms: ${{ matrix.platform.docker }}
504-
outputs: type=docker,dest=/tmp/${{ env.PLATFORM_UNIQUE_TAG }}.tar
486+
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ env.APPLICATION_NAME }}
487+
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-${{ env.APPLICATION_NAME }},mode=max
488+
platforms: linux/amd64, linux/arm64
489+
outputs: type=oci,dest=/tmp/${{ env.UNIQUE_TAG }}.tar
505490

506491
- name: Upload artifact
507492
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
508493
with:
509-
name: containers-${{ env.PLATFORM_PAIR }}
510-
path: /tmp/${{ env.PLATFORM_UNIQUE_TAG }}.tar
494+
name: containers-${{ env.APPLICATION_NAME }}
495+
path: /tmp/${{ env.UNIQUE_TAG }}.tar
511496
if-no-files-found: error
512497
retention-days: 1
513498

514499
docker-publish:
515500
name: Publish Docker container
516501
runs-on: ubuntu-latest
517502
needs:
518-
- cargo-build
519-
- cargo-fmt
520-
- cargo-test-and-report
521-
- cargo-clippy-and-report
522503
- docker-build
523504
# Check if the event is not triggered by a fork
524505
if: |
525506
github.event.pull_request.head.repo.full_name == github.repository &&
526507
github.event_name == 'pull_request'
508+
env:
509+
APPLICATION_NAME: PLACEHOLDER # overridden in step 'Set application name', this is merely to satisfy the linter
527510
steps:
528511
- name: Set up Docker Buildx
529512
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
@@ -542,22 +525,31 @@ jobs:
542525
username: ${{ github.actor }}
543526
password: ${{ secrets.GITHUB_TOKEN }}
544527

528+
- name: Set application name
529+
shell: bash
530+
run: |
531+
APPLICATION_NAME=${{ github.repository }}
532+
echo "APPLICATION_NAME=${APPLICATION_NAME##*/}" >> ${GITHUB_ENV}
533+
545534
- name: Lowercase the image name
546535
shell: bash
547536
run: |
548537
echo "IMAGE_NAME=${IMAGE_NAME,,}" >> ${GITHUB_ENV}
549538
550539
- name: Load images from artifacts
551540
shell: bash
541+
id: image
542+
working-directory: /tmp/containers
552543
run: |
553-
ls -l /tmp/containers/
544+
echo "${{ secrets.GITHUB_TOKEN }}" | oras login -u "${{ github.actor }}" --password-stdin ${{ env.REGISTRY }}
545+
546+
ls -l /tmp/containers
554547
for container in /tmp/containers/*
555548
do
556-
echo $container
557-
docker load --input $container
549+
echo "Found ${container}"
558550
tag=$(basename -- $container .tar)
559-
echo ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:${tag}
560-
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${tag}
551+
552+
oras copy --from-oci-layout "${container}:${tag}" "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${tag}"
561553
done
562554
563555
- name: Extract Docker metadata
@@ -574,18 +566,13 @@ jobs:
574566
working-directory: /tmp/containers
575567
run: |
576568
# all files in dir
577-
platform_tags=(*)
578-
569+
containers=(*)
579570
# yeet extension
580-
platform_tags=${platform_tags[@]%.tar}
581-
571+
containers=${containers[@]%.tar}
582572
new_tags="${{ join(steps.meta.outputs.tags, ' ') }}"
583573
new_tags=$(printf -- '--tag %s ' $new_tags)
584-
585-
expanded_platform_tags=$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:%s ' $platform_tags)
586-
587-
docker buildx imagetools create $new_tags $expanded_platform_tags
588-
574+
expanded_containters_tags=$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:%s ' ${containers})
575+
docker buildx imagetools create $new_tags $expanded_containters_tags
589576
for new_tag in $(echo "${{ join(steps.meta.outputs.tags, ' ') }}"); do
590577
docker buildx imagetools inspect --raw $new_tag
591578
done

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,5 @@ color-eyre = "0.6.3"
4747
# BUT that means that we need to include openssl
4848
# Documentation on the syntax:
4949
# https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies
50-
[target.'cfg(all(target_arch = "x86_64", target_os="linux", target_env="musl"))'.dependencies]
50+
[target.'cfg(all(any(target_arch="x86_64", target_arch="aarch64"), target_os="linux", target_env="musl"))'.dependencies]
5151
# openssl = { version = "0.10.36", features = ["vendored"] }

Dockerfile

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1-
FROM --platform=$BUILDPLATFORM rust:1.83.0@sha256:39a313498ed0d74ccc01efb98ec5957462ac5a43d0ef73a6878f745b45ebfd2c AS builder
1+
FROM --platform=${BUILDPLATFORM} rust:1.83.0@sha256:f5375f865a8a5a734b9b9a38d58cd322d6a2eb8bb1aea8def32b89837258e7f8 AS rust-base
22

3-
ARG TARGET=x86_64-unknown-linux-musl
43
ARG APPLICATION_NAME
54

6-
RUN rustup target add ${TARGET}
7-
8-
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
5+
RUN rm -f /etc/apt/apt.conf.d/docker-clean \
6+
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache
97

108
# borrowed (Ba Dum Tss!) from
119
# https://github.com/pablodeymo/rust-musl-builder/blob/7a7ea3e909b1ef00c177d9eeac32d8c9d7d6a08c/Dockerfile#L48-L49
12-
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
13-
dpkg --add-architecture arm64 && \
10+
RUN --mount=type=cache,id=apt-cache-amd64,target=/var/cache/apt,sharing=locked \
11+
--mount=type=cache,id=apt-lib-amd64,target=/var/lib/apt,sharing=locked \
1412
apt-get update && \
1513
apt-get --no-install-recommends install -y \
1614
build-essential \
1715
musl-dev \
18-
musl-tools \
16+
musl-tools
17+
18+
FROM rust-base AS rust-linux-amd64
19+
ARG TARGET=x86_64-unknown-linux-musl
20+
21+
FROM rust-base AS rust-linux-arm64
22+
ARG TARGET=aarch64-unknown-linux-musl
23+
RUN --mount=type=cache,id=apt-cache-arm64,from=rust-base,source=/var/cache/apt,target=/var/cache/apt,sharing=locked \
24+
--mount=type=cache,id=apt-lib-arm64,from=rust-base,source=/var/lib/apt,target=/var/lib/apt,sharing=locked \
25+
dpkg --add-architecture arm64 && \
26+
apt-get update && \
27+
apt-get --no-install-recommends install -y \
1928
libc6-dev-arm64-cross \
2029
gcc-aarch64-linux-gnu
2130

31+
FROM rust-${TARGETPLATFORM//\//-} AS rust-cargo-build
32+
33+
RUN rustup target add ${TARGET} && rustup component add clippy rustfmt
34+
2235
# The following block
2336
# creates an empty app, and we copy in Cargo.toml and Cargo.lock as they represent our dependencies
2437
# This allows us to copy in the source in a different layer which in turn allows us to leverage Docker's layer caching
@@ -28,14 +41,26 @@ RUN cargo new ${APPLICATION_NAME}
2841
WORKDIR /build/${APPLICATION_NAME}
2942
COPY .cargo ./.cargo
3043
COPY Cargo.toml Cargo.lock ./
31-
RUN --mount=type=cache,id=cargo-dependencies,target=/build/${APPLICATION_NAME}/target \
44+
45+
RUN --mount=type=cache,target=/build/${APPLICATION_NAME}/target \
46+
--mount=type=cache,id=cargo-git,target=/usr/local/cargo/git/db,sharing=locked \
47+
--mount=type=cache,id=cargo-registery,target=/usr/local/cargo/registry/,sharing=locked \
3248
cargo build --release --target ${TARGET}
3349

50+
FROM rust-cargo-build AS rust-build
51+
52+
WORKDIR /build/${APPLICATION_NAME}
53+
3454
# now we copy in the source which is more prone to changes and build it
3555
COPY src ./src
3656

57+
# ensure cargo picks up on the change
58+
RUN touch ./src/main.rs
59+
3760
# --release not needed, it is implied with install
38-
RUN --mount=type=cache,id=full-build,target=/build/${APPLICATION_NAME}/target \
61+
RUN --mount=type=cache,target=/build/${APPLICATION_NAME}/target \
62+
--mount=type=cache,id=cargo-git,target=/usr/local/cargo/git/db,sharing=locked \
63+
--mount=type=cache,id=cargo-registery,target=/usr/local/cargo/registry/,sharing=locked \
3964
cargo install --path . --target ${TARGET} --root /output
4065

4166
FROM alpine:3.21.0@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45
@@ -46,7 +71,8 @@ RUN addgroup -S appgroup && adduser -S appuser -G appgroup
4671
USER appuser
4772

4873
WORKDIR /app
49-
COPY --from=builder /output/bin/${APPLICATION_NAME} /app/entrypoint
74+
75+
COPY --from=rust-build /output/bin/* /app/entrypoint
5076

5177
ENV RUST_BACKTRACE=full
5278
ENTRYPOINT ["/app/entrypoint"]

0 commit comments

Comments
 (0)