From a6f94cfd95603a54ef9903520789336e74066612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 14 May 2026 19:18:07 +0200 Subject: [PATCH] fix(ci): lowercase GHCR image ref for the multi-arch publish workflow The v1.4.25 W11a refactor moved the workflow off `docker/metadata-action` as the single source of the image-ref string and started passing `${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}` directly into the `outputs:` of `docker/build-push-action`. `IMAGE_NAME` comes from `github.repository` (`MBombeck/HealthLog`), and GHCR rejects pushes with any uppercase character in the repo segment, so both per-platform builds failed with `invalid reference format`. Resolve the lowercase form once at the top of each job via `${VAR,,}` bash expansion and reuse it across `metadata-action`, `build-push-action`, and the `buildx imagetools` calls in the merge stage. --- .github/workflows/docker-publish.yml | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 752cb854b..14187490f 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -65,6 +65,18 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + # GHCR rejects repository refs with uppercase letters. `IMAGE_NAME` + # is derived from `github.repository`, which preserves the case of + # the GitHub org/user (`MBombeck/HealthLog`). `docker/metadata-action` + # lowercases its `images:` input transparently, so the legacy + # single-job workflow worked; the multi-arch matrix now feeds + # `outputs:` of build-push-action directly and needs the lowercase + # form there too. Resolve it once at the top of the job and reuse + # via $GITHUB_ENV. + - name: Lowercase image ref + run: | + echo "IMAGE_REF=${REGISTRY}/${IMAGE_NAME,,}" >> "$GITHUB_ENV" + # Skip login on PRs from forks — they don't have package:write. # Image is built but not pushed in that case. - name: Log in to GitHub Container Registry @@ -79,7 +91,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.IMAGE_REF }} labels: | org.opencontainers.image.title=HealthLog org.opencontainers.image.description=Self-hosted health-tracking PWA @@ -100,7 +112,7 @@ jobs: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} outputs: | - type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} + type=image,name=${{ env.IMAGE_REF }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} cache-from: | type=gha,scope=build-${{ matrix.cache_scope }}-${{ github.ref_name }} type=gha,scope=build-${{ matrix.cache_scope }}-main @@ -156,6 +168,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + # GHCR requires the repository ref to be lowercase. See the same + # step in the `build` job above for the longer rationale. + - name: Lowercase image ref + run: | + echo "IMAGE_REF=${REGISTRY}/${IMAGE_NAME,,}" >> "$GITHUB_ENV" + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: @@ -167,7 +185,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.IMAGE_REF }} tags: | type=ref,event=branch type=ref,event=pr @@ -186,11 +204,11 @@ jobs: run: | # shellcheck disable=SC2046,SC2086 docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + $(printf '${{ env.IMAGE_REF }}@sha256:%s ' *) - name: Inspect published manifest run: | - docker buildx imagetools inspect '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}' + docker buildx imagetools inspect '${{ env.IMAGE_REF }}:${{ steps.meta.outputs.version }}' # Phase C2 (v1.4.15): trigger Coolify deploy automatically once GHCR has # the new image. Removes the manual SSH + force-pull recipe documented