name: Docker
on:
push:
branches:
- main
tags:
- "**"
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
defaults:
run:
shell: bash -euxo pipefail {0}
env:
DOCKERHUB_USERNAME: zhongruoyu
DOCKERHUB_REPOSITORY: zhongruoyu/shortener
GHCR_USERNAME: zhongruoyu
GHCR_REPOSITORY: ghcr.io/zhongruoyu/shortener
BUILD_CACHE_REPOSITORY: ghcr.io/zhongruoyu/shortener/cache
jobs:
metadata:
name: Determine metadata
runs-on: ubuntu-latest
outputs:
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
steps:
- name: Determine metadata
id: metadata
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf with:
images: |
${{ env.DOCKERHUB_REPOSITORY }}
${{ env.GHCR_REPOSITORY }}
flavor: |
latest=${{ github.event_name == 'push' && github.ref_type == 'tag' }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha,enable=${{ github.event_name == 'push' && github.ref_type == 'branch' }},prefix=,format=long
build:
needs: metadata
strategy:
fail-fast: false
matrix:
arch:
- x86_64
- aarch64
include:
- { arch: x86_64, runs-on: ubuntu-latest }
- { arch: aarch64, runs-on: ubuntu-24.04-arm }
name: Build (${{ matrix.arch }})
runs-on: ${{ matrix.runs-on }}
environment:
name: docker
deployment: false
permissions:
contents: read
packages: write steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with:
persist-credentials: false
- name: Cache Cargo registry
id: cache-cargo
if: (github.event_name == 'push' && github.ref_type == 'branch') || github.event_name == 'pull_request'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae with:
path: |
cargo-registry-index
cargo-registry-cache
cargo-git-db
key: docker-cargo-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
docker-cargo-${{ matrix.arch }}-
lookup-only: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/main') }}
- name: Cache build artifacts
id: cache-target
if: (github.event_name == 'push' && github.ref_type == 'branch') || github.event_name == 'pull_request'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae with:
path: |
target
key: docker-target-${{ matrix.arch }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
docker-target-${{ matrix.arch }}-
lookup-only: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/main') }}
- name: Set up Docker Buildx
id: setup-buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd - name: Restore Docker cache mount for Cargo registry
if: (github.event_name == 'push' && github.ref_type == 'branch') || github.event_name == 'pull_request'
uses: reproducible-containers/buildkit-cache-dance@5422eac04292c961a382e0f584ea0f03ad9da723 with:
builder: ${{ steps.setup-buildx.outputs.name }}
cache-map: |
{
"cargo-registry-index": "/usr/local/cargo/registry/index",
"cargo-registry-cache": "/usr/local/cargo/registry/cache",
"cargo-git-db": "/usr/local/cargo/git/db"
}
skip-extraction: ${{ steps.cache-cargo.outputs.cache-hit }}
- name: Restore Docker cache mount for build artifacts
if: (github.event_name == 'push' && github.ref_type == 'branch') || github.event_name == 'pull_request'
uses: reproducible-containers/buildkit-cache-dance@5422eac04292c961a382e0f584ea0f03ad9da723 with:
builder: ${{ steps.setup-buildx.outputs.name }}
cache-map: |
{
"target": "/app/target"
}
skip-extraction: ${{ steps.cache-target.outputs.cache-hit }}
- name: Login to Docker Hub
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag')
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag')
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with:
registry: ghcr.io
username: ${{ env.GHCR_USERNAME }}
password: ${{ github.token }}
- name: Set up build
id: setup
run: |
rust_version="$(gh api /repos/rust-lang/rust/releases/latest --jq '.tag_name')"
cache_from=""
cache_to=""
outputs=()
if [[ ("$GITHUB_EVENT_NAME" = "push" && "$GITHUB_REF_TYPE" = "branch") ||
"$GITHUB_EVENT_NAME" = "pull_request" ]]; then
cache_from="type=registry,ref=$BUILD_CACHE_REPOSITORY:$ARCH"
fi
if [[ "$GITHUB_EVENT_NAME" = "push" ]]; then
if [[ "$GITHUB_REF_TYPE" = "branch" && "$GITHUB_REF" = "refs/heads/main" ]]; then
cache_to="type=registry,ref=$BUILD_CACHE_REPOSITORY:$ARCH,mode=max"
fi
if [[ "$GITHUB_REF" = "refs/heads/main" || "$GITHUB_REF_TYPE" = "tag" ]]; then
outputs+=(
"type=image,name=$DOCKERHUB_REPOSITORY,name-canonical=true,push=true,push-by-digest=true"
"type=image,name=$GHCR_REPOSITORY,name-canonical=true,push=true,push-by-digest=true"
)
fi
fi
{
echo "rust_version=$rust_version"
echo "cache_from=$cache_from"
echo "cache_to=$cache_to"
echo "outputs<<OUTPUTS"
if [[ ${#outputs[@]} -gt 0 ]]; then
printf '%s\n' "${outputs[@]}"
fi
echo "OUTPUTS"
} >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
ARCH: ${{ matrix.arch }}
- name: Build and push Docker image by digest
id: build
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f with:
context: .
platforms: linux/${{ matrix.arch }}
labels: ${{ needs.metadata.outputs.labels }}
build-args: |
RUST_VERSION=${{ steps.setup.outputs.rust_version }}
cache-from: ${{ steps.setup.outputs.cache_from }}
cache-to: ${{ steps.setup.outputs.cache_to }}
outputs: ${{ steps.setup.outputs.outputs }}
- name: Export digest
run: |
mkdir -p "$RUNNER_TEMP"/digests
echo "${DIGEST#sha256:}" > "$RUNNER_TEMP"/digests/"$ARCH"
env:
DIGEST: ${{ steps.build.outputs.digest }}
ARCH: ${{ matrix.arch }}
- name: Upload digest
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: digests-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
merge:
name: Merge
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag')
needs: [metadata, build]
runs-on: ubuntu-latest
environment:
name: docker
deployment: false
permissions:
contents: read
packages: write steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd - name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 with:
registry: ghcr.io
username: ${{ env.GHCR_USERNAME }}
password: ${{ github.token }}
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Create manifest list and push
run: |
mapfile -t tags <<< "$TAGS"
tag_args=()
for tag in "${tags[@]}"; do
tag_args+=(--tag="$tag")
done
images=(
"$GHCR_REPOSITORY@sha256:$(cat "$RUNNER_TEMP"/digests/x86_64)"
"$GHCR_REPOSITORY@sha256:$(cat "$RUNNER_TEMP"/digests/aarch64)"
)
docker buildx imagetools create "${tag_args[@]}" "${images[@]}"
env:
TAGS: ${{ needs.metadata.outputs.tags }}