This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-graalvm-distro.git
The following commit(s) were added to refs/heads/main by this push:
new 7af63a6 Add full-release.sh, update release guide and CI config
7af63a6 is described below
commit 7af63a629568b0df8ea33ac6479c4fbdb3da3288
Author: Wu Sheng <[email protected]>
AuthorDate: Sun Mar 15 10:02:05 2026 +0800
Add full-release.sh, update release guide and CI config
---
.github/workflows/ci.yml | 29 ++++----
docs/release-guide.md | 73 +++++++++++++------
release/full-release.sh | 186 +++++++++++++++++++++++++++++++++++++++++++++++
release/release.sh | 8 ++
4 files changed, 257 insertions(+), 39 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 81e3cd5..b698a0e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -199,8 +199,8 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- - name: Log in to Docker Hub
- if: github.event_name != 'pull_request'
+ - name: Log in to Docker Hub (release only)
+ if: github.event_name != 'pull_request' &&
needs.init-skywalking.outputs.is-release == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USER }}
@@ -218,8 +218,8 @@ jobs:
DIST=${{ steps.dist.outputs.path }}
outputs: type=image,name=${{ env.GHCR_IMAGE
}},push-by-digest=true,name-canonical=true,push=true
- - name: Build and push by digest (Docker Hub)
- if: github.event_name != 'pull_request'
+ - name: Build and push by digest (Docker Hub, release only)
+ if: github.event_name != 'pull_request' &&
needs.init-skywalking.outputs.is-release == 'true'
id: build-dockerhub
uses: docker/build-push-action@v6
with:
@@ -236,8 +236,10 @@ jobs:
mkdir -p /tmp/digests/ghcr /tmp/digests/dockerhub
digest="${{ steps.build-ghcr.outputs.digest }}"
touch "/tmp/digests/ghcr/${digest#sha256:}"
- digest="${{ steps.build-dockerhub.outputs.digest }}"
- touch "/tmp/digests/dockerhub/${digest#sha256:}"
+ if [[ -n "${{ steps.build-dockerhub.outputs.digest }}" ]]; then
+ digest="${{ steps.build-dockerhub.outputs.digest }}"
+ touch "/tmp/digests/dockerhub/${digest#sha256:}"
+ fi
- name: Rename tarball with arch and version
if: github.event_name != 'pull_request'
@@ -398,7 +400,8 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- - name: Log in to Docker Hub
+ - name: Log in to Docker Hub (release only)
+ if: needs.init-skywalking.outputs.is-release == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USER }}
@@ -418,18 +421,14 @@ jobs:
${TAGS} \
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
- - name: Create and push Docker Hub manifest
+ - name: Create and push Docker Hub manifest (release only)
+ if: needs.init-skywalking.outputs.is-release == 'true'
working-directory: /tmp/digests/dockerhub
run: |
VERSION="${{ needs.init-skywalking.outputs.version }}"
- COMMIT_SHA="${{ needs.init-skywalking.outputs.commit-sha }}"
- IS_RELEASE="${{ needs.init-skywalking.outputs.is-release }}"
- TAGS="-t ${{ env.DOCKERHUB_IMAGE }}:${COMMIT_SHA}"
- if [[ "${IS_RELEASE}" == "true" ]]; then
- TAGS="${TAGS} -t ${{ env.DOCKERHUB_IMAGE }}:${VERSION} -t ${{
env.DOCKERHUB_IMAGE }}:latest"
- fi
docker buildx imagetools create \
- ${TAGS} \
+ -t ${{ env.DOCKERHUB_IMAGE }}:${VERSION} \
+ -t ${{ env.DOCKERHUB_IMAGE }}:latest \
$(printf '${{ env.DOCKERHUB_IMAGE }}@sha256:%s ' *)
# ── Upload tarballs to GitHub Release (release builds only) ──
diff --git a/docs/release-guide.md b/docs/release-guide.md
index 2f78deb..42f6c6a 100644
--- a/docs/release-guide.md
+++ b/docs/release-guide.md
@@ -12,14 +12,14 @@ The release manager needs the following tools installed
locally:
| GraalVM JDK 25 | Build macOS native binary | [sdkman](https://sdkman.io/):
`sdk install java 25-graal` |
| GPG | Sign release artifacts | `brew install gnupg` |
| git | Version control | `brew install git` |
-| gh | GitHub CLI (download CI artifacts, create releases) | `brew install gh`
|
+| gh | GitHub CLI (download CI artifacts, create PRs) | `brew install gh` |
| svn | Upload to Apache dist/dev | `brew install svn` |
| make | Build orchestration | Xcode Command Line Tools |
| shasum | SHA-512 checksums | Built-in on macOS |
### GPG Key Setup
-Your GPG key must use an `@apache.org` email address. The release script
verifies this.
+Your GPG key must use an `@apache.org` email address. The release scripts
verify this.
```bash
# Generate a key (if you don't have one)
@@ -31,21 +31,44 @@ gpg --armor --export [email protected] >> KEYS
Ensure your public key is in
https://dist.apache.org/repos/dist/release/skywalking/KEYS.
-## Release Process Overview
+## Release Scripts
-The release is a three-phase pipeline:
+All scripts are in the `release/` directory:
+| Script | Purpose |
+|--------|---------|
+| `full-release.sh` | Automated end-to-end pipeline (recommended) |
+| `pre-release.sh` | Bump version, tag, bump to next SNAPSHOT |
+| `release.sh` | Package, sign, SVN upload, vote email |
+
+## Automated Release (Recommended)
+
+Run a single command to execute the entire release pipeline:
+
+```bash
+release/full-release.sh
```
-pre-release.sh ──push──> GitHub Actions CI ──wait──> release.sh ──> [VOTE]
email
-```
-1. **`pre-release.sh`** — Bump version, tag, bump to next SNAPSHOT
-2. **GitHub Actions CI** — Automatically builds Linux native binaries and
creates GitHub Release
-3. **`release.sh`** — Build macOS binary, create source tarball, sign, upload
to SVN, generate vote email
+The script orchestrates `pre-release.sh` and `release.sh` with CI polling in
between:
+
+1. **Pre-flight checks** — validates tools, GPG key (`@apache.org`), clean
`main` branch,
+ up-to-date with remote, `gh` authentication
+2. **Calls `pre-release.sh`** — prompts for release version and next SNAPSHOT
version,
+ bumps all `pom.xml` files, commits, creates tag
+3. **Pushes tag** — triggers CI release build (Linux native binaries + Docker
+ GitHub Release)
+4. **Creates PR** — moves the SNAPSHOT bump commit to a branch
(`version/{next}-SNAPSHOT`),
+ resets `main` to the tagged release commit, creates a PR for the version
bump
+5. **Polls CI** — checks tag build status every 30s until green (~30-45 min)
+6. **Calls `release.sh`** — builds macOS native binary, creates source tarball,
+ signs all artifacts, uploads to SVN, generates vote email
+
+After the script completes, send the vote email to
`[email protected]`.
+
+## Manual Release (Step by Step)
-## Phase 1: Pre-Release
+If you prefer to run each phase separately, use the three individual steps
below.
-Run from the repo root:
+### Step 1: Pre-Release
```bash
release/pre-release.sh
@@ -64,11 +87,11 @@ After the script completes, review and push:
```bash
git log --oneline -3
-git push origin v0.1.0
-git push origin main
+git push origin v0.1.0 # triggers CI release build
+git push origin main # push next SNAPSHOT version
```
-## Phase 2: GitHub Actions CI (Automatic)
+### Step 2: Wait for CI
Pushing the `v*` tag triggers the CI workflow (`.github/workflows/ci.yml`) in
release mode.
@@ -85,7 +108,7 @@ The GitHub Release page will have:
- `apache-skywalking-graalvm-distro-{version}-linux-amd64.tar.gz` + `.sha512`
- `apache-skywalking-graalvm-distro-{version}-linux-arm64.tar.gz` + `.sha512`
-## Phase 3: Release Packaging
+### Step 3: Release Packaging
Once CI completes, run:
@@ -106,7 +129,7 @@ The script performs these steps in order:
| SVN upload | Upload all artifacts to Apache dist/dev | Asks for Apache LDAP
credentials |
| Vote email | Generate `[VOTE]` email template | Saved to
`release-package/dist/vote-email.txt` |
-### Release Artifacts
+## Release Artifacts
After completion, `release/release-package/dist/` contains:
@@ -121,7 +144,7 @@ vote-email.txt
These are also uploaded to:
`https://dist.apache.org/repos/dist/dev/skywalking/graalvm-distro/0.1.0/`
-## Phase 4: Vote
+## Vote
Send the generated vote email to `[email protected]`.
@@ -130,11 +153,13 @@ The vote remains open for at least 72 hours and requires
PMC approval.
## Quick Reference
```bash
-# Full release flow
-release/pre-release.sh # bump version, tag
-git push origin v0.1.0 # trigger CI
-git push origin main # push next SNAPSHOT
-# ... wait for CI ...
-release/release.sh 0.1.0 # package, sign, upload, generate vote email
-# ... send vote email ...
+# Automated (recommended) — single command, runs everything
+release/full-release.sh
+
+# Manual — three steps
+release/pre-release.sh # 1. bump version, tag, next SNAPSHOT
+git push origin v0.1.0 # push tag (triggers CI)
+git push origin main # push next SNAPSHOT
+ # 2. wait for CI to go green
+release/release.sh 0.1.0 # 3. package, sign, SVN upload, vote email
```
diff --git a/release/full-release.sh b/release/full-release.sh
new file mode 100755
index 0000000..d1aee1f
--- /dev/null
+++ b/release/full-release.sh
@@ -0,0 +1,186 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Full automated release pipeline. Orchestrates pre-release.sh and release.sh
+# with CI polling in between.
+#
+# Flow:
+# 1. Validate prerequisites (tools, GPG, branch, working tree)
+# 2. Run pre-release.sh (bump version, tag, bump to next SNAPSHOT)
+# 3. Move snapshot commit to a PR branch, push tag + PR
+# 4. Poll CI until tag build is green
+# 5. Run release.sh (macOS build, sign, SVN upload, vote email)
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+REPO="apache/skywalking-graalvm-distro"
+
+# ─── Helpers ─────────────────────────────────────────────────────────────────
+log() { echo "===> $*"; }
+error() { echo "ERROR: $*" >&2; exit 1; }
+
+# ─── Step 1: Pre-flight checks ──────────────────────────────────────────────
+log "Pre-flight checks..."
+
+for cmd in git gpg tar gh shasum make svn; do
+ command -v "${cmd}" >/dev/null 2>&1 || error "'${cmd}' is not installed"
+done
+log " Tools: OK"
+
+cd "${REPO_ROOT}"
+
+CURRENT_BRANCH=$(git branch --show-current)
+[[ "${CURRENT_BRANCH}" == "main" ]] \
+ || error "Must be on 'main' branch (currently on '${CURRENT_BRANCH}')"
+
+[[ -z "$(git status --porcelain)" ]] \
+ || error "Working tree is not clean. Commit or stash changes first."
+
+git fetch origin main --quiet
+LOCAL_SHA=$(git rev-parse HEAD)
+REMOTE_SHA=$(git rev-parse origin/main)
+[[ "${LOCAL_SHA}" == "${REMOTE_SHA}" ]] \
+ || error "Local main (${LOCAL_SHA:0:8}) differs from origin/main
(${REMOTE_SHA:0:8}). Pull or push first."
+log " Branch: main, clean, up to date"
+
+GPG_EMAIL=$(gpg --list-secret-keys --keyid-format LONG 2>/dev/null \
+ | grep "uid" | head -1 | sed 's/.*<\(.*\)>.*/\1/')
+[[ "${GPG_EMAIL}" == *@apache.org ]] \
+ || error "GPG key email '${GPG_EMAIL}' is not an @apache.org address."
+log " GPG signer: ${GPG_EMAIL}"
+
+gh auth status --hostname github.com >/dev/null 2>&1 \
+ || error "'gh' is not authenticated. Run: gh auth login"
+log " GitHub CLI: authenticated"
+
+# ─── Step 2: Run pre-release.sh ──────────────────────────────────────────────
+log "Running pre-release.sh..."
+echo ""
+
+"${SCRIPT_DIR}/pre-release.sh"
+
+# ─── Step 3: Extract versions from what pre-release.sh created ───────────────
+# After pre-release.sh: HEAD is "Bump version to X.Y.Z-SNAPSHOT", HEAD~1 is
tagged "Release X.Y.Z"
+NEXT_VERSION=$(sed -n 's/.*<version>\(.*\)<\/version>.*/\1/p'
"${REPO_ROOT}/pom.xml" | head -1)
+TAG=$(git describe --tags --abbrev=0 HEAD~1)
+RELEASE_VERSION="${TAG#v}"
+SNAPSHOT_BRANCH="version/${NEXT_VERSION}"
+
+log "Detected: release=${RELEASE_VERSION}, tag=${TAG}, next=${NEXT_VERSION}"
+
+# ─── Step 4: Move snapshot commit to PR branch ──────────────────────────────
+# Current state: main has [Release X.Y.Z] -> [Bump version to X.Y.Z-SNAPSHOT]
+# We want: main stays at [Release X.Y.Z], PR branch has [Bump version to
X.Y.Z-SNAPSHOT]
+
+log "Moving snapshot commit to branch ${SNAPSHOT_BRANCH}..."
+
+# Create PR branch from current HEAD (which includes the snapshot bump)
+git checkout -b "${SNAPSHOT_BRANCH}"
+
+# Reset main back to the release commit (the tagged one)
+git checkout main
+git reset --hard "${TAG}"
+
+# ─── Step 5: Push tag and PR branch ─────────────────────────────────────────
+log "Pushing tag ${TAG}..."
+git push origin "${TAG}"
+
+log "Pushing branch ${SNAPSHOT_BRANCH}..."
+git push -u origin "${SNAPSHOT_BRANCH}"
+
+log "Creating PR for version bump..."
+PR_URL=$(gh pr create \
+ --repo "${REPO}" \
+ --title "Bump version to ${NEXT_VERSION}" \
+ --body "$(cat <<EOF
+Automated version bump after release ${RELEASE_VERSION}.
+
+- Bumps all pom.xml from \`${RELEASE_VERSION}\` to \`${NEXT_VERSION}\`
+- Created by \`release/full-release.sh\`
+EOF
+)" \
+ --base main \
+ --head "${SNAPSHOT_BRANCH}" \
+ 2>&1)
+log "PR created: ${PR_URL}"
+
+# ─── Step 6: Wait for CI on tag ──────────────────────────────────────────────
+log "Waiting for CI workflow on tag ${TAG}..."
+echo " Polling every 30s. This typically takes 30-45 minutes."
+echo ""
+
+# Wait for the workflow run to appear
+WORKFLOW_RUN_ID=""
+for attempt in $(seq 1 20); do
+ WORKFLOW_RUN_ID=$(gh run list --repo "${REPO}" \
+ --branch "${TAG}" --workflow ci.yml \
+ --json databaseId --jq '.[0].databaseId' 2>/dev/null || true)
+ if [[ -n "${WORKFLOW_RUN_ID}" && "${WORKFLOW_RUN_ID}" != "null" ]]; then
+ break
+ fi
+ echo " [$(date +%H:%M:%S)] Waiting for workflow to start...
(${attempt}/20)"
+ sleep 15
+done
+
+[[ -n "${WORKFLOW_RUN_ID}" && "${WORKFLOW_RUN_ID}" != "null" ]] \
+ || error "Could not find CI workflow run for tag ${TAG} after 5 minutes."
+
+log "Found workflow run: ${WORKFLOW_RUN_ID}"
+echo " https://github.com/${REPO}/actions/runs/${WORKFLOW_RUN_ID}"
+echo ""
+
+# Poll until complete
+while true; do
+ RUN_STATUS=$(gh run view "${WORKFLOW_RUN_ID}" --repo "${REPO}" \
+ --json status,conclusion --jq '.status + ":" + (.conclusion //
"pending")' 2>/dev/null || echo "unknown:unknown")
+
+ STATUS="${RUN_STATUS%%:*}"
+ CONCLUSION="${RUN_STATUS##*:}"
+
+ echo " [$(date +%H:%M:%S)] Status: ${STATUS}, Conclusion: ${CONCLUSION}"
+
+ if [[ "${STATUS}" == "completed" ]]; then
+ if [[ "${CONCLUSION}" == "success" ]]; then
+ log "CI passed!"
+ break
+ else
+ error "CI failed with conclusion: ${CONCLUSION}. Check:
https://github.com/${REPO}/actions/runs/${WORKFLOW_RUN_ID}"
+ fi
+ fi
+
+ sleep 30
+done
+
+# ─── Step 7: Verify GitHub Release ──────────────────────────────────────────
+log "Verifying GitHub Release for ${TAG}..."
+
+gh release view "${TAG}" --repo "${REPO}" >/dev/null 2>&1 \
+ || error "GitHub Release for ${TAG} not found despite CI success."
+
+ASSET_COUNT=$(gh release view "${TAG}" --repo "${REPO}" --json assets --jq
'.assets | length')
+log "GitHub Release has ${ASSET_COUNT} assets"
+
+# ─── Step 8: Run release.sh ─────────────────────────────────────────────────
+echo ""
+echo "═══════════════════════════════════════════════════════"
+echo " CI complete. Starting release packaging..."
+echo "═══════════════════════════════════════════════════════"
+echo ""
+
+exec "${SCRIPT_DIR}/release.sh" "${RELEASE_VERSION}"
diff --git a/release/release.sh b/release/release.sh
index ce34e9a..4458e9d 100755
--- a/release/release.sh
+++ b/release/release.sh
@@ -230,6 +230,14 @@ echo ""
SVN_CHECKOUT_DIR="${SCRIPT_DIR}/${RELEASE_DIR}/svn-checkout"
mkdir -p "${SVN_CHECKOUT_DIR}"
+# Create the parent directory in SVN if it does not exist yet
+if ! svn info "${SVN_DEV_BASE}" --username "${SVN_USER}" --password
"${SVN_PASS}" --non-interactive >/dev/null 2>&1; then
+ log "SVN directory ${SVN_DEV_BASE} does not exist, creating..."
+ svn mkdir "${SVN_DEV_BASE}" \
+ -m "Create graalvm-distro directory for release staging" \
+ --username "${SVN_USER}" --password "${SVN_PASS}" --non-interactive
+fi
+
# Checkout the parent directory (sparse — just top level)
svn checkout --depth empty "${SVN_DEV_BASE}" "${SVN_CHECKOUT_DIR}" \
--username "${SVN_USER}" --password "${SVN_PASS}" --non-interactive