Skip to content

Update Nextcloud Docker Images #541

Update Nextcloud Docker Images

Update Nextcloud Docker Images #541

name: Update Nextcloud Docker Images
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
detect:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Detect image:tag combinations with ffmpeg support
id: set-matrix
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
run: |
# Cache for ffmpeg availability: alpine_<arch>=1/0, debian_<arch>=1/0
declare -A ffmpeg_cache
test_ffmpeg() {
local arch="$1"
local is_alpine="$2"
local cache_key="${is_alpine}_${arch//\//_}"
# Return cached result if available
if [[ -n "${ffmpeg_cache[$cache_key]}" ]]; then
return ${ffmpeg_cache[$cache_key]}
fi
echo "Testing ffmpeg for $arch ($( [ "$is_alpine" = "true" ] && echo "Alpine" || echo "Debian"))..."
local result=1
if [ "$is_alpine" = "true" ]; then
if docker run --rm --platform="$arch" alpine:latest sh -c "apk add --no-cache --simulate ffmpeg" >/dev/null 2>&1; then
result=0
fi
else
if docker run --rm --platform="$arch" debian:bookworm-slim sh -c "apt-get update >/dev/null 2>&1 && apt-cache show ffmpeg 2>/dev/null" | grep -q "Package: ffmpeg"; then
result=0
fi
fi
ffmpeg_cache[$cache_key]=$result
if [ $result -eq 0 ]; then
echo " ✓ ffmpeg available"
else
echo " ✗ ffmpeg NOT available, will skip this arch"
fi
return $result
}
combinations=()
# Get available tags and filter out versioned ones
tags=$(curl -s "https://hub.docker.com/v2/repositories/library/nextcloud/tags?page_size=100" | \
jq -r '.results[].name | select(test("^[0-9]") | not)')
for tag in $tags; do
manifest=$(docker manifest inspect nextcloud:$tag 2>/dev/null)
if [ $? -eq 0 ]; then
# Get the current base image digest
base_digest=$(echo "$manifest" | jq -r '.manifests[0].digest // ""')
# Get the base digest label stored in our image (if it exists)
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${DOCKERHUB_USERNAME}/nextcloud:pull" | jq -r '.token')
our_config=$(curl -s -H "Authorization: Bearer $token" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"https://registry-1.docker.io/v2/${DOCKERHUB_USERNAME}/nextcloud/manifests/$tag" 2>/dev/null)
config_digest=$(echo "$our_config" | jq -r '.config.digest // ""')
stored_base_digest=""
if [[ -n "$config_digest" && "$config_digest" != "null" ]]; then
config_blob=$(curl -s -H "Authorization: Bearer $token" \
"https://registry-1.docker.io/v2/${DOCKERHUB_USERNAME}/nextcloud/blobs/$config_digest" 2>/dev/null)
stored_base_digest=$(echo "$config_blob" | jq -r '.config.Labels["org.opencontainers.image.base.digest"] // ""')
fi
# Only rebuild if base image has changed or we don't have the image yet
if [[ -n "$base_digest" && "$base_digest" != "$stored_base_digest" ]]; then
echo "Processing tag: $tag"
# Get all architectures from manifest
all_archs=$(echo "$manifest" | jq -r '.manifests[].platform | select(.os != "unknown" and .architecture != "unknown") | .os + "/" + .architecture + if .variant then "/" + .variant else "" end' | sort | uniq)
# Determine if alpine or debian based
is_alpine="false"
if echo "$tag" | grep -q "alpine"; then
is_alpine="true"
fi
# Filter architectures based on ffmpeg availability (using cache)
supported_archs=()
for arch in $all_archs; do
if test_ffmpeg "$arch" "$is_alpine"; then
supported_archs+=("$arch")
fi
done
if [ ${#supported_archs[@]} -gt 0 ]; then
platforms=$(IFS=,; echo "${supported_archs[*]}")
combinations+=("{\"tag\":\"$tag\",\"platforms\":\"$platforms\",\"base_digest\":\"$base_digest\"}")
echo " → $tag: ${#supported_archs[@]} architectures"
else
echo " → $tag: No supported architectures, skipping"
fi
fi
fi
done
echo "matrix={\"include\":[$(IFS=,; echo "${combinations[*]}")]}" >> $GITHUB_OUTPUT
build:
needs: detect
runs-on: ubuntu-latest
if: ${{ needs.detect.outputs.matrix != '{"include":[]}' && needs.detect.outputs.matrix != '{"include":[,]}' && needs.detect.outputs.matrix != '{"include":[]}' }}
strategy:
matrix: ${{fromJson(needs.detect.outputs.matrix)}}
fail-fast: false
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and Push Nextcloud Image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
build-args: |
IMAGE=nextcloud
TAG=${{ matrix.tag }}
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/nextcloud:${{ matrix.tag }}
platforms: ${{ matrix.platforms }}
provenance: false
sbom: false
labels: |
org.opencontainers.image.base.digest=${{ matrix.base_digest }}