mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 13:14:54 -04:00
feat: extract multi-arch image building to shared logic (#17877)
This commit is contained in:
parent
894545aeed
commit
0beb3ac4c1
118
.github/actions/image-build/action.yml
vendored
Normal file
118
.github/actions/image-build/action.yml
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
name: 'Single arch image build'
|
||||||
|
description: 'Build single-arch image on platform appropriate runner'
|
||||||
|
inputs:
|
||||||
|
image:
|
||||||
|
description: 'Name of the image to build'
|
||||||
|
required: true
|
||||||
|
ghcr-token:
|
||||||
|
description: 'GitHub Container Registry token'
|
||||||
|
required: true
|
||||||
|
platform:
|
||||||
|
description: 'Platform to build for'
|
||||||
|
required: true
|
||||||
|
artifact-key-base:
|
||||||
|
description: 'Base key for artifact name'
|
||||||
|
required: true
|
||||||
|
context:
|
||||||
|
description: 'Path to build context'
|
||||||
|
required: true
|
||||||
|
dockerfile:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
required: true
|
||||||
|
build-args:
|
||||||
|
description: 'Docker build arguments'
|
||||||
|
required: false
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
id: prepare
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PLATFORM: ${{ inputs.platform }}
|
||||||
|
run: |
|
||||||
|
echo "platform-pair=${PLATFORM//\//-}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ inputs.ghcr-token }}
|
||||||
|
|
||||||
|
- name: Generate cache key suffix
|
||||||
|
id: cache-key-suffix
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
REF: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
|
echo "cache-key-suffix=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
||||||
|
echo "suffix=${SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate cache target
|
||||||
|
id: cache-target
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
BUILD_ARGS: ${{ inputs.build-args }}
|
||||||
|
IMAGE: ${{ inputs.image }}
|
||||||
|
SUFFIX: ${{ steps.cache-key-suffix.outputs.suffix }}
|
||||||
|
PLATFORM_PAIR: ${{ steps.prepare.outputs.platform-pair }}
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
# Essentially just ignore the cache output (forks can't write to registry cache)
|
||||||
|
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
HASH=$(sha256sum <<< "${BUILD_ARGS}" | cut -d' ' -f1)
|
||||||
|
CACHE_KEY="${PLATFORM_PAIR}-${HASH}"
|
||||||
|
echo "cache-key-base=${CACHE_KEY}" >> $GITHUB_OUTPUT
|
||||||
|
echo "cache-to=type=registry,ref=${IMAGE}-build-cache:${CACHE_KEY}-${SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
|
env:
|
||||||
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||||
|
with:
|
||||||
|
context: ${{ inputs.context }}
|
||||||
|
file: ${{ inputs.dockerfile }}
|
||||||
|
platforms: ${{ inputs.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
||||||
|
cache-from: |
|
||||||
|
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-${{ env.CACHE_KEY_SUFFIX }}
|
||||||
|
type=registry,ref=${{ inputs.image }}-build-cache:${{ steps.cache-target.outputs.cache-key-base }}-main
|
||||||
|
outputs: type=image,"name=${{ inputs.image }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
build-args: |
|
||||||
|
BUILD_ID=${{ github.run_id }}
|
||||||
|
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.meta.outputs.tags }}
|
||||||
|
BUILD_SOURCE_REF=${{ github.ref_name }}
|
||||||
|
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
||||||
|
${{ inputs.build-args }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
shell: bash
|
||||||
|
run: | # zizmor: ignore[template-injection]
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.artifact-key-base }}-${{ steps.cache-target.outputs.cache-key-base }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
451
.github/workflows/docker.yml
vendored
451
.github/workflows/docker.yml
vendored
@ -40,6 +40,8 @@ jobs:
|
|||||||
- 'machine-learning/**'
|
- 'machine-learning/**'
|
||||||
workflow:
|
workflow:
|
||||||
- '.github/workflows/docker.yml'
|
- '.github/workflows/docker.yml'
|
||||||
|
- '.github/workflows/multi-runner-build.yml'
|
||||||
|
- '.github/actions/image-build'
|
||||||
|
|
||||||
- name: Check if we should force jobs to run
|
- name: Check if we should force jobs to run
|
||||||
id: should_force
|
id: should_force
|
||||||
@ -103,429 +105,74 @@ jobs:
|
|||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_PR}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
docker buildx imagetools create -t "${REGISTRY_NAME}/${REPOSITORY}:${TAG_COMMIT}" "${REGISTRY_NAME}/${REPOSITORY}:${TAG_OLD}"
|
||||||
|
|
||||||
build_and_push_ml:
|
machine-learning:
|
||||||
name: Build and Push ML
|
name: Build and Push ML
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }}
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
env:
|
|
||||||
image: immich-machine-learning
|
|
||||||
context: machine-learning
|
|
||||||
file: machine-learning/Dockerfile
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
strategy:
|
strategy:
|
||||||
# Prevent a failure in one image from stopping the other builds
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: cpu
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: cuda
|
|
||||||
suffix: -cuda
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: mich
|
|
||||||
device: rocm
|
|
||||||
suffix: -rocm
|
|
||||||
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
device: openvino
|
|
||||||
suffix: -openvino
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: armnn
|
|
||||||
suffix: -armnn
|
|
||||||
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
device: rknn
|
|
||||||
suffix: -rknn
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
env:
|
|
||||||
REF: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
|
||||||
echo "CACHE_KEY_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${GHCR_REPO}-build-cache:${PLATFORM_PAIR}-${{ matrix.device }}-${CACHE_KEY_SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platforms }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ matrix.device }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=${{ matrix.device }}
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: | # zizmor: ignore[template-injection]
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: ml-digests-${{ matrix.device }}-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_ml:
|
|
||||||
name: Merge & Push ML
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
packages: write
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_ml == 'true' && !github.event.pull_request.head.repo.fork }}
|
|
||||||
env:
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-machine-learning
|
|
||||||
DOCKER_REPO: altran1502/immich-machine-learning
|
|
||||||
strategy:
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- device: cpu
|
- device: cpu
|
||||||
|
tag-suffix: ''
|
||||||
- device: cuda
|
- device: cuda
|
||||||
suffix: -cuda
|
tag-suffix: '-cuda'
|
||||||
- device: rocm
|
platforms: linux/amd64
|
||||||
suffix: -rocm
|
|
||||||
- device: openvino
|
- device: openvino
|
||||||
suffix: -openvino
|
tag-suffix: '-openvino'
|
||||||
|
platforms: linux/amd64
|
||||||
- device: armnn
|
- device: armnn
|
||||||
suffix: -armnn
|
tag-suffix: '-armnn'
|
||||||
|
platforms: linux/arm64
|
||||||
- device: rknn
|
- device: rknn
|
||||||
suffix: -rknn
|
tag-suffix: '-rknn'
|
||||||
needs:
|
platforms: linux/arm64
|
||||||
- build_and_push_ml
|
- device: rocm
|
||||||
steps:
|
tag-suffix: '-rocm'
|
||||||
- name: Download digests
|
platforms: linux/amd64
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
runner-mapping: '{"linux/amd64": "mich"}'
|
||||||
with:
|
uses: ./.github/workflows/multi-runner-build.yml
|
||||||
path: ${{ runner.temp }}/digests
|
|
||||||
pattern: ml-digests-${{ matrix.device }}-*
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
if: ${{ github.event_name == 'release' }}
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf "${GHCR_REPO}@sha256:%s " *)
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
build_and_push_server:
|
|
||||||
name: Build and Push Server
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
needs: pre-job
|
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
|
||||||
env:
|
|
||||||
image: immich-server
|
|
||||||
context: .
|
|
||||||
file: server/Dockerfile
|
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- platform: linux/amd64
|
|
||||||
runner: ubuntu-latest
|
|
||||||
- platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: |
|
|
||||||
platform=${{ matrix.platform }}
|
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate cache key suffix
|
|
||||||
env:
|
|
||||||
REF: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
||||||
echo "CACHE_KEY_SUFFIX=pr-${{ github.event.number }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
SUFFIX=$(echo "${REF}" | sed 's/[^a-zA-Z0-9]/-/g')
|
|
||||||
echo "CACHE_KEY_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate cache target
|
|
||||||
id: cache-target
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
|
||||||
# Essentially just ignore the cache output (forks can't write to registry cache)
|
|
||||||
echo "cache-to=type=local,dest=/tmp/discard,ignore-error=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "cache-to=type=registry,ref=${GHCR_REPO}-build-cache:${PLATFORM_PAIR}-${CACHE_KEY_SUFFIX},mode=max,compression=zstd" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
id: build
|
|
||||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
|
||||||
with:
|
|
||||||
context: ${{ env.context }}
|
|
||||||
file: ${{ env.file }}
|
|
||||||
platforms: ${{ matrix.platform }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-to: ${{ steps.cache-target.outputs.cache-to }}
|
|
||||||
cache-from: |
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-${{ env.CACHE_KEY_SUFFIX }}
|
|
||||||
type=registry,ref=${{ env.GHCR_REPO }}-build-cache:${{ env.PLATFORM_PAIR }}-main
|
|
||||||
outputs: type=image,"name=${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
build-args: |
|
|
||||||
DEVICE=cpu
|
|
||||||
BUILD_ID=${{ github.run_id }}
|
|
||||||
BUILD_IMAGE=${{ github.event_name == 'release' && github.ref_name || steps.metadata.outputs.tags }}
|
|
||||||
BUILD_SOURCE_REF=${{ github.ref_name }}
|
|
||||||
BUILD_SOURCE_COMMIT=${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Export digest
|
|
||||||
run: | # zizmor: ignore[template-injection]
|
|
||||||
mkdir -p ${{ runner.temp }}/digests
|
|
||||||
digest="${{ steps.build.outputs.digest }}"
|
|
||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Upload digest
|
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
||||||
with:
|
|
||||||
name: server-digests-${{ env.PLATFORM_PAIR }}
|
|
||||||
path: ${{ runner.temp }}/digests/*
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge_server:
|
|
||||||
name: Merge & Push Server
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
actions: read
|
actions: read
|
||||||
packages: write
|
packages: write
|
||||||
if: ${{ needs.pre-job.outputs.should_run_server == 'true' && !github.event.pull_request.head.repo.fork }}
|
secrets:
|
||||||
env:
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
GHCR_REPO: ghcr.io/${{ github.repository_owner }}/immich-server
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
DOCKER_REPO: altran1502/immich-server
|
|
||||||
needs:
|
|
||||||
- build_and_push_server
|
|
||||||
steps:
|
|
||||||
- name: Download digests
|
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
image: immich-machine-learning
|
||||||
pattern: server-digests-*
|
context: machine-learning
|
||||||
merge-multiple: true
|
dockerfile: machine-learning/Dockerfile
|
||||||
|
platforms: ${{ matrix.platforms }}
|
||||||
|
runner-mapping: ${{ matrix.runner-mapping }}
|
||||||
|
tag-suffix: ${{ matrix.tag-suffix }}
|
||||||
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
|
build-args: |
|
||||||
|
DEVICE=${{ matrix.device }}
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
server:
|
||||||
if: ${{ github.event_name == 'release' }}
|
name: Build and Push Server
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_run_server == 'true' }}
|
||||||
|
uses: ./.github/workflows/multi-runner-build.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
packages: write
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
image: immich-server
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
context: .
|
||||||
|
dockerfile: server/Dockerfile
|
||||||
- name: Login to GHCR
|
dockerhub-push: ${{ github.event_name == 'release' }}
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
build-args: |
|
||||||
with:
|
DEVICE=cpu
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
|
||||||
|
|
||||||
- name: Generate docker image tags
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
|
||||||
with:
|
|
||||||
flavor: |
|
|
||||||
# Disable latest tag
|
|
||||||
latest=false
|
|
||||||
suffix=${{ matrix.suffix }}
|
|
||||||
images: |
|
|
||||||
name=${{ env.GHCR_REPO }}
|
|
||||||
name=${{ env.DOCKER_REPO }},enable=${{ github.event_name == 'release' }}
|
|
||||||
tags: |
|
|
||||||
# Tag with branch name
|
|
||||||
type=ref,event=branch
|
|
||||||
# Tag with pr-number
|
|
||||||
type=ref,event=pr
|
|
||||||
# Tag with long commit sha hash
|
|
||||||
type=sha,format=long,prefix=commit-
|
|
||||||
# Tag with git tag on release
|
|
||||||
type=ref,event=tag
|
|
||||||
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: ${{ runner.temp }}/digests
|
|
||||||
run: |
|
|
||||||
# Process annotations
|
|
||||||
declare -a ANNOTATIONS=()
|
|
||||||
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
|
||||||
while IFS= read -r annotation; do
|
|
||||||
# Extract key and value by removing the manifest: prefix
|
|
||||||
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
|
||||||
key="${BASH_REMATCH[1]}"
|
|
||||||
value="${BASH_REMATCH[2]}"
|
|
||||||
# Use array to properly handle arguments with spaces
|
|
||||||
ANNOTATIONS+=(--annotation "index:$key=$value")
|
|
||||||
fi
|
|
||||||
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
|
||||||
fi
|
|
||||||
|
|
||||||
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
||||||
SOURCE_ARGS=$(printf "${GHCR_REPO}@sha256:%s " *)
|
|
||||||
|
|
||||||
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
|
||||||
|
|
||||||
success-check-server:
|
success-check-server:
|
||||||
name: Docker Build & Push Server Success
|
name: Docker Build & Push Server Success
|
||||||
needs: [merge_server, retag_server]
|
needs: [server, retag_server]
|
||||||
permissions: {}
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
@ -540,7 +187,7 @@ jobs:
|
|||||||
|
|
||||||
success-check-ml:
|
success-check-ml:
|
||||||
name: Docker Build & Push ML Success
|
name: Docker Build & Push ML Success
|
||||||
needs: [merge_ml, retag_ml]
|
needs: [machine-learning, retag_ml]
|
||||||
permissions: {}
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
|
185
.github/workflows/multi-runner-build.yml
vendored
Normal file
185
.github/workflows/multi-runner-build.yml
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
name: 'Multi-runner container image build'
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
image:
|
||||||
|
description: 'Name of the image'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
context:
|
||||||
|
description: 'Path to build context'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
dockerfile:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
tag-suffix:
|
||||||
|
description: 'Suffix to append to the image tag'
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
dockerhub-push:
|
||||||
|
description: 'Push to Docker Hub'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
build-args:
|
||||||
|
description: 'Docker build arguments'
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
platforms:
|
||||||
|
description: 'Platforms to build for'
|
||||||
|
type: string
|
||||||
|
runner-mapping:
|
||||||
|
description: 'Mapping from platforms to runners'
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME:
|
||||||
|
required: false
|
||||||
|
DOCKERHUB_TOKEN:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ inputs.image }}
|
||||||
|
DOCKERHUB_IMAGE: altran1502/${{ inputs.image }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
matrix:
|
||||||
|
name: 'Generate matrix'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||||
|
key: ${{ steps.artifact-key.outputs.base }}
|
||||||
|
steps:
|
||||||
|
- name: Generate build matrix
|
||||||
|
id: matrix
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PLATFORMS: ${{ inputs.platforms || 'linux/amd64,linux/arm64' }}
|
||||||
|
RUNNER_MAPPING: ${{ inputs.runner-mapping || '{"linux/amd64":"ubuntu-latest","linux/arm64":"ubuntu-24.04-arm"}' }}
|
||||||
|
run: |
|
||||||
|
matrix=$(jq -R -c \
|
||||||
|
--argjson runner_mapping "${RUNNER_MAPPING}" \
|
||||||
|
'split(",") | map({platform: ., runner: $runner_mapping[.]})' \
|
||||||
|
<<< "${PLATFORMS}")
|
||||||
|
echo "${matrix}"
|
||||||
|
echo "matrix=${matrix}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Determine artifact key
|
||||||
|
id: artifact-key
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
IMAGE: ${{ inputs.image }}
|
||||||
|
SUFFIX: ${{ inputs.tag-suffix }}
|
||||||
|
run: |
|
||||||
|
if [[ -n "${SUFFIX}" ]]; then
|
||||||
|
base="${IMAGE}${SUFFIX}-digests"
|
||||||
|
else
|
||||||
|
base="${IMAGE}-digests"
|
||||||
|
fi
|
||||||
|
echo "${base}"
|
||||||
|
echo "base=${base}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: matrix
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include: ${{ fromJson(needs.matrix.outputs.matrix) }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- uses: ./.github/actions/image-build
|
||||||
|
with:
|
||||||
|
context: ${{ inputs.context }}
|
||||||
|
dockerfile: ${{ inputs.dockerfile }}
|
||||||
|
image: ${{ env.GHCR_IMAGE }}
|
||||||
|
ghcr-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
platform: ${{ matrix.platform }}
|
||||||
|
artifact-key-base: ${{ needs.matrix.outputs.key }}
|
||||||
|
build-args: ${{ inputs.build-args }}
|
||||||
|
|
||||||
|
merge:
|
||||||
|
needs: [matrix, build]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: ${{ needs.matrix.outputs.key }}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
if: ${{ inputs.dockerhub-push }}
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||||
|
|
||||||
|
- name: Generate docker image tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||||
|
env:
|
||||||
|
DOCKER_METADATA_PR_HEAD_SHA: 'true'
|
||||||
|
with:
|
||||||
|
flavor: |
|
||||||
|
# Disable latest tag
|
||||||
|
latest=false
|
||||||
|
suffix=${{ inputs.tag-suffix }}
|
||||||
|
images: |
|
||||||
|
name=${{ env.GHCR_IMAGE }}
|
||||||
|
name=${{ env.DOCKERHUB_IMAGE }},enable=${{ inputs.dockerhub-push }}
|
||||||
|
tags: |
|
||||||
|
# Tag with branch name
|
||||||
|
type=ref,event=branch
|
||||||
|
# Tag with pr-number
|
||||||
|
type=ref,event=pr
|
||||||
|
# Tag with long commit sha hash
|
||||||
|
type=sha,format=long,prefix=commit-
|
||||||
|
# Tag with git tag on release
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=release,enable=${{ github.event_name == 'release' }}
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: ${{ runner.temp }}/digests
|
||||||
|
run: |
|
||||||
|
# Process annotations
|
||||||
|
declare -a ANNOTATIONS=()
|
||||||
|
if [[ -n "$DOCKER_METADATA_OUTPUT_JSON" ]]; then
|
||||||
|
while IFS= read -r annotation; do
|
||||||
|
# Extract key and value by removing the manifest: prefix
|
||||||
|
if [[ "$annotation" =~ ^manifest:(.+)=(.+)$ ]]; then
|
||||||
|
key="${BASH_REMATCH[1]}"
|
||||||
|
value="${BASH_REMATCH[2]}"
|
||||||
|
# Use array to properly handle arguments with spaces
|
||||||
|
ANNOTATIONS+=(--annotation "index:$key=$value")
|
||||||
|
fi
|
||||||
|
done < <(jq -r '.annotations[]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
SOURCE_ARGS=$(printf "${GHCR_IMAGE}@sha256:%s " *)
|
||||||
|
|
||||||
|
docker buildx imagetools create $TAGS "${ANNOTATIONS[@]}" $SOURCE_ARGS
|
Loading…
x
Reference in New Issue
Block a user