Release tooling: tag-based versioning, milestone prompt, and Docker images via GitHub Actions - #821
Conversation
Every step after `prepare` derived the version from the working branch:
version=$(git describe --tags --abbrev=0 | sed 's/^v//')
Release tags are created on release/x.y.z and never become ancestors of main, so
`git describe` cannot see them from main. By the time `vote-passed` runs - days
later, after the release PR is merged and the branch auto-deleted - describe walks
straight past every release tag to an unrelated ancient one, v3.2.6 in this
repository. That is not a loud failure: `promote` would svn mv a 3.2.6 path, and
`docker` would push apache/skywalking-java-agent:3.2.6-{alpine,java8,...} to
Docker Hub under real version tags.
The tag is the only thing that still pins a release once the branch is gone, and
it is already what the rest of the flow acts on: release:perform builds from the
tag, the source tar is cut from the tag, and the vote email quotes the tag's
commit IDs. Resolve the version from the tag list too - branch-independent, and it
outlives the release branch.
resolve_version() takes an explicit argument, else $RELEASE_VERSION, else the
highest vX.Y.Z tag. It validates the x.y.z shape and refuses to run when the tag
does not exist, so a typo aborts instead of addressing a path that was never
released. Wired into stage, upload, email, promote and docker, each of which now
accepts an optional version argument; prepare-vote threads its version through
explicitly rather than re-deriving it.
vote-passed additionally prints what it is about to publish - tag, SVN move,
Docker Hub tags, old version being removed - and asks for confirmation, since both
destinations are public and awkward to walk back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
When cmd_prepare resets CHANGES.md for the next development version it wrote a literal placeholder: All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/xxx?closed=1) Nothing in the release flow ever filled that in, so the placeholder rode the release PR into main unless someone noticed and edited it by hand, leaving the next version's change log pointing at a dead milestone link. Ask for the milestone ID instead. The prompt is raised up front, next to the version confirmation, rather than after release:prepare, so the release does not stop for input in the middle of a multi-minute build. The answer is validated as numeric and cross-checked against the milestone's title on apache/skywalking, so a typo or a stale ID (for example last release's milestone) is reported before anything is committed. NEXT_MILESTONE=<id> answers non-interactively for scripted runs, and a blank answer keeps the old placeholder behaviour but now warns. gh api writes its error body to stdout on failure, so the title lookup gates on gh's exit status; otherwise a 404 payload would be reported as the milestone name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Official images were pushed by the release manager running `make docker.push.*`
locally, from `vote-passed`. That means a multi-arch build of six variants on a
laptop, over whatever network it happens to be on, authenticated with a personal
Docker Hub session, with no record of what ran.
publish-docker.yaml already builds these images for every push to main; it just
sends them to ghcr.io tagged with the commit SHA. Give it a `release: released`
trigger and point that path at Docker Hub instead, matching what apache/skywalking
does. On a release it uses HUB=apache, NAME=skywalking-java-agent and the tag name
with `v` stripped, so the published tags are unchanged:
apache/skywalking-java-agent:x.y.z-{alpine,java8,java11,java17,java21,java25}.
The trigger is `released`, not `published`, so cutting a pre-release does not ship
official images. The matrix gains alpine on releases only, leaving the per-commit
development images as they are. QEMU and buildx are set up explicitly because the
Makefile builds linux/amd64 and linux/arm64.
Creating the GitHub Release is what fires this, so the script now does that as a
release step: `github-release` publishes the tag with changes/changes-x.y.z.md as
its notes, and vote-passed runs it where it used to run `docker`. `docker` stays as
a documented fallback for when the workflow fails.
Requires DOCKERHUB_USER and DOCKERHUB_TOKEN repository secrets, which
apache/skywalking already has.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous local `cmd_docker` extracted apache-skywalking-java-agent-x.y.z.tgz - the artifact that was signed, uploaded to dist and voted on - and fed that directory to the Dockerfile as ARG DIST. Moving the push into GitHub Actions quietly dropped that: build-tar ran `make build`, so the published image would have contained a recompile of the tag rather than the bits the PMC approved. Skip build-tar on release events and download the tarball from the Apache distribution area instead, then prove it is the right one before it goes into an image: sha512 rules out a truncated download, and verifying the detached signature against the project KEYS file rules out anything the release manager did not sign. `release.sh promote` does the svn mv from dist/dev to dist/release immediately before the GitHub Release that triggers this workflow, so the file is in place by the time the job runs. build-docker needs `always()` in its condition, since a skipped build-tar would otherwise skip it as well. Development images are unaffected and still come from the artifact build-tar uploads. Verified against the real 9.7.0 artifact: sha512 matches, gpg reports a good signature from the release manager's key, and the tarball unpacks to skywalking-agent/, which is what the Makefile passes as ARG DIST. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed The previous local Now curl -fsSL -O "${BASE}/${TARBALL}" # dist/release/skywalking/java-agent/x.y.z
curl -fsSL -O "${BASE}/${TARBALL}.asc"
curl -fsSL -O "${BASE}/${TARBALL}.sha512"
sha512sum -c "${TARBALL}.sha512"
curl -fsSL https://downloads.apache.org/skywalking/KEYS | gpg --import
gpg --verify "${TARBALL}.asc" "${TARBALL}"sha512 rules out a truncated download; the signature check rules out anything the release manager did not sign.
Verified against the real 9.7.0 artifact: All three dist URLs return HTTP 200 and the KEYS file carries 29 public keys. |
DOCKERHUB_USER and DOCKERHUB_TOKEN are provisioned by ASF INFRA on request and are not set on this repository yet, so the first release to use this workflow would have died inside docker/login-action with nothing pointing at the cause. Check them at the top of the release path and say what is missing, where they come from, and how to publish in the meantime. Document the request process alongside, since .asf.yaml cannot set secrets and it has to go through an INFRA ticket. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The six image variants differ only in the JRE they sit on. The Dockerfile takes BASE_IMAGE and ADDs the same DIST directory, and the agent is Java 8 bytecode that runs on all of them, so one package serves every variant - which is what the old local `make docker.push.alpine docker.push.java8 ...` did from a single extracted tarball. The matrix I added ignored that and had each of the six jobs download and verify its own copy of the 46MB release tarball: 276MB per release pulled from dist.apache.org, which is SVN-backed rather than a CDN, plus six redundant signature checks. Fold the acquisition back into the single upstream job, which now either compiles the agent (development images) or downloads and verifies the voted tarball (releases), and hands the result to the matrix as an artifact. That also drops the `always()` condition the skipped-job arrangement needed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Supersedes #820, which is folded in here. Three fixes to the release flow, all hit while
releasing 9.7.0.
1. Identify the release by its tag, not the checked-out branch
Every step after
preparederived the version from the working branch:version=$(git describe --tags --abbrev=0 | sed 's/^v//')Release tags are created on
release/x.y.zand never become ancestors ofmain, sogit describecannot see them frommain. By the timevote-passedruns — days later,after the release PR is merged and the branch auto-deleted — describe walks straight past
every release tag to an unrelated ancient one:
Not a loud failure:
promotewouldsvn mva3.2.6path, anddockerwould pushapache/skywalking-java-agent:3.2.6-*to Docker Hub under real-looking version tags.The tag is the only thing that still pins a release once the branch is gone, and it is
already what the rest of the flow acts on —
release:performbuilds from it, the sourcetar is cut from it, the vote email quotes its commit IDs.
resolve_version()now takes anexplicit argument, else
$RELEASE_VERSION, else the highestvX.Y.Ztag; it validates theshape and refuses to run when the tag does not exist. Wired into
stage,upload,email,promote,docker, each of which accepts an optional version argument.vote-passedalso prints what it is about to publish and asks for confirmation, since SVNdist/releaseand Docker Hub are both public and awkward to walk back.2. Prompt for the next milestone ID (was #820)
Resetting
CHANGES.mdfor the next development version wrote a placeholder nothing everfilled in:
Unless the release manager spotted it, the placeholder rode the release PR into
mainandthe next change log shipped pointing at a dead link — which is what happened for 9.7.0 and
had to be corrected by hand.
preparenow asks for the ID, up front next to the version confirmation rather than afterthe multi-minute
release:preparebuild. The answer is validated as numeric andcross-checked against the milestone title on
apache/skywalking, so reusing last release'sID is caught:
NEXT_MILESTONE=<id>answers non-interactively; a blank answer keeps the old placeholderbehaviour but now warns.
3. Publish release Docker images from GitHub Actions
Official images were pushed by the release manager running
make docker.push.*locally: amulti-arch build of six variants on a laptop, over whatever network it happens to be on,
with a personal Docker Hub session and no record of what ran.
publish-docker.yamlalready builds these images on every push tomain— it just sendsthem to ghcr.io tagged with the commit SHA. It now also has a
release: releasedtriggerwhose path targets Docker Hub, matching
apache/skywalking. Published tags are unchanged:released, notpublished, so a pre-release does not ship official images.alpineon releases only; per-commit development images are untouched.linux/amd64andlinux/arm64.Creating the GitHub Release is what fires it, so that becomes a release step:
github-releasepublishes the tag usingchanges/changes-x.y.z.mdas its notes, andvote-passedruns it where it used to rundocker.dockerremains as a documentedfallback.
Important
This needs
DOCKERHUB_USERandDOCKERHUB_TOKENrepository secrets, whichapache/skywalkingalready has but this repository does not yet. Until they are added,the release path will fail at login and the fallback
release.sh docker x.y.zis needed.The three third-party actions are pinned to the same reviewed SHAs already used in
apache/skywalking, so they are on the ASF allow-list.Testing
bash -n tools/releasing/release.shpasses; workflow YAML parses.resolve_versionexercised from a branch cut offmain, wheregit describereturns the brokenv3.2.6:9.7.09.7.0/v9.7.09.7.0RELEASE_VERSION=9.7.09.7.09.6.09.6.0— older releases stay addressable9.9.9abcCHANGES.mdis byte-identical to the hand-written 9.7.0 correction.release, 5 onpush.🤖 Generated with Claude Code