Build Docker images #2062
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |