From 00f00d6d75605ce1b9907a6155627a3fd1cfb0e4 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Wed, 30 Sep 2026 09:27:55 -0700 Subject: [PATCH] ci: copy each release image to GHCR as a replica The Gitea registry stays authoritative; GHCR becomes a copy of it, the way the GitHub repository is a copy of the Gitea one. After the tag build has pushed the release image to the registry, a new ghcr job copies it to ghcr.io under the same version tag and :latest with `imagetools create` -- a copy, not a rebuild, so the digest on GHCR is the digest on the registry. Anything still pulling the old ghcr.io name, including the TrueNAS app submission, keeps receiving releases. The job uses the run's own token and is left out of the status reported to Gitea, so a GHCR problem cannot fail a release. --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d66ddb..7a3c1ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,6 +361,38 @@ jobs: echo "attached $name" done + # ------------------------------------------------------ ghcr replica ------ + # Copies the release image from the Gitea registry, which stays the + # authoritative one, to ghcr.io under the same version tag and :latest. It is + # a copy, not a second build: the digest on GHCR is the digest on the + # registry, so `docker pull ghcr.io/...` gets exactly the same image. Left + # out of the report to Gitea, like the release copy, so a GHCR problem + # cannot fail a release. + ghcr: + if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'tag' }} + needs: [version, index] + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ needs.version.outputs.version }} + run: | + set -euo pipefail + src="${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}" + dst="ghcr.io/${GITHUB_REPOSITORY,,}" + tag="$TAG" + echo "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin + docker buildx imagetools create -t "$dst:$tag" -t "$dst:latest" "$src:$tag" + want="$(docker buildx imagetools inspect "$src:$tag" --format '{{json .Manifest.Digest}}')" + got="$(docker buildx imagetools inspect "$dst:$tag" --format '{{json .Manifest.Digest}}')" + echo "registry $src:$tag = $want" + echo "ghcr $dst:$tag = $got" + [ "$want" = "$got" ] || echo "::warning::GHCR digest differs from the registry's" + docker logout ghcr.io + # ---------------------------------------------------- github release ------ # Copies this tag's Gitea release -- notes and files -- to a GitHub release, # so the replica's Releases page, and anyone watching it, keeps up. Gitea's