Skip to content

Commit

Permalink
Split linux build into debian & alpine
Browse files Browse the repository at this point in the history
  • Loading branch information
vickunwu committed Jan 17, 2025
1 parent 342bbc0 commit e5e5254
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 36 deletions.
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Ignore everything
*

// Allow what is needed
!crates
!tests
!resources/docker/entrypoint.sh

!Cargo.lock
!Cargo.toml
125 changes: 89 additions & 36 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,28 @@ jobs:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
- target: x86_64-unknown-linux-musl
- target: aarch64-unknown-linux-gnu
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
- target: aarch64-unknown-linux-musl
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
- target: armv7-unknown-linux-gnueabihf
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
- target: armv7-unknown-linux-musleabihf
build_env: "JEMALLOC_SYS_WITH_LG_PAGE=16"
- target: arm-unknown-linux-gnueabihf
- target: arm-unknown-linux-musleabihf
- variant: debian
- variant: alpine

name: Build / ${{matrix.target}}
name: Build / ${{matrix.variant}}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: "arm64,arm"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
# buildkitd-config-inline: |
# [worker.oci]
# max-parallelism = 2
driver-opts: |
network=host
- name: Log In to GitHub Container Registry
uses: docker/login-action@v3
Expand All @@ -82,26 +80,21 @@ jobs:
path: |
var-cache-apt
var-lib-apt
key: docker-apt-cache-${{ hashFiles('Dockerfile.build') }}
key: apt-cache-${{ hashFiles('Dockerfile.debian') }}-${{ hashFiles('Dockerfile.alpine') }}

- name: Calculate shasum of external deps
id: cal-dep-shasum
run: |
echo "checksum=$(yq -p toml -oy '.package[] | select((.source | contains("")) or (.checksum | contains("")))' Cargo.lock | sha256sum | awk '{print $1}')" >> "$GITHUB_OUTPUT"
- name: Get latest zig version
id: zig-version
run: |
echo "zig_version=$(curl -s "https://api.github.com/repos/ziglang/zig/releases/latest" | jq -r '.tag_name')" >> "$GITHUB_OUTPUT"
- name: Cache Cargo
uses: actions/cache@v4
id: cargo-cache
with:
path: |
usr-local-cargo-registry
usr-local-cargo-git
key: docker-cargo-cache-${{ steps.cal-dep-shasum.outputs.checksum }}
key: cargo-cache-${{ steps.cal-dep-shasum.outputs.checksum }}

- name: Inject cache into docker
uses: reproducible-containers/[email protected]
Expand All @@ -123,7 +116,7 @@ jobs:
${{github.repository}}
ghcr.io/${{github.repository}}
flavor: |
suffix=${{ endsWith(matrix.target, 'musl') && '-alpine' || '' }},onlatest=true
suffix=${{ endsWith(matrix.variant, 'alpine') && '-alpine' || '' }},onlatest=true
tags: |
type=ref,event=tag
type=edge,branch=main
Expand All @@ -133,44 +126,104 @@ jobs:
id: build
uses: docker/bake-action@v6
env:
TARGET: ${{matrix.target}}
DOCKER_BUILD_RECORD_UPLOAD: false
ARTIFACT_REPO: ghcr.io/${{github.repository}}
BUILD_ENV: ${{matrix.build_env}}
ZIG_VERSION: ${{ steps.zig-version.outputs.zig_version }}
DOCKER_PLATFORM: ${{ startsWith(matrix.target, 'x86') && 'amd64' || 'arm64' }}
with:
source: .
provenance: false
files: |
docker-bake.hcl
bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: ${{(github.event_name == 'push' || inputs.Docker) && (startsWith(matrix.target, 'x86') || startsWith(matrix.target, 'aarch64')) && 'build,image' || 'build'}}
targets: ${{ (github.event_name == 'push' || inputs.Docker) && format('build-{0},image-{0}', matrix.variant) || format('build-{0}', matrix.variant) }}

- name: Upload Artifacts
- name: Generate target mappings
id: map-targets
run: |
cd ${{ matrix.variant }}
declare -A TRIPLET_MAP
if [ "${{ matrix.variant }}" = "debian" ]; then
TRIPLET_MAP=(
["linux_amd64"]="x86_64-unknown-linux-gnu"
["linux_arm64"]="aarch64-unknown-linux-gnu"
["linux_arm_v7"]="armv7-unknown-linux-gnueabihf"
["linux_arm_v6"]="arm-unknown-linux-gnueabihf"
)
else
TRIPLET_MAP=(
["linux_amd64"]="x86_64-unknown-linux-musl"
["linux_arm64"]="aarch64-unknown-linux-musl"
["linux_arm_v7"]="armv7-unknown-linux-musleabihf"
["linux_arm_v6"]="arm-unknown-linux-musleabihf"
)
fi
targets=()
for dir in linux_*; do
if [ -d "$dir" ]; then
if [ -n "${TRIPLET_MAP[$dir]}" ]; then
targets+=("{\"folder\":\"$dir\",\"triplet\":\"${TRIPLET_MAP[$dir]}\"}")
else
echo "Warning: Unknown directory format: $dir"
fi
fi
done
json="["
json+=$(IFS=,; echo "${targets[*]}")
json+="]"
echo "target_mappings=$json" >> $GITHUB_OUTPUT
echo "Generated mappings:"
echo "$json"
# Upload for x86_64
- name: Upload x86_64 artifact
uses: actions/upload-artifact@v4
with:
name: ${{matrix.target}}
path: |
artifact
!artifact/*.json
name: ${{ fromJson(steps.map-targets.outputs.target_mappings)[0].triplet }}
path: ${{ matrix.variant }}/${{ fromJson(steps.map-targets.outputs.target_mappings)[0].folder }}

# Upload for aarch64
- name: Upload aarch64 artifact
uses: actions/upload-artifact@v4
with:
name: ${{ fromJson(steps.map-targets.outputs.target_mappings)[1].triplet }}
path: ${{ matrix.variant }}/${{ fromJson(steps.map-targets.outputs.target_mappings)[1].folder }}

# Upload for armv7
- name: Upload armv7 artifact
uses: actions/upload-artifact@v4
with:
name: ${{ fromJson(steps.map-targets.outputs.target_mappings)[2].triplet }}
path: ${{ matrix.variant }}/${{ fromJson(steps.map-targets.outputs.target_mappings)[2].folder }}

# Upload for arm
- name: Upload arm artifact
uses: actions/upload-artifact@v4
with:
name: ${{ fromJson(steps.map-targets.outputs.target_mappings)[3].triplet }}
path: ${{ matrix.variant }}/${{ fromJson(steps.map-targets.outputs.target_mappings)[3].folder }}

- name: Get image digest
id: image-digest
if: (github.event_name == 'push' || inputs.Docker) && (startsWith(matrix.target, 'x86') || startsWith(matrix.target, 'aarch64'))
if: github.event_name == 'push' || inputs.Docker
run: |
echo "image_digest=$(echo '${{ steps.build.outputs.metadata }}' | jq -r '.image.["containerimage.digest"]')" >> "$GITHUB_OUTPUT"
echo "image_digest=$(echo '${{ steps.build.outputs.metadata }}' | jq -r '.image-${{matrix.variant}}.["containerimage.digest"]')" >> "$GITHUB_OUTPUT"
- name: Attest Dockerhub
uses: actions/attest-build-provenance@v2
if: (github.event_name == 'push' || inputs.Docker) && (startsWith(matrix.target, 'x86') || startsWith(matrix.target, 'aarch64'))
if: github.event_name == 'push' || inputs.Docker
with:
subject-name: index.docker.io/${{github.repository}}
subject-digest: ${{ steps.image-digest.outputs.image_digest }}
push-to-registry: true

- name: Attest GHCR
uses: actions/attest-build-provenance@v2
if: (github.event_name == 'push' || inputs.Docker) && (startsWith(matrix.target, 'x86') || startsWith(matrix.target, 'aarch64'))
if: github.event_name == 'push' || inputs.Docker
with:
subject-name: ghcr.io/${{github.repository}}
subject-digest: ${{ steps.image-digest.outputs.image_digest }}
Expand Down
118 changes: 118 additions & 0 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# syntax=docker/dockerfile:1
# check=skip=FromPlatformFlagConstDisallowed,RedundantTargetPlatform

# *****************
# Base image for planner & builder
# *****************
FROM --platform=$BUILDPLATFORM docker.io/library/rust:slim-bookworm AS base

ENV DEBIAN_FRONTEND="noninteractive" \
RUSTC_WRAPPER="sccache" \
BINSTALL_DISABLE_TELEMETRY=true \
SCCACHE_GHA_ENABLED=true \
CARGO_TERM_COLOR=always \
LANG=C.UTF-8 \
TZ=UTC \
TERM=xterm-256color
# With zig, we only need libclang and make
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache && \
apt-get update && \
apt-get install -yq --no-install-recommends curl xz-utils make jq libclang-16-dev
# Install zig
RUN ZIG_VERSION=$(curl --retry 5 -sL "https://api.github.com/repos/ziglang/zig/releases/latest" | jq -r '.tag_name') && \
[ ! -z "$ZIG_VERSION" ] && \
curl --retry 5 -Ls "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz" | tar -J -x -C /usr/local && \
ln -s "/usr/local/zig-linux-$(uname -m)-${ZIG_VERSION}/zig" /usr/local/bin/zig
# Install cargo-binstall
RUN curl --retry 5 -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
# Install cargo-chef & sccache & cargo-zigbuild
RUN cargo binstall --no-confirm cargo-chef sccache cargo-zigbuild

# *****************
# Planner
# *****************
FROM base AS planner
WORKDIR /app
COPY . .
# Generate recipe file
RUN cargo chef prepare --recipe-path recipe.json

# *****************
# Builder
# *****************
FROM base AS builder
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
ARG TARGETPLATFORM
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Generate musl build env
RUN case "$TARGETPLATFORM" in \
"linux/amd64") \
echo "export CARGO_TARGET=x86_64-unknown-linux-musl" >> /build-env \
;; \
"linux/arm64") \
echo "export CARGO_TARGET=aarch64-unknown-linux-musl" >> /build-env \
echo "export JEMALLOC_SYS_WITH_LG_PAGE=16" >> /build-env \
;; \
"linux/arm/v7") \
echo "export CARGO_TARGET=armv7-unknown-linux-musleabihf" >> /build-env \
echo "export JEMALLOC_SYS_WITH_LG_PAGE=16" >> /build-env \
;; \
"linux/arm/v6") \
echo "export CARGO_TARGET=arm-unknown-linux-musleabihf" >> /build-env \
;; \
*) \
echo "Error: Unsupported platform $TARGETPLATFORM" && exit 1 \
;; \
esac && \
cat /build-env
RUN source /build-env && \
rustup target add "${CARGO_TARGET}" && \
mkdir -p artifact
# Cargo-chef Cache Layer - 1
RUN --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
--mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
source /build-env && \
cargo chef cook --recipe-path recipe.json --zigbuild --release --target "${CARGO_TARGET}" -p mail-server -p stalwart-cli
# Copy the source code
COPY . .
# Build generic version
RUN --mount=type=secret,id=ACTIONS_CACHE_URL,env=ACTIONS_CACHE_URL \
--mount=type=secret,id=ACTIONS_RUNTIME_TOKEN,env=ACTIONS_RUNTIME_TOKEN \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
source /build-env && \
cargo zigbuild --release --target ${CARGO_TARGET} -p mail-server -p stalwart-cli && \
mv /app/target/${CARGO_TARGET}/release/stalwart-mail /app/artifact/stalwart-mail && \
mv /app/target/${CARGO_TARGET}/release/stalwart-cli /app/artifact/stalwart-cli

# *****************
# Binary stage
# *****************
FROM scratch AS binaries
COPY --from=builder /app/artifact /

# *****************
# Runtime image for musl targets
# *****************
FROM --platform=$TARGETPLATFORM alpine AS runtime
WORKDIR /opt/stalwart-mail
RUN apk add --update --no-cache ca-certificates tzdata && rm -rf /var/cache/apk/*
COPY --from=binaries /stalwart-mail /usr/local/bin
COPY --from=binaries /stalwart-cli /usr/local/bin
COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod -R 755 /usr/local/bin
CMD ["/usr/local/bin/stalwart-mail"]
VOLUME [ "/opt/stalwart-mail" ]
EXPOSE 443 25 110 587 465 143 993 995 4190 8080
ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"]
# FIXME 需要将所有的export输入到一个文本然后在需要的地方source
# 修改glare 用jq
# 需要分别有两个dockerfile docker 脚本解析 TARGETPLATFORM 来生成 rust-target
# 修改upload-artifact * 3,在其之前添加解析name的action脚本
Loading

0 comments on commit e5e5254

Please sign in to comment.