From e5300f1a865dc481427e15ed677ee8cdc94e4aea Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Wed, 13 May 2026 22:12:32 +0000 Subject: [PATCH] ci(image): wire singleton merges + `--` artifact separator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the same singletons gap on the LocalAI server image workflow that PR #9781 closed for backends. The user observed it as missing :latest-gpu-nvidia-cuda-12 etc. on quay.io/go-skynet/local-ai — the build matrix has six single-arch entries with no corresponding merge step, so their per-arch digests push (push-by-digest=true) and never get tagged: - -gpu-hipblas (hipblas-jobs) - -gpu-nvidia-cuda-12 (core-image-build) - -gpu-nvidia-cuda-13 (core-image-build) - -gpu-intel (core-image-build) - -nvidia-l4t-arm64 (gh-runner) - -nvidia-l4t-arm64-cuda-13 (gh-runner) Only :latest, :v, :latest-gpu-vulkan and :v-gpu-vulkan were actually being published before this commit (the two multiarch suffixes that had merge jobs). Changes: 1. image.yml: add six new merge jobs, one per single-arch entry. Each `needs:` only its parent build job (matching the existing pattern for core-image-merge / gpu-vulkan-image-merge). 2. image_build.yml: switch artifact name to `digests-localai--`. The `--` separator anchors the merge-side glob so a singleton tag-suffix doesn't over-match a longer suffix that shares its prefix (-nvidia-l4t-arm64 vs -nvidia-l4t-arm64-cuda-13). Same convention as backend_build.yml's fix. 3. image_merge.yml: update the download pattern to match. Next master push or tag release should produce :latest-gpu-hipblas, :latest-gpu-nvidia-cuda-12, :latest-gpu-nvidia-cuda-13, :latest-gpu-intel, :latest-nvidia-l4t-arm64, :latest-nvidia-l4t-arm64-cuda-13 (and their :v-* equivalents) for the first time on the post-#9781 workflow. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Ettore Di Giacinto --- .github/workflows/image.yml | 86 ++++++++++++++++++++++++++++++- .github/workflows/image_build.yml | 6 ++- .github/workflows/image_merge.yml | 5 +- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 91e5c97f7..e24b2f45f 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -175,7 +175,91 @@ dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} - + + # Single-arch server-image merges. Same conceptual fix as the backend + # singletons in PR #9781: image_build.yml pushes by canonical digest + # only, so without a downstream merge step there's no tag for consumers + # (no :latest-gpu-nvidia-cuda-12, no :v-gpu-nvidia-cuda-12, etc.). + # Each merge job needs only its parent build matrix and is filtered by + # tag-suffix in image_merge.yml's artifact-download pattern. + gpu-nvidia-cuda-12-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: core-image-build + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-gpu-nvidia-cuda-12' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + + gpu-nvidia-cuda-13-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: core-image-build + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-gpu-nvidia-cuda-13' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + + gpu-intel-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: core-image-build + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-gpu-intel' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + + gpu-hipblas-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: hipblas-jobs + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-gpu-hipblas' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + + nvidia-l4t-arm64-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: gh-runner + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-nvidia-l4t-arm64' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + + nvidia-l4t-arm64-cuda-13-image-merge: + if: github.repository == 'mudler/LocalAI' + needs: gh-runner + uses: ./.github/workflows/image_merge.yml + with: + tag-latest: 'auto' + tag-suffix: '-nvidia-l4t-arm64-cuda-13' + secrets: + dockerUsername: ${{ secrets.DOCKERHUB_USERNAME }} + dockerPassword: ${{ secrets.DOCKERHUB_PASSWORD }} + quayUsername: ${{ secrets.LOCALAI_REGISTRY_USERNAME }} + quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} + gh-runner: if: github.repository == 'mudler/LocalAI' uses: ./.github/workflows/image_build.yml diff --git a/.github/workflows/image_build.yml b/.github/workflows/image_build.yml index 9b9683b32..96cfebdd8 100644 --- a/.github/workflows/image_build.yml +++ b/.github/workflows/image_build.yml @@ -202,7 +202,11 @@ jobs: if: github.event_name != 'pull_request' uses: actions/upload-artifact@v7 with: - name: digests-localai${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}-${{ inputs.platform-tag }} + # `--` separator + 'single' placeholder for empty platform-tag — + # same pattern as backend_build.yml. Prevents prefix collisions + # in the merge-side glob (e.g. -nvidia-l4t-arm64 is a prefix of + # -nvidia-l4t-arm64-cuda-13). + name: digests-localai${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}--${{ inputs.platform-tag || 'single' }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/image_merge.yml b/.github/workflows/image_merge.yml index 85315fe58..f667c7d4c 100644 --- a/.github/workflows/image_merge.yml +++ b/.github/workflows/image_merge.yml @@ -45,7 +45,10 @@ jobs: - name: Download digests uses: actions/download-artifact@v8 with: - pattern: digests-localai${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}-* + # `--` separator anchors the glob so we don't over-match sibling + # tag-suffixes (e.g. -nvidia-l4t-arm64 vs -nvidia-l4t-arm64-cuda-13). + # Must stay in sync with image_build.yml's upload-artifact name. + pattern: digests-localai${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}--* merge-multiple: true path: /tmp/digests