From 2a136456a762f67966b02b9b67ce9a1e21fc04c7 Mon Sep 17 00:00:00 2001 From: Wu Sheng Date: Fri, 14 Aug 2026 13:36:56 +0800 Subject: [PATCH] Publish release images by dispatch, built from the tag Publishing the 9.7.0 GitHub Release produced no workflow run at all - not a failed one, nothing - and the images were never pushed. GitHub runs a release event's workflow as it exists at the tag, not as it exists on the default branch. The v9.7.0 tag was cut on 12 Aug and the release trigger landed on main the next day, so GitHub read the tag's copy of publish-docker.yaml, found no release trigger, and correctly did nothing. That is not a one-off. The tag is cut at `prepare` and the Release is published at `vote-passed`, at least 72 hours later, so anything changed in this workflow inside that window silently does not apply to the release in flight. The window reopens every release; 9.7.0 was just the first time something landed in it. Drop the release trigger and dispatch instead. `github-release` creates the Release and then runs `gh workflow run publish-docker.yaml -f version=x.y.z`, so publishing stays automatic rather than becoming something to remember, and there is one path rather than two that could both fire. A dispatch always runs the workflow from the default branch, which makes what publishes a function of main rather than of when the tag happened to be cut. If the dispatch fails the Release is already out, so the script warns with the retry commands instead of aborting. Everything the run acts on still comes from the tag rather than from a branch or a timestamp. The tree is checked out at refs/tags/vx.y.z, so Dockerfile and Makefile - which decide what the image actually is - come from the released source instead of from whatever main looks like now; without that a dispatch would have built with main's copies. The agent package remains the sha512- and signature-verified tarball from dist/release, and the image tags come from the version. A leading v on the input is tolerated, and leaving it blank builds a development image exactly as a push to main does. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/publish-docker.yaml | 58 ++++++++++++++++------ docs/en/contribution/release-java-agent.md | 29 ++++++++--- tools/releasing/release.sh | 23 +++++++-- 3 files changed, 83 insertions(+), 27 deletions(-) diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index ba10b71356..17da4c62bc 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -24,19 +24,33 @@ name: publish-docker # tagged x.y.z-, pushed to Docker Hub as # apache/skywalking-java-agent. # -# The release trigger is `released` rather than `published`, so publishing a -# pre-release does not ship official images. Creating the GitHub Release is the -# last step of `tools/releasing/release.sh vote-passed`. +# Releases publish by dispatch, deliberately, rather than by a `release` trigger. +# For a `release` event GitHub runs the workflow as it exists **at the tag**, and +# the tag is cut at `prepare` while the GitHub Release is published at +# `vote-passed`, at least 72 hours later. Anything changed here in between would +# silently not apply to the release in flight - that window is how 9.7.0 shipped +# with no workflow run at all. A dispatch always runs the workflow from the +# default branch, so what is on main is what publishes. +# +# `release.sh github-release` creates the Release and then dispatches this +# workflow, so this stays automatic; running it by hand from the Actions tab is +# how you retry a release whose images failed to push. on: push: branches: - main - release: - types: - - released + workflow_dispatch: + inputs: + version: + description: 'Release version to publish to Docker Hub, e.g. 9.7.0. Leave blank to build a development image from main.' + required: false + default: '' env: SKIP_TEST: true + # Non-empty exactly when this run publishes an official release. Everything + # below keys off this rather than the event name. + RELEASE_TAG: ${{ github.event.inputs.version }} jobs: # One agent package feeds every image. The variants differ only in the JRE they @@ -56,19 +70,19 @@ jobs: # Development images are compiled from the branch. - name: Cache local Maven repository - if: github.event_name != 'release' + if: env.RELEASE_TAG == '' uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-publish-docker-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-maven-publish-docker- - uses: actions/setup-java@v2 - if: github.event_name != 'release' + if: env.RELEASE_TAG == '' with: distribution: temurin java-version: 17 - name: Build Agent - if: github.event_name != 'release' + if: env.RELEASE_TAG == '' run: make build # A release is never rebuilt. The published image has to carry the artifact @@ -79,11 +93,10 @@ jobs: # from dist/dev to dist/release immediately before the GitHub Release that # triggers this workflow, so the file is in place by the time this runs. - name: Download the released agent package - if: github.event_name == 'release' + if: env.RELEASE_TAG != '' run: | set -euo pipefail - TAG=${{ github.event.release.tag_name }} - VERSION=${TAG#v} + VERSION=${RELEASE_TAG#v} BASE="https://dist.apache.org/repos/dist/release/skywalking/java-agent/${VERSION}" TARBALL="apache-skywalking-java-agent-${VERSION}.tgz" @@ -120,18 +133,32 @@ jobs: # A release publishes the complete set the previous manual `make # docker.push.*` produced, alpine included. Per-commit development # images keep the existing JRE-only set. - base: ${{ github.event_name == 'release' && fromJSON('["alpine","java8","java11","java17","java21","java25"]') || fromJSON('["java8","java11","java17","java21","java25"]') }} + base: ${{ github.event.inputs.version && fromJSON('["alpine","java8","java11","java17","java21","java25"]') || fromJSON('["java8","java11","java17","java21","java25"]') }} steps: - uses: actions/checkout@v2 with: submodules: true + # The workflow file necessarily comes from the default branch on a + # dispatch - that is the point, it is what makes the run deterministic. The + # tree it builds must not. Dockerfile and Makefile decide what the image + # actually is, so for a release take them from the tag, exactly as the + # release:perform build and the source tarball did. + - name: Check out the released tag + if: env.RELEASE_TAG != '' + run: | + set -euo pipefail + VERSION=${RELEASE_TAG#v} + git fetch --depth 1 origin "refs/tags/v${VERSION}:refs/tags/v${VERSION}" + git checkout "refs/tags/v${VERSION}" + git submodule update --init --depth 1 + git --no-pager log -1 --format='building from %h %d' - uses: actions/download-artifact@v4 with: name: skywalking-agent path: skywalking-agent - name: Set environment variables run: | - if [[ "${{ github.event_name }}" == "release" ]]; then + if [[ -n "${RELEASE_TAG}" ]]; then # Provisioned by ASF INFRA on request, as for apache/skywalking. # Without them docker/login-action fails with an opaque error, so say # what is actually missing. @@ -149,8 +176,7 @@ jobs: echo "DOCKER_REGISTRY=docker.io" >> $GITHUB_ENV echo "DOCKER_USERNAME=${{ secrets.DOCKERHUB_USER }}" >> $GITHUB_ENV echo "DOCKER_PASSWORD=${{ secrets.DOCKERHUB_TOKEN }}" >> $GITHUB_ENV - TAG=${{ github.event.release.tag_name }} - echo "TAG=${TAG#v}" >> $GITHUB_ENV + echo "TAG=${RELEASE_TAG#v}" >> $GITHUB_ENV else echo "HUB=ghcr.io/apache/skywalking-java" >> $GITHUB_ENV echo "DOCKER_REGISTRY=ghcr.io" >> $GITHUB_ENV diff --git a/docs/en/contribution/release-java-agent.md b/docs/en/contribution/release-java-agent.md index 71b8c500b4..541f01379f 100644 --- a/docs/en/contribution/release-java-agent.md +++ b/docs/en/contribution/release-java-agent.md @@ -134,14 +134,29 @@ After the vote passes, run `vote-passed` which executes: 4. **cleanup** (optional) — if old version is provided, remove it from `dist/release`. Update download page links to point to `https://archive.apache.org/dist/skywalking` ### Docker images -Docker images are published by GitHub Actions, not from your machine. Publishing the -GitHub Release fires the `release: released` trigger in -[`.github/workflows/publish-docker.yaml`](../../../.github/workflows/publish-docker.yaml), -which builds every base variant and pushes +Docker images are published by GitHub Actions, not from your machine. `github-release` +publishes the GitHub Release and then dispatches +[`.github/workflows/publish-docker.yaml`](../../../.github/workflows/publish-docker.yaml) +with the release version. It builds every base variant and pushes `apache/skywalking-java-agent:x.y.z-{alpine,java8,java11,java17,java21,java25}` to Docker -Hub for `linux/amd64` and `linux/arm64`. Watch that workflow; if it fails you can fall back -to pushing from your machine with `./tools/releasing/release.sh docker x.y.z`, which needs -you to be logged in to Docker Hub with push access to the `apache` organisation. +Hub for `linux/amd64` and `linux/arm64`. Watch that workflow. + +If it fails, run it again: **Actions → publish-docker → Run workflow**, entering the +version (`9.7.0`). That is the same path `github-release` takes, so retrying is safe and +idempotent. + +> [!NOTE] +> There is deliberately **no `release:` trigger**. GitHub runs a release event's workflow as +> it exists *at the tag*, and the tag is cut at `prepare` while the Release is published at +> `vote-passed`, at least 72 hours later — so any change to the workflow in between would +> silently not apply to the release in flight. That window is why 9.7.0 published with no +> workflow run at all. Dispatching instead means the workflow always comes from `main`, +> while everything it acts on comes from the tag: the tree it builds is checked out at +> `vx.y.z`, and the agent package is the verified tarball from `dist/release`. + +As a last resort you can push from your machine with +`./tools/releasing/release.sh docker x.y.z`, which needs you to be logged in to Docker Hub +with push access to the `apache` organisation. The image contains the exact tarball that was voted on. The workflow downloads `apache-skywalking-java-agent-x.y.z.tgz` from `dist/release`, checks it against the diff --git a/tools/releasing/release.sh b/tools/releasing/release.sh index 210bd56a12..72e06e7aa8 100755 --- a/tools/releasing/release.sh +++ b/tools/releasing/release.sh @@ -689,11 +689,26 @@ cmd_github_release() { fi gh release create "${tag}" --title "${version}" "${notes_args[@]}" - info "GitHub Release ${tag} published." - info " publish-docker.yaml is now pushing to Docker Hub:" - info " apache/skywalking-java-agent:${version}-{alpine,java8,java11,java17,java21,java25}" - info " Watch: https://github.com/apache/skywalking-java/actions/workflows/publish-docker.yaml" + + # Dispatch rather than let a `release` trigger fire. GitHub runs a release + # event's workflow as it exists at the tag, and the tag is cut at `prepare` + # while this runs at `vote-passed`, at least 72 hours later - anything + # changed in publish-docker.yaml in between would silently not apply. A + # dispatch always runs the workflow from the default branch, so the release + # publishes with what is on main, and builds from the tag. + info "Dispatching publish-docker for ${version}..." + if gh workflow run publish-docker.yaml -f version="${version}"; then + info " Pushing to Docker Hub:" + info " apache/skywalking-java-agent:${version}-{alpine,java8,java11,java17,java21,java25}" + info " Watch: https://github.com/apache/skywalking-java/actions/workflows/publish-docker.yaml" + else + warn " Could not dispatch publish-docker. The release itself is published;" + warn " only the images are missing. Retry with:" + warn " gh workflow run publish-docker.yaml -f version=${version}" + warn " or from Actions -> publish-docker -> Run workflow, or as a last resort" + warn " $0 docker ${version}" + fi } # ============================================================