feat: use multi-runner on docker publish #31
Workflow file for this run
This file contains 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
# Build and publish a Docker image. | |
# | |
# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local | |
# artifacts job within `cargo-dist`. | |
# | |
# TODO(charlie): Ideally, the publish step would happen as a publish job within `cargo-dist`, but | |
# sharing the built image as an artifact between jobs is challenging. | |
name: "Build Docker image" | |
on: | |
workflow_call: | |
inputs: | |
plan: | |
required: true | |
type: string | |
pull_request: | |
paths: | |
- .github/workflows/build-docker.yml | |
env: | |
UV_BASE_IMG: ghcr.io/${{ github.repository_owner }}/uv | |
jobs: | |
docker-build: | |
name: Build Docker image (ghcr.io/astral-sh/uv) for ${{ matrix.platform }} | |
runs-on: ubuntu-latest | |
environment: | |
name: release | |
strategy: | |
fail-fast: false | |
matrix: | |
platform: | |
- linux/amd64 | |
- linux/arm64 | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- uses: docker/setup-buildx-action@v3 | |
- uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Extract metadata (tags, labels) for Docker | |
id: meta | |
uses: docker/metadata-action@v5 | |
with: | |
images: ${{ env.UV_BASE_IMG }} | |
- name: Check tag consistency | |
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} | |
run: | | |
version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g') | |
if [ "${{ fromJson(inputs.plan).announcement_tag }}" != "${version}" ]; then | |
echo "The input tag does not match the version from pyproject.toml:" >&2 | |
echo "${{ fromJson(inputs.plan).announcement_tag }}" >&2 | |
echo "${version}" >&2 | |
exit 1 | |
else | |
echo "Releasing ${version}" | |
fi | |
- name: Normalize Platform Pair (replace / with -) | |
run: | | |
platform=${{ matrix.platform }} | |
echo "PLATFORM_TUPLE=${platform//\//-}" >> $GITHUB_ENV | |
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ | |
- name: Build and push by digest | |
id: build | |
uses: docker/build-push-action@v6 | |
with: | |
context: . | |
platforms: ${{ matrix.platform }} | |
cache-from: type=gha,scope=uv-${{ env.PLATFORM_TUPLE }} | |
cache-to: type=gha,mode=min,scope=uv-${{ env.PLATFORM_TUPLE }} | |
labels: ${{ steps.meta.outputs.labels }} | |
outputs: type=image,name=${{ env.UV_BASE_IMG }},push-by-digest=true,name-canonical=true,push=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} | |
- name: Export digests | |
run: | | |
mkdir -p /tmp/digests | |
digest="${{ steps.build.outputs.digest }}" | |
touch "/tmp/digests/${digest#sha256:}" | |
- name: Upload digests | |
uses: actions/upload-artifact@v4 | |
with: | |
name: digests-${{ env.PLATFORM_TUPLE }} | |
path: /tmp/digests/* | |
if-no-files-found: error | |
retention-days: 1 | |
docker-publish: | |
name: Publish Docker image (ghcr.io/astral-sh/uv) | |
runs-on: ubuntu-latest | |
environment: | |
name: release | |
needs: | |
- docker-build | |
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} | |
steps: | |
- name: Download digests | |
uses: actions/download-artifact@v4 | |
with: | |
path: /tmp/digests | |
pattern: digests-* | |
merge-multiple: true | |
- uses: docker/setup-buildx-action@v3 | |
- name: Extract metadata (tags, labels) for Docker | |
id: meta | |
uses: docker/metadata-action@v5 | |
with: | |
images: ${{ env.UV_BASE_IMG }} | |
- uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ | |
- name: Create manifest list and push | |
working-directory: /tmp/digests | |
run: | | |
docker buildx imagetools create \ | |
-t ${{ env.UV_BASE_IMG }}:latest \ | |
-t ${{ env.UV_BASE_IMG }}:${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || 'dry-run' }} \ | |
$(printf '${{ env.UV_BASE_IMG }}@sha256:%s ' *) |