Skip to content

Build Docker images #2062

Build Docker images

Build Docker images #2062

Workflow file for this run

name: Build Docker images
on:
push:
tags:
- "**[0-9]+.[0-9]+.[0-9]+*"
schedule:
- cron: "42 7 * * *" # run at 7:42 UTC (morning) every day
workflow_dispatch:
permissions:
contents: read
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
DOCKER_REPO: ${{ secrets.DOCKERHUB_USERNAME }}/cargo-chef
jobs:
resolve_inputs:
name: Resolve release version and Rust tag groups
runs-on: ubuntu-latest
outputs:
package_version: ${{ steps.collect.outputs.package_version }}
is_release_version: ${{ steps.collect.outputs.is_release_version }}
group_matrix: ${{ steps.collect.outputs.group_matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- id: collect
run: |
set -euo pipefail
LATEST_TAG=$(
git ls-remote --tags --refs origin \
| awk -F/ '{print $NF}' \
| sort -V \
| tail -n 1
)
CHEF_PACKAGE_VERSION=${LATEST_TAG#v}
echo "package_version=$CHEF_PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
if ! [[ "$CHEF_PACKAGE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version '$CHEF_PACKAGE_VERSION' is not a release semver. Skipping image publish."
echo "is_release_version=false" >> "$GITHUB_OUTPUT"
echo 'group_matrix=[]' >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Detected release version $CHEF_PACKAGE_VERSION"
echo "is_release_version=true" >> "$GITHUB_OUTPUT"
# Build one matrix row per official-images source group (GitCommit + Directory).
# All aliases in the same group are attached to the same canonical image.
GROUP_MATRIX=$(
curl --silent https://raw.githubusercontent.com/docker-library/official-images/master/library/rust \
| awk '
/^Tags:/ {
tags = substr($0, 7)
gsub(/, /, "\n", tags)
next
}
/^Architectures:/ {
arches = substr($0, 16)
gsub(/, /, ",", arches)
next
}
/^GitCommit:/ {
git = substr($0, 12)
next
}
/^Directory:/ {
dir = substr($0, 11)
n = split(tags, arr, "\n")
for (i = 1; i <= n; i++) {
tag = arr[i]
print git ":" dir "\t" tag "\t" arches
}
tags = ""
arches = ""
git = ""
dir = ""
}
' \
| sort -u \
| jq -R -s -c '
split("\n")
| map(select(length > 0))
| map(split("\t"))
| map({group_key: .[0], rust_image_tag: .[1], architectures: (.[2] // "" | split(",") | map(select(length > 0)))})
| group_by(.group_key)
| map({
group_key: .[0].group_key,
rust_aliases: (map(.rust_image_tag) | sort),
architectures: (map(.architectures) | add | unique | sort),
representative_rust_tag: (
map(.rust_image_tag)
| sort_by(
if test("^latest$") then [5, .]
elif test("^[0-9]+\\.[0-9]+\\.[0-9]+([-.].+)?$") then [1, .]
elif test("^[0-9]+\\.[0-9]+([-.].+)?$") then [2, .]
elif test("^[0-9]+([-.].+)?$") then [3, .]
else [4, .]
end
)
| .[0]
),
platforms: (
map(.architectures)
| add
| unique
| map(
if . == "amd64" then "linux/amd64"
elif . == "arm64v8" then "linux/arm64"
elif . == "arm32v7" then "linux/arm/v7"
elif . == "i386" then "linux/386"
else empty
end
)
| unique
| sort
)
})
'
)
if [ "$GROUP_MATRIX" = "[]" ] || [ -z "$GROUP_MATRIX" ]; then
echo "Failed to generate Rust image group matrix" >&2
exit 1
fi
echo "group_matrix=$GROUP_MATRIX" >> "$GITHUB_OUTPUT"
build_unique_images:
name: Build unique group images
needs: [resolve_inputs]
if: ${{ needs.resolve_inputs.outputs.is_release_version == 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
group_entry: ${{fromJSON(needs.resolve_inputs.outputs.group_matrix)}}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine if canonical image exists
id: canonical_status
run: |
CHEF_PACKAGE_VERSION=${{ needs.resolve_inputs.outputs.package_version }}
GROUP_KEY='${{ matrix.group_entry.group_key }}'
GROUP_KEY_TAG=$(printf '%s' "$GROUP_KEY" | shasum -a 256 | cut -c1-16)
CANONICAL_IMAGE=$DOCKER_REPO:$CHEF_PACKAGE_VERSION-base-$GROUP_KEY_TAG
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$CANONICAL_IMAGE" >/dev/null 2>&1; then
echo "Canonical image already exists for group $GROUP_KEY_TAG. Skipping build."
echo "result=skip" >> "$GITHUB_OUTPUT"
else
echo "Canonical image does not exist for group $GROUP_KEY_TAG. Building."
echo "result=true" >> "$GITHUB_OUTPUT"
fi
- name: Build and push canonical image
if: ${{ steps.canonical_status.outputs.result == 'true' }}
run: |
CHEF_PACKAGE_VERSION=${{ needs.resolve_inputs.outputs.package_version }}
RUST_IMAGE_TAG=${{ matrix.group_entry.representative_rust_tag }}
GROUP_KEY='${{ matrix.group_entry.group_key }}'
GROUP_KEY_TAG=$(printf '%s' "$GROUP_KEY" | shasum -a 256 | cut -c1-16)
CANONICAL_IMAGE=$DOCKER_REPO:$CHEF_PACKAGE_VERSION-base-$GROUP_KEY_TAG
PLATFORMS_JSON='${{ toJSON(matrix.group_entry.platforms) }}'
PLATFORMS=$(jq -r 'join(",")' <<< "$PLATFORMS_JSON")
if [ -z "$PLATFORMS" ] || [ "$PLATFORMS" = "null" ]; then
echo "No supported platforms found for group $GROUP_KEY_TAG. Skipping."
exit 0
fi
docker buildx build \
--tag $CANONICAL_IMAGE \
--build-arg=BASE_IMAGE=rust:$RUST_IMAGE_TAG \
--build-arg=CHEF_TAG=$CHEF_PACKAGE_VERSION \
--platform "$PLATFORMS" \
--push \
./docker
publish_group_aliases:
name: Publish group aliases
needs: [resolve_inputs, build_unique_images]
if: ${{ needs.resolve_inputs.outputs.is_release_version == 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
group_entry: ${{fromJSON(needs.resolve_inputs.outputs.group_matrix)}}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Publish aliases for Rust group
run: |
set -euo pipefail
CHEF_PACKAGE_VERSION=${{ needs.resolve_inputs.outputs.package_version }}
GROUP_KEY='${{ matrix.group_entry.group_key }}'
GROUP_KEY_TAG=$(printf '%s' "$GROUP_KEY" | shasum -a 256 | cut -c1-16)
CANONICAL_IMAGE=$DOCKER_REPO:$CHEF_PACKAGE_VERSION-base-$GROUP_KEY_TAG
RUST_ALIASES_JSON='${{ toJSON(matrix.group_entry.rust_aliases) }}'
mapfile -t RUST_ALIASES < <(jq -r '.[]' <<< "$RUST_ALIASES_JSON")
if [ "${#RUST_ALIASES[@]}" -eq 0 ]; then
echo "No aliases found for group $GROUP_KEY_TAG. Skipping."
exit 0
fi
tag_args=()
has_latest_alias=false
for RUST_IMAGE_TAG in "${RUST_ALIASES[@]}"; do
CHEF_IMAGE=$DOCKER_REPO:$CHEF_PACKAGE_VERSION-rust-$RUST_IMAGE_TAG
CHEF_IMAGE_LATEST=$DOCKER_REPO:latest-rust-$RUST_IMAGE_TAG
tag_args+=(--tag "$CHEF_IMAGE")
tag_args+=(--tag "$CHEF_IMAGE_LATEST")
if [ "$RUST_IMAGE_TAG" = "latest" ]; then
has_latest_alias=true
fi
done
if [ "$has_latest_alias" = "true" ]; then
tag_args+=(--tag "$DOCKER_REPO:latest")
fi
docker buildx imagetools create "${tag_args[@]}" "$CANONICAL_IMAGE"