mirror of
https://github.com/mudler/LocalAI.git
synced 2026-05-31 12:07:45 -04:00
The existing master push pipeline produces `master` (rolling) and
`sha-<short>` tags. Neither is orderable by build time, so downstream
GitOps that want to auto-bump to the newest master build (e.g. Flux
ImagePolicy) can't pick the latest from the tag list — alphabetical
sort over hex shas is effectively random, and the rolling `master`
tag can't be referenced as an immutable bump target.
Add a third tag of the form `master-<epoch>-<sha>` (Unix epoch in
seconds + short sha), gated on default-branch pushes via metadata-
action's `is_default_branch` predicate. The sha is retained for
traceability; the epoch makes the tags numerically orderable, so a
Flux ImagePolicy like
filterTags:
pattern: '^master-(?P<ts>[0-9]+)-[a-f0-9]+$'
extract: '$ts'
policy:
numerical:
order: asc
will reliably bump to the newest master build.
Applied to both image_build.yml (OCI labels stay consistent) and
image_merge.yml (the actual tag publisher via buildx imagetools).
147 lines
5.5 KiB
YAML
147 lines
5.5 KiB
YAML
---
|
|
name: 'merge LocalAI image manifest list (reusable)'
|
|
|
|
# Reusable workflow that joins per-arch digest artifacts (uploaded by
|
|
# image_build.yml when called with platform-tag) into a single tagged
|
|
# multi-arch manifest list.
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
tag-latest:
|
|
description: 'Whether the manifest list should also be tagged latest (auto/false/true)'
|
|
required: false
|
|
type: string
|
|
default: ''
|
|
tag-suffix:
|
|
description: 'Image tag suffix (empty for core image). Used in artifact pattern with a -core placeholder for empty.'
|
|
required: true
|
|
type: string
|
|
secrets:
|
|
dockerUsername:
|
|
required: false
|
|
dockerPassword:
|
|
required: false
|
|
quayUsername:
|
|
required: true
|
|
quayPassword:
|
|
required: true
|
|
|
|
jobs:
|
|
merge:
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
quay_username: ${{ secrets.quayUsername }}
|
|
steps:
|
|
# Sparse checkout: needed for .github/scripts/ (the keepalive cleanup
|
|
# script). Skips the rest of the source tree.
|
|
- name: Checkout (.github/scripts only)
|
|
uses: actions/checkout@v6
|
|
with:
|
|
sparse-checkout: |
|
|
.github/scripts
|
|
sparse-checkout-cone-mode: false
|
|
|
|
- name: Download digests
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
# `--` 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
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@master
|
|
|
|
- name: Login to DockerHub
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@v4
|
|
with:
|
|
username: ${{ secrets.dockerUsername }}
|
|
password: ${{ secrets.dockerPassword }}
|
|
|
|
- name: Login to Quay.io
|
|
uses: docker/login-action@v4
|
|
with:
|
|
registry: quay.io
|
|
username: ${{ secrets.quayUsername }}
|
|
password: ${{ secrets.quayPassword }}
|
|
|
|
- name: Docker meta
|
|
id: meta
|
|
uses: docker/metadata-action@v6
|
|
with:
|
|
images: |
|
|
quay.io/go-skynet/local-ai
|
|
localai/localai
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=semver,pattern={{raw}}
|
|
type=sha
|
|
type=raw,value={{branch}}-{{date 'X'}}-{{sha}},enable={{is_default_branch}}
|
|
flavor: |
|
|
latest=${{ inputs.tag-latest }}
|
|
suffix=${{ inputs.tag-suffix }},onlatest=true
|
|
|
|
# Source from ci-cache, not local-ai. See backend_merge.yml for the
|
|
# detailed rationale — quay's manifest GC is per-repository, so the
|
|
# untagged digest in local-ai gets reaped while the same content lives
|
|
# tagged under ci-cache (anchored by image_build.yml). buildx imagetools
|
|
# create copies the manifest into local-ai (blobs already cross-mounted)
|
|
# and publishes the manifest list with user-facing tags. End state in
|
|
# local-ai is self-contained; no embedded reference to ci-cache.
|
|
- name: Create manifest list and push (quay)
|
|
working-directory: /tmp/digests
|
|
run: |
|
|
set -euo pipefail
|
|
tags=$(jq -cr '.tags | map(select(startswith("quay.io/"))) | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
if [ -z "$tags" ]; then
|
|
echo "No quay.io tags from docker/metadata-action; skipping quay merge"
|
|
else
|
|
# shellcheck disable=SC2086
|
|
docker buildx imagetools create $tags \
|
|
$(printf 'quay.io/go-skynet/ci-cache@sha256:%s ' *)
|
|
fi
|
|
|
|
- name: Create manifest list and push (dockerhub)
|
|
if: github.event_name != 'pull_request'
|
|
working-directory: /tmp/digests
|
|
run: |
|
|
set -euo pipefail
|
|
tags=$(jq -cr '.tags | map(select(startswith("localai/"))) | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
if [ -z "$tags" ]; then
|
|
echo "No dockerhub tags from docker/metadata-action; skipping dockerhub merge"
|
|
else
|
|
# shellcheck disable=SC2086
|
|
docker buildx imagetools create $tags \
|
|
$(printf 'localai/localai@sha256:%s ' *)
|
|
fi
|
|
|
|
- name: Inspect manifest
|
|
run: |
|
|
set -euo pipefail
|
|
first_tag=$(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
if [ -n "$first_tag" ] && [ "$first_tag" != "null" ]; then
|
|
docker buildx imagetools inspect "$first_tag"
|
|
fi
|
|
|
|
# See .github/scripts/cleanup-keepalive-tags.sh for the best-effort
|
|
# semantics — fails soft when the registry credential isn't OAuth-scoped.
|
|
- name: Cleanup keepalive tags in ci-cache
|
|
if: github.event_name != 'pull_request' && success()
|
|
env:
|
|
TAG_SUFFIX: ${{ inputs.tag-suffix == '' && '-core' || inputs.tag-suffix }}
|
|
QUAY_TOKEN: ${{ secrets.quayPassword }}
|
|
run: .github/scripts/cleanup-keepalive-tags.sh
|
|
|
|
- name: Job summary
|
|
run: |
|
|
set -euo pipefail
|
|
echo "Merged manifest tags:" >> "$GITHUB_STEP_SUMMARY"
|
|
jq -r '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON" | sed 's/^/- /' >> "$GITHUB_STEP_SUMMARY"
|
|
echo >> "$GITHUB_STEP_SUMMARY"
|
|
echo "Per-arch digests:" >> "$GITHUB_STEP_SUMMARY"
|
|
ls -1 /tmp/digests | sed 's/^/- sha256:/' >> "$GITHUB_STEP_SUMMARY"
|