diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 91e5c97f7..716faaaee 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -151,7 +151,11 @@ ubuntu-codename: 'noble' core-image-merge: - if: github.repository == 'mudler/LocalAI' + # !cancelled(): without it, GHA's default `needs:` cascade skips the + # merge whenever any matrix cell of the parent build fails or is + # cancelled. Same fix as backend.yml's merge jobs — we still want to + # publish the manifest list for tag-suffixes whose legs all succeeded. + if: ${{ !cancelled() && github.repository == 'mudler/LocalAI' }} needs: core-image-build uses: ./.github/workflows/image_merge.yml with: @@ -164,7 +168,7 @@ quayPassword: ${{ secrets.LOCALAI_REGISTRY_PASSWORD }} gpu-vulkan-image-merge: - if: github.repository == 'mudler/LocalAI' + if: ${{ !cancelled() && github.repository == 'mudler/LocalAI' }} needs: core-image-build uses: ./.github/workflows/image_merge.yml with: @@ -175,7 +179,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: ${{ !cancelled() && 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: ${{ !cancelled() && 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: ${{ !cancelled() && 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: ${{ !cancelled() && 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: ${{ !cancelled() && 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: ${{ !cancelled() && 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