From c177e2158880fe9152a2db4a5a856f0a9982f6db Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 23 Sep 2026 08:29:52 -0400 Subject: [PATCH] ci: gate the legacy mirror behind an environment --- .github/workflows/docker-build-workflow.yaml | 50 +++++++++++++------- .github/workflows/images.yaml | 50 +++++++++++++------- 2 files changed, 68 insertions(+), 32 deletions(-) diff --git a/.github/workflows/docker-build-workflow.yaml b/.github/workflows/docker-build-workflow.yaml index c8f7a4e1..5338994e 100644 --- a/.github/workflows/docker-build-workflow.yaml +++ b/.github/workflows/docker-build-workflow.yaml @@ -64,20 +64,36 @@ jobs: platforms: linux/amd64,linux/arm64 target: ${{ matrix.target }} - # After the repository moves to another owner, deployments still pull the old - # GHCR namespace. Copying each published tag there keeps them updating until the - # deprecation window closes. Copies are cross-repo blob mounts within GHCR, so no - # image data is rebuilt or re-uploaded. - # - # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with - # write:packages for that owner) to turn this on. It stays off when either is unset, - # and while the owner still matches the one publishing the images. - - name: Mirror tags to the legacy namespace - if: ${{ vars.LEGACY_GHCR_OWNER != '' }} - continue-on-error: true + # After the repository moved to another owner, deployments still pull the old GHCR + # namespace. Copying each published tag there keeps them updating until the deprecation + # window closes. Copies are cross-repo blob mounts within GHCR, so no image data is + # rebuilt or re-uploaded. + # + # It runs in its own job so the credential that can write to the legacy namespace lives + # in an environment, out of reach of workflows on other branches, and so a mirror failure + # cannot fail a publish. + # + # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with + # write:packages for that owner) to turn this on. + mirror: + name: Mirror ${{ matrix.image_name }} to the legacy namespace + if: ${{ vars.LEGACY_GHCR_OWNER != '' }} + needs: build + runs-on: ubuntu-latest + timeout-minutes: 15 + continue-on-error: true + environment: legacy-mirror + strategy: + fail-fast: false + matrix: + image_name: [librechat-rag-api-dev, librechat-rag-api-dev-lite] + + steps: + - name: Copy the published tags env: LEGACY_OWNER: ${{ vars.LEGACY_GHCR_OWNER }} LEGACY_TOKEN: ${{ secrets.LEGACY_GHCR_TOKEN }} + IMAGE_NAME: ${{ matrix.image_name }} TAGS: "${{ github.sha }} ${{ inputs.tag }}" run: | set -euo pipefail @@ -92,12 +108,14 @@ jobs: exit 0 fi - # The pull side needs this login too: it replaces the GITHUB_TOKEN credential - # for ghcr.io, and both namespaces are read with it. + # Registry names must be lowercase, and the repository owner is not guaranteed to be. + source_image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/${IMAGE_NAME}" + legacy_image="ghcr.io/${legacy_owner}/${IMAGE_NAME}" + + # This login reads both namespaces: it replaces the default ghcr.io credential. echo "$LEGACY_TOKEN" | docker login ghcr.io -u "$legacy_owner" --password-stdin - legacy_image="ghcr.io/${legacy_owner}/${GHCR_IMAGE##*/}" for tag in $TAGS; do - echo "Mirroring ${GHCR_IMAGE}:${tag} -> ${legacy_image}:${tag}" - docker buildx imagetools create -t "${legacy_image}:${tag}" "${GHCR_IMAGE}:${tag}" + echo "Mirroring ${source_image}:${tag} -> ${legacy_image}:${tag}" + docker buildx imagetools create -t "${legacy_image}:${tag}" "${source_image}:${tag}" done diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml index b2865e8b..8b04a503 100644 --- a/.github/workflows/images.yaml +++ b/.github/workflows/images.yaml @@ -78,20 +78,36 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - # After the repository moves to another owner, deployments still pull the old - # GHCR namespace. Copying each published tag there keeps them updating until the - # deprecation window closes. Copies are cross-repo blob mounts within GHCR, so no - # image data is rebuilt or re-uploaded. - # - # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with - # write:packages for that owner) to turn this on. It stays off when either is unset, - # and while the owner still matches the one publishing the images. - - name: Mirror tags to the legacy namespace - if: ${{ vars.LEGACY_GHCR_OWNER != '' }} - continue-on-error: true + # After the repository moved to another owner, deployments still pull the old GHCR + # namespace. Copying each published tag there keeps them updating until the deprecation + # window closes. Copies are cross-repo blob mounts within GHCR, so no image data is + # rebuilt or re-uploaded. + # + # It runs in its own job so the credential that can write to the legacy namespace lives + # in an environment, out of reach of workflows on other branches, and so a mirror failure + # cannot fail a publish. + # + # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with + # write:packages for that owner) to turn this on. + mirror: + name: Mirror ${{ matrix.image_name }} to the legacy namespace + if: ${{ vars.LEGACY_GHCR_OWNER != '' }} + needs: build + runs-on: ubuntu-latest + timeout-minutes: 15 + continue-on-error: true + environment: legacy-mirror + strategy: + fail-fast: false + matrix: + image_name: [librechat-rag-api-dev, librechat-rag-api-dev-lite] + + steps: + - name: Copy the published tags env: LEGACY_OWNER: ${{ vars.LEGACY_GHCR_OWNER }} LEGACY_TOKEN: ${{ secrets.LEGACY_GHCR_TOKEN }} + IMAGE_NAME: ${{ matrix.image_name }} TAGS: "${{ github.sha }} latest" run: | set -euo pipefail @@ -106,12 +122,14 @@ jobs: exit 0 fi - # The pull side needs this login too: it replaces the GITHUB_TOKEN credential - # for ghcr.io, and both namespaces are read with it. + # Registry names must be lowercase, and the repository owner is not guaranteed to be. + source_image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/${IMAGE_NAME}" + legacy_image="ghcr.io/${legacy_owner}/${IMAGE_NAME}" + + # This login reads both namespaces: it replaces the default ghcr.io credential. echo "$LEGACY_TOKEN" | docker login ghcr.io -u "$legacy_owner" --password-stdin - legacy_image="ghcr.io/${legacy_owner}/${GHCR_IMAGE##*/}" for tag in $TAGS; do - echo "Mirroring ${GHCR_IMAGE}:${tag} -> ${legacy_image}:${tag}" - docker buildx imagetools create -t "${legacy_image}:${tag}" "${GHCR_IMAGE}:${tag}" + echo "Mirroring ${source_image}:${tag} -> ${legacy_image}:${tag}" + docker buildx imagetools create -t "${legacy_image}:${tag}" "${source_image}:${tag}" done