From 194d3917c5df4f4ea1cb13151596f9d2b2d71f59 Mon Sep 17 00:00:00 2001 From: zhanghao Date: Tue, 20 Oct 2026 12:09:03 +0800 Subject: [PATCH] ci: also publish release images to the China mirror Every image this workflow pushes to Docker Hub's 4pdosc is now copied to modelsphere.tencentcloudcr.com/4pdosc as well, under the same tags, for users who pull from mainland China, where Docker Hub is slow or blocked. The copy is by digest with crane, which copies manifests verbatim, so both registries serve identical bytes. (buildx imagetools create was ruled out: given a single manifest, such as an image built with provenance disabled, it wraps it in a new index and the digest changes.) crane is a pinned release, checked against its sha256. It runs as its own step after the Docker Hub push, so a mirror outage fails that step without stopping or half-finishing the Docker Hub release. Without the org secrets TCR_USERNAME / TCR_TOKEN it is skipped with a notice. --- .github/workflows/release.yml | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e28fce0..f526306 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push hang-watcher + id: push uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . @@ -72,3 +73,43 @@ jobs: platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.tags.outputs.t0 }} + + - name: Mirror hang-watcher to modelsphere.tencentcloudcr.com + # The China mirror of Docker Hub's 4pdosc. Copies what this job just pushed, + # by digest, with crane: manifests are copied verbatim, so both registries + # serve identical bytes (`buildx imagetools create` would wrap a single + # manifest in a new index and change its digest). A separate step after the + # Docker Hub push: a mirror outage fails this step but cannot stop or + # half-finish the Docker Hub release. Skipped with a notice when the org + # secrets TCR_USERNAME / TCR_TOKEN are not available. + env: + TAGS: ${{ steps.tags.outputs.t0 }} + DIGEST: ${{ steps.push.outputs.digest }} + TCR_USERNAME: ${{ secrets.TCR_USERNAME }} + TCR_TOKEN: ${{ secrets.TCR_TOKEN }} + CRANE_VERSION: v0.20.6 + CRANE_SHA256: c1d593d01551f2c9a3df5ca0a0be4385a839bd9b86d4a76e18d7b17d16559127 + run: | + set -euo pipefail + mirror=modelsphere.tencentcloudcr.com + if [ -z "$TCR_USERNAME" ] || [ -z "$TCR_TOKEN" ]; then + echo "::notice::TCR_USERNAME/TCR_TOKEN not set; not mirrored to $mirror" + exit 0 + fi + crane="$RUNNER_TEMP/crane" + if [ ! -x "$crane" ]; then # once per job, however many images it mirrors + curl -sSfL -o "$RUNNER_TEMP/crane.tgz" \ + "https://github.com/google/go-containerregistry/releases/download/$CRANE_VERSION/go-containerregistry_Linux_x86_64.tar.gz" + echo "$CRANE_SHA256 $RUNNER_TEMP/crane.tgz" | sha256sum -c - + tar -xzf "$RUNNER_TEMP/crane.tgz" -C "$RUNNER_TEMP" crane + fi + printf '%s' "$TCR_TOKEN" | "$crane" auth login "$mirror" -u "$TCR_USERNAME" --password-stdin + for ref in $(printf '%s\n' "$TAGS" | tr ',' '\n'); do + case "$ref" in + docker.io/4pdosc/*) ref=${ref#docker.io/} ;; + 4pdosc/*) ;; + *) continue ;; + esac + echo "mirroring docker.io/$ref -> $mirror/$ref" + "$crane" copy "docker.io/${ref%:*}@$DIGEST" "$mirror/$ref" + done