name: Docker Build & Push (Blacksmith) on: push: branches: - main tags: - "v*" - "*.*.*" pull_request: branches: - main workflow_dispatch: concurrency: group: docker-blacksmith-${{ github.ref }} cancel-in-progress: true permissions: contents: read packages: write env: DOCKERFILE: dockerfile CONTEXT: . DOCKERHUB_IMAGE: ${{ vars.DOCKERHUB_IMAGE || 'openllmvtuber/open-llm-vtuber' }} GHCR_IMAGE: ${{ vars.GHCR_IMAGE || '' }} jobs: meta: runs-on: blacksmith-8vcpu-ubuntu-2204 outputs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} dockerhub_image: ${{ steps.image.outputs.dockerhub_image }} ghcr_image: ${{ steps.image.outputs.ghcr_image }} steps: - name: Resolve image names id: image shell: bash run: | set -euo pipefail dockerhub_image="${DOCKERHUB_IMAGE}" if [ -n "${GHCR_IMAGE:-}" ]; then ghcr_image="${GHCR_IMAGE}" else ghcr_image="ghcr.io/${GITHUB_REPOSITORY,,}" fi echo "dockerhub_image=${dockerhub_image}" >> "$GITHUB_OUTPUT" echo "ghcr_image=${ghcr_image}" >> "$GITHUB_OUTPUT" - name: Docker image metadata id: meta uses: docker/metadata-action@v5 with: images: | ${{ steps.image.outputs.dockerhub_image }} ${{ steps.image.outputs.ghcr_image }} tags: | type=ref,event=branch type=ref,event=tag type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,format=short type=raw,value=latest,enable={{is_default_branch}} build: needs: meta runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - platform: amd64 runner: blacksmith-8vcpu-ubuntu-2204 docker_platform: linux/amd64 - platform: arm64 runner: blacksmith-8vcpu-ubuntu-2204-arm docker_platform: linux/arm64 steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Blacksmith Docker builder uses: useblacksmith/setup-docker-builder@v1 - name: Login to Docker Hub (retry) if: github.event_name != 'pull_request' shell: bash env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | set -euo pipefail for attempt in 1 2 3 4; do if echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin; then exit 0 fi if [ "${attempt}" -eq 4 ]; then echo "Docker Hub login failed after ${attempt} attempts." >&2 exit 1 fi sleep $((attempt * 5)) done - name: Login to GHCR (retry) if: github.event_name != 'pull_request' shell: bash env: GHCR_USER: ${{ github.actor }} GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail for attempt in 1 2 3 4; do if echo "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USER}" --password-stdin; then exit 0 fi if [ "${attempt}" -eq 4 ]; then echo "GHCR login failed after ${attempt} attempts." >&2 exit 1 fi sleep $((attempt * 5)) done - name: Prepare temporary arch tags id: temp-tags shell: bash run: | set -euo pipefail ref_slug="$(echo "${GITHUB_REF_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')" suffix="tmp-${ref_slug}-${{ matrix.platform }}" { echo "tags<> "$GITHUB_OUTPUT" - name: Build and push (${{ matrix.platform }}) uses: useblacksmith/build-push-action@v2 with: context: ${{ env.CONTEXT }} file: ${{ env.DOCKERFILE }} platforms: ${{ matrix.docker_platform }} push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.temp-tags.outputs.tags }} labels: ${{ needs.meta.outputs.labels }} manifest: needs: [meta, build] runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - name: Login to Docker Hub (retry) shell: bash env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | set -euo pipefail for attempt in 1 2 3 4; do if echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin; then exit 0 fi if [ "${attempt}" -eq 4 ]; then echo "Docker Hub login failed after ${attempt} attempts." >&2 exit 1 fi sleep $((attempt * 5)) done - name: Login to GHCR (retry) shell: bash env: GHCR_USER: ${{ github.actor }} GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail for attempt in 1 2 3 4; do if echo "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USER}" --password-stdin; then exit 0 fi if [ "${attempt}" -eq 4 ]; then echo "GHCR login failed after ${attempt} attempts." >&2 exit 1 fi sleep $((attempt * 5)) done - name: Set up Buildx uses: docker/setup-buildx-action@v3 - name: Create and push multi-arch manifests shell: bash run: | set -euo pipefail ref_slug="$(echo "${GITHUB_REF_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')" suffix_base="tmp-${ref_slug}" mapfile -t tags <<< "${{ needs.meta.outputs.tags }}" for tag in "${tags[@]}"; do [ -z "$tag" ] && continue base="${tag%:*}" docker buildx imagetools create \ --tag "$tag" \ "${base}:${suffix_base}-amd64" \ "${base}:${suffix_base}-arm64" done