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

Reply via email to