This is an automated email from the ASF dual-hosted git repository. wu-sheng pushed a commit to branch feat/ci-publish-dockerhub-on-tag in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
commit 80281674a46fc86a4d3846c62cbac6896d552328 Author: Wu Sheng <[email protected]> AuthorDate: Sun May 24 09:18:17 2026 +0800 ci: mirror multi-arch image to Docker Hub on tag-push; retire script Step 5 CI already builds the per-arch image natively for every commit + tag and publishes the multi-arch manifest to GHCR. Add a final step in the manifest job (gated on `v*` tag pushes) that mirrors the same multi-arch manifest to Docker Hub via `docker buildx imagetools create`, exactly the way skywalking-graalvm-distro does it. Same blobs, registry-level manifest copy — no rebuild, no QEMU. Tagging on the shared `apache/skywalking-ui` repo follows Horizon's convention: `:horizon-<v>` (immutable) + `:latest` (newest Horizon release). Booster-ui uses its own tags on the same repo. release-finalize.sh Step 5 is no longer the source of truth — it now verifies the Docker Hub tags landed (which they should have, since CI runs on tag-push). If CI didn't publish (workflow failed / DOCKERHUB secrets missing / tag pushed before this workflow shipped), the script falls back to the manual `imagetools create` mirror — same command CI runs, just from a workstation with Docker Hub push rights. That covers the case we hit finalizing 0.5.0 (CI workflow predates this change, so the v0.5.0 tag didn't get the Docker Hub mirror). Also adds scripts/.finalize-work to .gitignore — same pattern as scripts/.release-work; release-script working trees aren't artifacts we want to track. Secrets required on the repo for the new CI step (set by an org admin): DOCKERHUB_USER — Docker Hub account with push rights on apache/skywalking-ui DOCKERHUB_TOKEN — PAT for that account --- .github/workflows/publish-image.yaml | 44 ++++++++++++++- .gitignore | 3 +- scripts/release-finalize.sh | 104 ++++++++++++++++++----------------- 3 files changed, 98 insertions(+), 53 deletions(-) diff --git a/.github/workflows/publish-image.yaml b/.github/workflows/publish-image.yaml index baaeae2..047339d 100644 --- a/.github/workflows/publish-image.yaml +++ b/.github/workflows/publish-image.yaml @@ -14,9 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Publish a multi-arch Horizon UI container image to GHCR on: -# - push to `main` (tagged with `main` + the full commit SHA) -# - any `v*` tag (tagged with the version + the full commit SHA) +# Publish a multi-arch Horizon UI container image: +# - GHCR on every push to `main` and every `v*` tag. +# - Docker Hub (`apache/skywalking-ui`) on `v*` tags only — release +# mirror, not per-commit. The Docker Hub repo is shared with +# booster-ui, so Horizon's tag prefix is `horizon-<v>` and the +# `latest` tag on this repo points at the newest Horizon release. # # Architecture story # The Dockerfile builds `dist/` from source inside the image. The @@ -198,3 +201,38 @@ jobs: echo "::endgroup::" done echo "::notice::Published multi-arch manifest @sha=${{ github.sha }} (amd64+arm64)" + + # ── Mirror to Docker Hub on `v*` tags only. + # + # `apache/skywalking-ui` is shared with booster-ui; Horizon's + # release tags are `horizon-<v>` and the `latest` tag (Horizon + # owns `latest` on this repo as the newest Horizon release — + # booster-ui uses its own dated tags). + # + # `docker buildx imagetools create` does a manifest-level copy of + # the GHCR canonical sha tag (already multi-arch from the loop + # above) into the two Docker Hub tags. No rebuild, copies blobs + # across registries via the registry-to-registry mount API. + - name: Log in to Docker Hub (release only) + if: github.ref_type == 'tag' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Mirror multi-arch manifest to Docker Hub (release only) + if: github.ref_type == 'tag' + env: + DOCKERHUB_IMAGE: apache/skywalking-ui + run: | + set -eu + ver="${GITHUB_REF_NAME#v}" + src="${BASE}:${{ github.sha }}" + echo "::group::Mirror ${src} → ${DOCKERHUB_IMAGE}:horizon-${ver} + :latest" + docker buildx imagetools create \ + -t "${DOCKERHUB_IMAGE}:horizon-${ver}" \ + -t "${DOCKERHUB_IMAGE}:latest" \ + "${src}" + docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:horizon-${ver}" | head -40 + echo "::endgroup::" + echo "::notice::Mirrored to ${DOCKERHUB_IMAGE}:{horizon-${ver},latest}" diff --git a/.gitignore b/.gitignore index 694f426..d068795 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ horizon-wire.jsonl horizon-setup.json # Release -scripts/.release-work \ No newline at end of file +scripts/.release-work +scripts/.finalize-work \ No newline at end of file diff --git a/scripts/release-finalize.sh b/scripts/release-finalize.sh index 671c738..c160c00 100755 --- a/scripts/release-finalize.sh +++ b/scripts/release-finalize.sh @@ -36,11 +36,13 @@ # (src + bin tarballs + .asc + .sha512) fetched back from SVN release, # with the CHANGELOG section for <v> as the body. # -# 3. Build + push the multi-arch (amd64 + arm64) container image to -# Docker Hub apache/skywalking-ui, tagged: -# :horizon-<v> immutable, this release -# :latest moving — newest Horizon release. On this shared -# repo, `latest` serves Horizon (not booster-ui). +# 3. Verify the Docker Hub multi-arch image — CI's publish-image +# workflow mirrors the GHCR image to Docker Hub automatically on +# every `v*` tag push (apache/skywalking-ui:horizon-<v> and +# apache/skywalking-ui:latest). This step just confirms the two +# expected tags are present. Falls back to a manual local mirror +# (same `docker buildx imagetools create` CI runs) if CI didn't +# publish — needs Docker Hub push rights on apache/skywalking-ui. # # Usage: bash scripts/release-finalize.sh # @@ -59,8 +61,6 @@ SVN_RELEASE_URL="https://dist.apache.org/repos/dist/release/skywalking/horizon-u DOCKERHUB_REPO="apache/skywalking-ui" WORK_DIR="${SCRIPT_DIR}/.finalize-work" -BUILDER_NAME="horizon-release-builder" - # ========================== Helpers ========================== err() { echo "ERROR: $*" >&2; } @@ -89,7 +89,8 @@ if [ ${#MISSING[@]} -gt 0 ]; then fi if ! docker buildx version >/dev/null 2>&1; then - err "docker buildx is required for the multi-arch image build." + err "docker buildx is required (Step 5 uses 'imagetools create' to copy" + err "the CI-built multi-arch manifest from GHCR to Docker Hub)." exit 1 fi @@ -248,7 +249,7 @@ else if confirm "Create the GitHub release ${TAG} and attach the 6 artifacts?"; then gh release create "${TAG}" \ --repo apache/skywalking-horizon-ui \ - --title "Apache SkyWalking Horizon UI ${RELEASE_VERSION}" \ + --title "${RELEASE_VERSION}" \ --notes-file "${NOTES_FILE}" \ "${ART_DIR}/${SRC_BASE}" \ "${ART_DIR}/${SRC_BASE}.asc" \ @@ -265,47 +266,52 @@ fi # ========================== Step 5: Docker Hub multi-arch image ========================== note "Step 5 — Docker Hub image: ${DOCKERHUB_REPO}" -# Build from a CLEAN checkout of the tag so the image matches the released -# source exactly (no local uncommitted edits leak in). -BUILD_SRC="${WORK_DIR}/src" -echo "Checking out ${TAG} into ${BUILD_SRC}…" -git -C "${PROJECT_DIR}" archive --format=tar --prefix=src/ "${TAG}" | (cd "${WORK_DIR}" && tar -x) - -# A docker-container builder is required: the default 'docker' driver cannot -# emit a multi-platform manifest. Create one if absent + ensure QEMU is set -# up for the foreign-arch emulation. -if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then - echo "Creating buildx builder '${BUILDER_NAME}' (docker-container driver)…" - docker buildx create --name "${BUILDER_NAME}" --driver docker-container --bootstrap -fi -docker run --privileged --rm tonistiigi/binfmt --install arm64,amd64 >/dev/null 2>&1 || true - -# Horizon publishes the immutable per-release tag plus the moving `latest` -# on the shared repo. `latest` here points at the newest Horizon release — -# pulling `${DOCKERHUB_REPO}:latest` gets Horizon, not booster-ui. -IMG_TAGS=(-t "${DOCKERHUB_REPO}:horizon-${RELEASE_VERSION}" -t "${DOCKERHUB_REPO}:latest") -echo "Image tags to push:" -echo " ${DOCKERHUB_REPO}:horizon-${RELEASE_VERSION} (immutable, this release)" -echo " ${DOCKERHUB_REPO}:latest (moving — newest Horizon release)" - -if confirm "Build linux/amd64+arm64 and push to Docker Hub now? (emulated arch is slow)"; then - docker buildx build \ - --builder "${BUILDER_NAME}" \ - --platform linux/amd64,linux/arm64 \ - --file "${PROJECT_DIR}/Dockerfile" \ - --label "org.opencontainers.image.source=https://github.com/apache/skywalking-horizon-ui" \ - --label "org.opencontainers.image.revision=$(git -C "${PROJECT_DIR}" rev-parse "${TAG}")" \ - --label "org.opencontainers.image.version=${RELEASE_VERSION}" \ - --label "org.opencontainers.image.title=Apache SkyWalking Horizon UI" \ - --label "org.opencontainers.image.description=Next-generation web UI for Apache SkyWalking." \ - --label "org.opencontainers.image.licenses=Apache-2.0" \ - "${IMG_TAGS[@]}" \ - --push \ - "${BUILD_SRC}/src" - echo "Pushed multi-arch image to ${DOCKERHUB_REPO}." - echo "Verify: docker buildx imagetools inspect ${DOCKERHUB_REPO}:horizon-${RELEASE_VERSION}" +# CI (.github/workflows/publish-image.yaml) mirrors the multi-arch image +# to Docker Hub automatically on every `v*` tag push, so by the time +# you're finalizing a passed vote this should already be live. We just +# verify the two expected tags are present. +# +# Fallback: if CI didn't publish (workflow failed / secrets missing / +# tag pushed before this workflow shipped), we fall back to the manual +# `docker buildx imagetools create` mirror from the GHCR canonical tag +# — same operation CI does, run locally. That needs Docker Hub push +# rights on `apache/skywalking-ui`. +DH_VERSION_TAG="${DOCKERHUB_REPO}:horizon-${RELEASE_VERSION}" +DH_LATEST_TAG="${DOCKERHUB_REPO}:latest" +GHCR_SRC="ghcr.io/apache/skywalking-horizon-ui:${RELEASE_VERSION}" + +echo "Expected on Docker Hub:" +echo " ${DH_VERSION_TAG} (immutable, this release)" +echo " ${DH_LATEST_TAG} (moving — newest Horizon release)" + +if docker buildx imagetools inspect "${DH_VERSION_TAG}" >/dev/null 2>&1; then + echo "✓ ${DH_VERSION_TAG} already on Docker Hub — CI's publish-image mirror succeeded." + echo " Verify: docker buildx imagetools inspect ${DH_VERSION_TAG}" else - echo "Skipped Docker Hub push." + echo "✗ ${DH_VERSION_TAG} NOT on Docker Hub yet." + echo " This is the expected outcome only if the publish-image workflow" + echo " failed or didn't run on tag ${TAG}. Check:" + echo " https://github.com/apache/skywalking-horizon-ui/actions/workflows/publish-image.yaml" + if ! docker buildx imagetools inspect "${GHCR_SRC}" >/dev/null 2>&1; then + err "Source ${GHCR_SRC} not on GHCR either — CI didn't produce a multi-arch" + err "image to mirror. Re-run publish-image on ${TAG} from the Actions UI" + err "and then re-run this script." + exit 1 + fi + if confirm "Fall back to a manual local mirror from ${GHCR_SRC}?"; then + docker buildx imagetools create \ + -t "${DH_VERSION_TAG}" \ + -t "${DH_LATEST_TAG}" \ + "${GHCR_SRC}" + echo "Pushed multi-arch manifest to ${DOCKERHUB_REPO}." + else + echo "Skipped Docker Hub push — fix CI and re-run, OR run the imagetools" + echo "create manually:" + echo " docker buildx imagetools create \\" + echo " -t ${DH_VERSION_TAG} \\" + echo " -t ${DH_LATEST_TAG} \\" + echo " ${GHCR_SRC}" + fi fi # ========================== Done ==========================
