Files
trycua--cua/.github/workflows/docker-reusable-publish.yml
wehub-resource-sync 91e75e620b
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:19 +08:00

252 lines
9.3 KiB
YAML

name: Reusable Docker Publish Workflow
on:
workflow_dispatch: # Prevents phantom runs on push, allows manual testing
workflow_call:
inputs:
image_name:
description: "Name of the Docker image (e.g. cua-ubuntu, cua-xfce)"
required: true
type: string
context_dir:
description: "Directory containing the Dockerfile relative to workspace root (e.g. libs/kasm, libs/xfce)"
required: true
type: string
dockerfile_path:
description: "Path to Dockerfile relative to context_dir (e.g. Dockerfile)"
required: false
type: string
default: "Dockerfile"
tag_prefix:
description: "Prefix for semantic version tags (e.g. docker-kasm-v, docker-xfce-v)"
required: true
type: string
docker_hub_org:
description: "Docker Hub organization name"
required: false
type: string
default: "trycua"
skip_arm64:
description: "Skip arm64 build (for images that don't support arm64)"
required: false
type: boolean
default: false
secrets:
DOCKER_HUB_TOKEN:
required: true
jobs:
build-and-push:
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
exclude:
- platform: ${{ inputs.skip_arm64 && 'linux/arm64' || 'none' }}
steps:
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache
sudo docker system prune -af
df -h /
- name: Checkout
uses: actions/checkout@v4
- name: Prepare platform tag
id: platform
run: |
TAG=$(echo "${{ matrix.platform }}" | sed 's/\//-/g')
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.docker_hub_org }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build only (PR) - validate Dockerfile without pushing
if: github.event_name == 'pull_request'
id: build-pr
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
file: ./${{ inputs.context_dir }}/${{ inputs.dockerfile_path }}
push: false
platforms: ${{ matrix.platform }}
cache-from: |
type=registry,ref=${{ inputs.docker_hub_org }}/${{ inputs.image_name }}:buildcache-${{ steps.platform.outputs.tag }}
- name: Extract metadata (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
id: meta-main
uses: docker/metadata-action@v5
with:
images: ${{ inputs.docker_hub_org }}/${{ inputs.image_name }}
tags: |
type=raw,value=latest
- name: Build & push digest (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
id: build-main
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
file: ./${{ inputs.context_dir }}/${{ inputs.dockerfile_path }}
push: true
platforms: ${{ matrix.platform }}
outputs: type=registry,name=${{ inputs.docker_hub_org }}/${{ inputs.image_name }},push-by-digest=true
labels: ${{ steps.meta-main.outputs.labels }}
cache-from: |
type=registry,ref=${{ inputs.docker_hub_org }}/${{ inputs.image_name }}:buildcache-${{ steps.platform.outputs.tag }}
cache-to: type=registry,ref=${{ inputs.docker_hub_org }}/${{ inputs.image_name }}:buildcache-${{ steps.platform.outputs.tag }},mode=max
- name: Extract metadata (semver)
if: startsWith(github.ref, format('refs/tags/{0}', inputs.tag_prefix))
id: meta-semver
uses: docker/metadata-action@v5
with:
images: ${{ inputs.docker_hub_org }}/${{ inputs.image_name }}
tags: |
type=match,pattern=${{ inputs.tag_prefix }}(\d+\.\d+\.\d+),group=1
type=match,pattern=${{ inputs.tag_prefix }}(\d+\.\d+),group=1
type=match,pattern=${{ inputs.tag_prefix }}(\d+),group=1
type=raw,value=latest
- name: Build & push digest (semver)
if: startsWith(github.ref, format('refs/tags/{0}', inputs.tag_prefix))
id: build-semver
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
file: ./${{ inputs.context_dir }}/${{ inputs.dockerfile_path }}
push: true
platforms: ${{ matrix.platform }}
outputs: type=registry,name=${{ inputs.docker_hub_org }}/${{ inputs.image_name }},push-by-digest=true
labels: ${{ steps.meta-semver.outputs.labels }}
cache-from: |
type=registry,ref=${{ inputs.docker_hub_org }}/${{ inputs.image_name }}:buildcache-${{ steps.platform.outputs.tag }}
cache-to: type=registry,ref=${{ inputs.docker_hub_org }}/${{ inputs.image_name }}:buildcache-${{ steps.platform.outputs.tag }},mode=max
- name: Export digest
id: export-digest
if: |
github.event_name != 'pull_request' &&
((steps.build-main.outcome == 'success') ||
(steps.build-semver.outcome == 'success'))
run: |
mkdir -p /tmp/digests
digest="${{ steps.build-main.outputs.digest || steps.build-semver.outputs.digest }}"
if [ -n "$digest" ]; then
echo "$digest" > "/tmp/digests/${{ steps.platform.outputs.tag }}.txt"
echo "Digest exported for platform ${{ matrix.platform }}: $digest"
else
echo "No digest to export for platform ${{ matrix.platform }}"
exit 1
fi
- name: Upload digest artifact (unique per platform)
if: steps.export-digest.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: digests-${{ steps.platform.outputs.tag }}
path: /tmp/digests/*.txt
retention-days: 1
publish-manifest-list:
runs-on: ubuntu-latest
needs:
- build-and-push
if: github.event_name != 'pull_request' && (success() || failure()) # Skip on PRs, run on push/tag even if some builds failed
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.docker_hub_org }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Extract final metadata (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
uses: docker/metadata-action@v5
with:
images: ${{ inputs.docker_hub_org }}/${{ inputs.image_name }}
tags: |
type=raw,value=latest
- name: Extract final metadata (semver)
if: startsWith(github.ref, format('refs/tags/{0}', inputs.tag_prefix))
uses: docker/metadata-action@v5
with:
images: ${{ inputs.docker_hub_org }}/${{ inputs.image_name }}
tags: |
type=match,pattern=${{ inputs.tag_prefix }}(\d+\.\d+\.\d+),group=1
type=match,pattern=${{ inputs.tag_prefix }}(\d+\.\d+),group=1
type=match,pattern=${{ inputs.tag_prefix }}(\d+),group=1
type=raw,value=latest
- name: Download all digest artifacts
uses: actions/download-artifact@v4
with:
pattern: digests-*
path: /tmp/digests
merge-multiple: true
- name: Check available digests
id: check-digests
run: |
DIGEST_COUNT=$(find /tmp/digests -type f -name "*.txt" | wc -l)
echo "digest_count=$DIGEST_COUNT" >> $GITHUB_OUTPUT
if [ "$DIGEST_COUNT" -eq 0 ]; then
echo "Error: No platform builds succeeded!"
exit 1
fi
echo "Found $DIGEST_COUNT platform digest(s)"
for f in $(find /tmp/digests -type f -name "*.txt"); do
platform=$(basename "$f" .txt)
echo " - $platform"
done
- name: Create & push multi-arch manifest
run: |
IMAGE="${{ inputs.docker_hub_org }}/${{ inputs.image_name }}"
DIGEST_ARGS=""
for f in $(find /tmp/digests -type f -name "*.txt"); do
d=$(cat "$f")
DIGEST_ARGS="$DIGEST_ARGS ${IMAGE}@${d}"
done
echo "Using digests:"
echo "$DIGEST_ARGS"
# Create manifest for each tag produced by metadata-action
echo "${DOCKER_METADATA_OUTPUT_JSON}" | jq -r '.tags[]' | while read FULL_TAG; do
echo "Creating manifest: $FULL_TAG"
docker buildx imagetools create --tag "$FULL_TAG" $DIGEST_ARGS
done
- name: Inspect pushed manifests
run: |
IMAGE="${{ inputs.docker_hub_org }}/${{ inputs.image_name }}"
echo "Inspecting manifests:"
echo "${DOCKER_METADATA_OUTPUT_JSON}" | jq -r '.tags[]' | while read FULL_TAG; do
echo ""
echo "Inspecting: $FULL_TAG"
docker buildx imagetools inspect "$FULL_TAG"
done