This is an automated email from the ASF dual-hosted git repository.

snazy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new 10bb2ba37 Automate the release guide - Take 2 - Github workflows 
(#2383)
10bb2ba37 is described below

commit 10bb2ba370ab065005b5858de3c8cf8ebc962b14
Author: Pierre Laporte <[email protected]>
AuthorDate: Tue Nov 18 17:48:40 2025 +0100

    Automate the release guide - Take 2 - Github workflows (#2383)
    
    The release automation is simplified to four GitHub workflows that just 
require the really mandatory user input: the version number.
    1. workflow: Trigger the creation of the release branch
    2. workflow: Upgrade the release branch with the version and build the the 
final change-log for that version
    3. workflow: Build the RC artifacts from the release branch and push those 
to the various staging repositories
    4. workflow: Eventually release the artifacts.
    
    See also the [email 
announcement](https://lists.apache.org/thread/d0smz07gnr509yj5dc6omo3cvkf1pnh7).
    
    ---------
    
    Co-authored-by: Robert Stupp <[email protected]>
---
 .github/workflows/gradle.yml                       |   2 +-
 .github/workflows/helm.yml                         |   2 +-
 .github/workflows/python-client.yml                |   2 +-
 .github/workflows/regtest.yml                      |   2 +-
 .../workflows/release-1-create-release-branch.yml  | 122 +++++++
 .../release-2-update-release-candidate.yml         | 206 +++++++++++
 .../release-3-build-and-publish-artifacts.yml      | 400 +++++++++++++++++++++
 .github/workflows/release-4-publish-release.yml    | 379 +++++++++++++++++++
 .github/workflows/spark_client_regtests.yml        |   2 +-
 releasey/README.md                                 |  61 ++++
 releasey/libs/_constants.sh                        |  40 +++
 releasey/libs/_exec.sh                             |  46 +++
 releasey/libs/_github.sh                           |  72 ++++
 releasey/libs/_log.sh                              |  63 ++++
 releasey/libs/_version.sh                          | 177 +++++++++
 releasey/release-process-flowchart.mmd             |  39 ++
 releasey/release-process-flowchart.png             | Bin 0 -> 226494 bytes
 17 files changed, 1610 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index c1241df70..06ad5a123 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -30,7 +30,7 @@ on:
   push:
     branches: [ "main" ]
   pull_request:
-    branches: [ "main" ]
+    branches: [ "main", "release/*" ]
 
 env:
   GRADLE_TOS_ACCEPTED: ${{ vars.GRADLE_TOS_ACCEPTED }}
diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml
index 832bf1663..337e8605f 100644
--- a/.github/workflows/helm.yml
+++ b/.github/workflows/helm.yml
@@ -28,7 +28,7 @@ on:
   push:
     branches: [ "main" ]
   pull_request:
-    branches: [ "main" ]
+    branches: [ "main", "release/*" ]
 
 env:
   GRADLE_TOS_ACCEPTED: ${{ vars.GRADLE_TOS_ACCEPTED }}
diff --git a/.github/workflows/python-client.yml 
b/.github/workflows/python-client.yml
index b8d9b0fab..adab68132 100644
--- a/.github/workflows/python-client.yml
+++ b/.github/workflows/python-client.yml
@@ -30,7 +30,7 @@ on:
   push:
     branches: [ "main" ]
   pull_request:
-    branches: [ "main" ]
+    branches: [ "main", "release/*" ]
 
 env:
   GRADLE_TOS_ACCEPTED: ${{ vars.GRADLE_TOS_ACCEPTED }}
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index d72e215ff..c4c746833 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -22,7 +22,7 @@ on:
   push:
     branches: [ "main" ]
   pull_request:
-    branches: [ "main" ]
+    branches: [ "main", "release/*" ]
 
 env:
   GRADLE_TOS_ACCEPTED: ${{ vars.GRADLE_TOS_ACCEPTED }}
diff --git a/.github/workflows/release-1-create-release-branch.yml 
b/.github/workflows/release-1-create-release-branch.yml
new file mode 100644
index 000000000..cde0fafd0
--- /dev/null
+++ b/.github/workflows/release-1-create-release-branch.yml
@@ -0,0 +1,122 @@
+#
+# 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.
+#
+
+name: Release - 1 - Create Release Branch
+
+on:
+  workflow_dispatch:
+    inputs:
+      version:
+        description: 'Release version without RC number (e.g., 
1.0.0-incubating)'
+        required: true
+        type: string
+      dry_run:
+        description: 'Dry run mode (check to enable, uncheck to perform actual 
operations)'
+        required: false
+        type: boolean
+        default: true
+
+jobs:
+  create-release-branch:
+    name: Release - 1 - Create Release Branch
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          # Fetch full history for proper branch operations
+          fetch-depth: 0
+          # Use a token with write permissions
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+          echo "## Mode" >> $GITHUB_STEP_SUMMARY
+          if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
+            echo "DRY_RUN=1" >> $GITHUB_ENV
+            echo "‼️ DRY_RUN mode enabled - No actual changes will be made" >> 
$GITHUB_STEP_SUMMARY
+          else
+            echo "DRY_RUN=0" >> $GITHUB_ENV
+            echo "DRY_RUN mode disabled - Performing actual operations" >> 
$GITHUB_STEP_SUMMARY
+          fi
+
+      - name: Configure Git
+        run: |
+          git config --global user.name "github-actions[bot]"
+          git config --global user.email 
"github-actions[bot]@users.noreply.github.com"
+
+      - name: Validate release parameters
+        run: |
+          source "${LIBS_DIR}/_version.sh"
+
+          # Extract release parameters from workflow inputs
+          version="${{ github.event.inputs.version }}"
+
+          echo "## Parameters" >> $GITHUB_STEP_SUMMARY
+
+          # Validate version format and extract components
+          if ! validate_and_extract_polaris_version "${version}"; then
+            echo "❌ Invalid version format. Expected: 
major.minor.patch-incubating, got: ${version}" >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          echo "Version: \`${version}\`" >> $GITHUB_STEP_SUMMARY
+
+          # Create branch name in major.minor.x format
+          branch_name="${major}.${minor}.x"
+
+          # Export parameters for next step
+          echo "version=${version}" >> $GITHUB_ENV
+          echo "branch_name=${branch_name}" >> $GITHUB_ENV
+
+      - name: Create release branch
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          release_branch="release/${branch_name}"
+
+          echo "## Summary" >> $GITHUB_STEP_SUMMARY
+
+          # Check if release branch already exists
+          if git show-ref --verify --quiet 
"refs/remotes/origin/${release_branch}"; then
+            echo "❌ Release branch ${release_branch} already exists. Delete 
the existing branch manually if you want to recreate it." >> 
$GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          exec_process git branch "${release_branch}"
+          exec_process git push origin "${release_branch}" --set-upstream
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          🎉 Release branch created successfully:
+
+          | Name | Value |
+          | --- | --- |
+          | Release branch name | \`${release_branch}\` |
+          | Ref | \`$(git rev-parse HEAD)\` |
+          | Version | \`${version}\` |
+          EOT
\ No newline at end of file
diff --git a/.github/workflows/release-2-update-release-candidate.yml 
b/.github/workflows/release-2-update-release-candidate.yml
new file mode 100644
index 000000000..f659a77b7
--- /dev/null
+++ b/.github/workflows/release-2-update-release-candidate.yml
@@ -0,0 +1,206 @@
+#
+# 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.
+#
+
+name: Release - 2 - Update version and Changelog for Release Candidate
+
+on:
+  workflow_dispatch:
+    inputs:
+      dry_run:
+        description: 'Dry run mode (check to enable, uncheck to perform actual 
operations)'
+        required: false
+        type: boolean
+        default: true
+
+jobs:
+  update-release-candidate:
+    name: Release - 2 - Update version and Changelog for Release Candidate
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          # Fetch full history. Branch operations require this.
+          fetch-depth: 0
+          # Use a token with write permissions
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Configure Git
+        run: |
+          git config --global user.name "github-actions[bot]"
+          git config --global user.email 
"github-actions[bot]@users.noreply.github.com"
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+          echo "## Mode" >> $GITHUB_STEP_SUMMARY
+          if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
+            echo "DRY_RUN=1" >> $GITHUB_ENV
+            echo "‼️ DRY_RUN mode enabled - No actual changes will be made" >> 
$GITHUB_STEP_SUMMARY
+          else
+            echo "DRY_RUN=0" >> $GITHUB_ENV
+            echo "DRY_RUN mode disabled - Performing actual operations" >> 
$GITHUB_STEP_SUMMARY
+          fi
+
+      - name: Auto-determine release branch and next RC number
+        run: |
+          source "${LIBS_DIR}/_version.sh"
+
+          # Get the current branch name
+          current_branch=$(git branch --show-current)
+
+          echo "## Parameters" >> $GITHUB_STEP_SUMMARY
+
+          # Validate that we're on a release branch
+          if [[ ! "${current_branch}" =~ ^release/(.+)$ ]]; then
+            echo "❌ Invalid branch: \`${current_branch}\`. This workflow must 
be run from a release branch (release/major.minor.x)" >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Extract version from release branch name
+          branch_version="${BASH_REMATCH[1]}"
+
+          # Validate branch version format and extract components
+          if ! validate_and_extract_branch_version "${branch_version}"; then
+            echo "❌ Invalid release branch version format: 
\`${branch_version}\`, expected: major.minor.x" >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Find the next available patch number for this major.minor version
+          find_next_patch_number "${major}" "${minor}"
+
+          # Build the target version using branch major.minor and determined 
patch
+          version_without_rc="${major}.${minor}.${patch}-incubating"
+
+          # Find the next available RC number by checking existing tags
+          find_next_rc_number "${version_without_rc}"
+
+          # Build the new release tag
+          release_tag="apache-polaris-${version_without_rc}-rc${rc_number}"
+
+          # Export all variables for next steps
+          echo "release_tag=${release_tag}" >> $GITHUB_ENV
+          echo "major=${major}" >> $GITHUB_ENV
+          echo "minor=${minor}" >> $GITHUB_ENV
+          echo "patch=${patch}" >> $GITHUB_ENV
+          echo "rc_number=${rc_number}" >> $GITHUB_ENV
+          echo "version_without_rc=${version_without_rc}" >> $GITHUB_ENV
+          echo "release_branch=${current_branch}" >> $GITHUB_ENV
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          | Parameter | Value |
+          | --- | --- |
+          | Release branch | \`${current_branch}\` |
+          | Version without RC | \`${version_without_rc}\` |
+          | RC number | \`${rc_number}\` |
+          | Release tag | \`${release_tag}\` |
+          EOT
+
+      - name: Verify GitHub checks are passing
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          source "${LIBS_DIR}/_github.sh"
+
+          # Get the current HEAD commit SHA
+          current_commit=$(git rev-parse HEAD)
+
+          echo "## Validation" >> $GITHUB_STEP_SUMMARY
+
+          # Verify all GitHub checks are passing
+          if ! check_github_checks_passed "${current_commit}"; then
+            echo "❌ GitHub checks are not all passing for commit 
\`${current_commit}\`. Please ensure all checks pass before updating the 
release candidate." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          echo "All GitHub checks are passing for commit 
\`${current_commit}\`" >> $GITHUB_STEP_SUMMARY
+
+      - name: Set up Java
+        uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5
+        with:
+          distribution: 'temurin'
+          java-version: '21'
+
+      - name: Update project versions
+        run: |
+          source "${LIBS_DIR}/_version.sh"
+
+          update_version "${version_without_rc}"
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Version update
+          All version files updated to \`${version_without_rc}\`
+          EOT
+
+      - name: Update changelog
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+          exec_process ./gradlew patchChangelog
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Changelog
+          Changelog patched successfully
+          EOT
+
+      - name: Commit and push changes
+        run: |
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Commit version files and changelog
+          exec_process git add \
+            "$VERSION_FILE" \
+            "$HELM_CHART_YAML_FILE" \
+            "$HELM_README_FILE" \
+            "$CHANGELOG_FILE"
+          exec_process git commit -m "[chore] Bump version to 
${version_without_rc} for release candidate ${rc_number}"
+
+          # Push the changes
+          exec_process git push origin "${release_branch}"
+
+          # Get the new commit SHA after our changes
+          new_tag_ref=$(git rev-parse HEAD)
+          echo "new_tag_ref=${new_tag_ref}" >> $GITHUB_ENV
+
+      - name: Create RC tag at new commit
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Create the tag at the new commit
+          exec_process git tag "${release_tag}" "${new_tag_ref}"
+          exec_process git push origin "${release_tag}"
+
+          echo "## Summary" >> $GITHUB_STEP_SUMMARY
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          🎉 Release candidate tag created successfully:
+
+          | Name | Value |
+          | --- | --- |
+          | Release candidate tag | \`${release_tag}\` |
+          | Commit | \`${new_tag_ref}\` |
+          | Version | \`${version_without_rc}\` |
+          | RC number | \`${rc_number}\` |
+          EOT
\ No newline at end of file
diff --git a/.github/workflows/release-3-build-and-publish-artifacts.yml 
b/.github/workflows/release-3-build-and-publish-artifacts.yml
new file mode 100644
index 000000000..bf4cdfa49
--- /dev/null
+++ b/.github/workflows/release-3-build-and-publish-artifacts.yml
@@ -0,0 +1,400 @@
+#
+# 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.
+#
+
+name: Release - 3 - Build and Publish Release Artifacts
+
+on:
+  workflow_dispatch:
+    inputs:
+      dry_run:
+        description: 'Dry run mode (check to enable, uncheck to perform actual 
operations)'
+        required: false
+        type: boolean
+        default: true
+
+jobs:
+  prerequisite-checks:
+    name: Prerequisite Checks
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    outputs:
+      dry_run: ${{ steps.set-outputs.outputs.dry_run }}
+      git_tag: ${{ steps.validate-tag.outputs.git_tag }}
+      version_without_rc: ${{ steps.validate-tag.outputs.version_without_rc }}
+      rc_number: ${{ steps.validate-tag.outputs.rc_number }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          fetch-depth: 0
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Set up environment variables
+        id: set-outputs
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+          echo "## Mode" >> $GITHUB_STEP_SUMMARY
+          if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
+            echo "DRY_RUN=1" >> $GITHUB_ENV
+            echo "dry_run=1" >> $GITHUB_OUTPUT
+            echo "‼️ DRY_RUN mode enabled - No actual changes will be made" >> 
$GITHUB_STEP_SUMMARY
+          else
+            echo "DRY_RUN=0" >> $GITHUB_ENV
+            echo "dry_run=0" >> $GITHUB_OUTPUT
+            echo "DRY_RUN mode disabled - Performing actual operations" >> 
$GITHUB_STEP_SUMMARY
+          fi
+
+      - name: Validate release candidate tag
+        id: validate-tag
+        run: |
+          source "${LIBS_DIR}/_version.sh"
+
+          echo "## Parameters" >> $GITHUB_STEP_SUMMARY
+
+          if ! git_tag=$(git describe --tags --exact-match HEAD 2>/dev/null); 
then
+            echo "❌ Current HEAD is not on a release candidate tag. Please 
checkout a release candidate tag first." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Validate git tag format and extract version components
+          if ! validate_and_extract_git_tag_version "${git_tag}"; then
+            echo "❌ Invalid git tag format: \`${git_tag}\`. Expected format: 
apache-polaris-x.y.z-incubating-rcN." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Export variables for next steps and job outputs
+          echo "git_tag=${git_tag}" >> $GITHUB_ENV
+          echo "version_without_rc=${version_without_rc}" >> $GITHUB_ENV
+          echo "rc_number=${rc_number}" >> $GITHUB_ENV
+
+          echo "git_tag=${git_tag}" >> $GITHUB_OUTPUT
+          echo "version_without_rc=${version_without_rc}" >> $GITHUB_OUTPUT
+          echo "rc_number=${rc_number}" >> $GITHUB_OUTPUT
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          | Parameter | Value |
+          | --- | --- |
+          | Git tag | \`${git_tag}\` |
+          | Version | \`${version_without_rc}\` |
+          | RC number | \`${rc_number}\` |
+          EOT
+
+  build-and-publish-artifacts:
+    name: Build and Publish Release Artifacts
+    runs-on: ubuntu-latest
+    needs: prerequisite-checks
+    permissions:
+      contents: read
+    env:
+      DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }}
+      git_tag: ${{ needs.prerequisite-checks.outputs.git_tag }}
+      version_without_rc: ${{ 
needs.prerequisite-checks.outputs.version_without_rc }}
+      rc_number: ${{ needs.prerequisite-checks.outputs.rc_number }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          fetch-depth: 0
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+      - name: Set up Java
+        uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5
+        with:
+          distribution: 'temurin'
+          java-version: '21'
+
+      - name: Install Subversion
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y subversion
+
+      - name: Import GPG key
+        uses: 
crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
+        with:
+          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+          passphrase: ${{ secrets.GPG_PASSPHRASE }}
+          git_user_signingkey: true
+          git_commit_gpgsign: true
+
+      - name: Build source and binary distributions
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          exec_process ./gradlew assemble sourceTarball -Prelease -PuseGpgAgent
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Build
+          Source and binary distributions built successfully
+          EOT
+
+      - name: Stage artifacts to Apache dist dev repository
+        env:
+          SVN_USERNAME: ${{ secrets.APACHE_USERNAME }}
+          SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          echo "::add-mask::$SVN_PASSWORD"
+
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          dist_dev_dir=${RELEASEY_DIR}/polaris-dist-dev
+          exec_process svn checkout --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive "${APACHE_DIST_URL}${APACHE_DIST_PATH}" 
"${dist_dev_dir}"
+
+          version_dir="${dist_dev_dir}/${version_without_rc}"
+          exec_process mkdir -p "${version_dir}"
+          exec_process cp build/distributions/* "${version_dir}/"
+          exec_process cp runtime/distribution/build/distributions/* 
"${version_dir}/"
+
+          exec_process cd "${dist_dev_dir}"
+          exec_process svn add "${version_without_rc}"
+
+          exec_process svn commit --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris 
${version_without_rc} RC${rc_number}"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Staging to dist dev
+          Artifacts staged to Apache dist dev repository
+          EOT
+
+      - name: Publish and close Apache Nexus staging repository
+        env:
+          ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }}
+          ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Publish artifacts to staging repository
+          exec_process ./gradlew publishToApache closeApacheStagingRepository 
-Prelease -PuseGpgAgent --info 2>&1 | tee gradle_publish_output.txt
+
+          # Extract staging repository ID and URL from Gradle output
+          staging_repo_id=""
+          staging_repo_url=""
+
+          # Look for staging repository ID in the output
+          if grep -q "Created staging repository" gradle_publish_output.txt; 
then
+            staging_repo_id=$(grep "Created staging repository" 
gradle_publish_output.txt | sed --regexp-extended "s/^Created staging 
repository .([a-z0-9-]+). at (.*)/\1/")
+            staging_repo_url=$(grep "Created staging repository" 
gradle_publish_output.txt | sed --regexp-extended "s/^Created staging 
repository .([a-z0-9-]+). at (.*)/\2/")
+          fi
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Nexus Staging Repository
+          Artifacts published and staging repository closed successfully
+
+          | Property | Value |
+          | --- | --- |
+          | Staging Repository ID | \`${staging_repo_id:-"Not extracted"}\` |
+          | Staging Repository URL | ${staging_repo_url:-"Not extracted"} |
+
+          ## Summary
+          🎉 Artifacts built and published successfully:
+
+          | Operation | Status |
+          | --- | --- |
+          | Build source and binary distributions | ✅ |
+          | Stage artifacts to Apache dist dev repository | ✅ |
+          | Stage artifacts to Apache Nexus staging repository | ✅ |
+          | Close Nexus staging repository | ✅ |
+          EOT
+
+  build-docker:
+    name: Build Docker Images
+    runs-on: ubuntu-latest
+    needs: [prerequisite-checks, build-and-publish-artifacts]
+    permissions:
+      contents: read
+    env:
+      DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }}
+      git_tag: ${{ needs.prerequisite-checks.outputs.git_tag }}
+      version_without_rc: ${{ 
needs.prerequisite-checks.outputs.version_without_rc }}
+      rc_number: ${{ needs.prerequisite-checks.outputs.rc_number }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          fetch-depth: 0
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+      - name: Set up Docker Buildx
+        run: |
+          docker buildx use default
+          docker buildx create \
+          --platform linux/amd64,linux/arm64 \
+          --use \
+          --name polarisbuild \
+          --driver-opt network=host || docker buildx use polarisbuild
+          docker buildx inspect
+
+      - name: Set up Java
+        uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5
+        with:
+          distribution: 'temurin'
+          java-version: '21'
+
+      - name: Build Polaris Server Docker image
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          exec_process ./gradlew :polaris-server:assemble 
:polaris-server:quarkusAppPartsBuild --rerun \
+            -Dquarkus.container-image.build=true \
+            -Dquarkus.container-image.push=false \
+            -Dquarkus.docker.buildx.platform="linux/amd64,linux/arm64" \
+            -Dquarkus.container-image.tag="${version_without_rc}"
+
+      - name: Build Polaris Admin Tool Docker image
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          exec_process ./gradlew :polaris-admin:assemble 
:polaris-admin:quarkusAppPartsBuild --rerun \
+            -Dquarkus.container-image.build=true \
+            -Dquarkus.container-image.push=false \
+            -Dquarkus.docker.buildx.platform="linux/amd64,linux/arm64" \
+            -Dquarkus.container-image.tag="${version_without_rc}"
+
+          echo "## Docker Images Summary" >> $GITHUB_STEP_SUMMARY
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          🎉 Docker images built successfully:
+
+          | Component | Status |
+          | --- | --- |
+          | Polaris Server Docker image | ✅ Built |
+          | Polaris Admin Tool Docker image | ✅ Built |
+          EOT
+
+  build-and-stage-helm-chart:
+    name: Build and Stage Helm Chart
+    runs-on: ubuntu-latest
+    needs: [prerequisite-checks, build-docker]
+    permissions:
+      contents: read
+    env:
+      DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }}
+      git_tag: ${{ needs.prerequisite-checks.outputs.git_tag }}
+      version_without_rc: ${{ 
needs.prerequisite-checks.outputs.version_without_rc }}
+      rc_number: ${{ needs.prerequisite-checks.outputs.rc_number }}
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          fetch-depth: 0
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+      - name: Set up Helm
+        uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # 
v4.3.1
+        with:
+          version: 'latest'
+
+      - name: Install Subversion
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y subversion
+
+      - name: Import GPG key
+        uses: 
crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
+        with:
+          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+          passphrase: ${{ secrets.GPG_PASSPHRASE }}
+          git_user_signingkey: true
+          git_commit_gpgsign: true
+
+      - name: Create Helm package
+        env:
+          GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+        run: |
+          echo "::add-mask::$GPG_PASSPHRASE"
+
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Make sure these files are always deleted
+          trap "rm -f /tmp/secring.gpg /tmp/pubring.gpg /tmp/passphrase" EXIT
+
+          echo "$GPG_PASSPHRASE" > /tmp/passphrase
+          gpg --batch --pinentry-mode loopback --passphrase-file 
/tmp/passphrase --export-secret-keys > /tmp/secring.gpg
+          gpg --batch --pinentry-mode loopback --export > /tmp/pubring.gpg
+
+          exec_process cd helm
+          exec_process helm package polaris --sign --key "." --keyring 
/tmp/secring.gpg --passphrase-file /tmp/passphrase
+          exec_process helm verify polaris-${version_without_rc}.tgz --keyring 
/tmp/pubring.gpg
+
+          calculate_sha512 polaris-${version_without_rc}.tgz
+          exec_process gpg --armor --output 
polaris-${version_without_rc}.tgz.asc --detach-sig 
polaris-${version_without_rc}.tgz
+          calculate_sha512 polaris-${version_without_rc}.tgz.prov
+          exec_process gpg --armor --output 
polaris-${version_without_rc}.tgz.prov.asc --detach-sig 
polaris-${version_without_rc}.tgz.prov
+
+      - name: Stage Helm chart to Apache dist dev repository
+        env:
+          SVN_USERNAME: ${{ secrets.APACHE_USERNAME }}
+          SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          echo "::add-mask::$SVN_PASSWORD"
+
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          dist_dev_dir=${RELEASEY_DIR}/polaris-dist-dev
+          exec_process svn checkout --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive "${APACHE_DIST_URL}${APACHE_DIST_PATH}" 
"${dist_dev_dir}"
+
+          exec_process mkdir -p 
"${dist_dev_dir}/helm-chart/${version_without_rc}"
+          exec_process cp helm/polaris-${version_without_rc}.tgz* 
"${dist_dev_dir}/helm-chart/${version_without_rc}/"
+
+          exec_process cd "${dist_dev_dir}/helm-chart"
+          exec_process helm repo index .
+
+          exec_process cd "${dist_dev_dir}"
+          exec_process svn add "helm-chart/${version_without_rc}"
+
+          exec_process svn commit --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris Helm chart 
${version_without_rc} RC${rc_number}"
+
+          echo "## Helm Chart Summary" >> $GITHUB_STEP_SUMMARY
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          🎉 Helm chart built and staged successfully:
+
+          | Component | Status |
+          | --- | --- |
+          | Helm package | ✅ Created and signed |
+          | Apache dist dev repository | ✅ Staged |
+          EOT
diff --git a/.github/workflows/release-4-publish-release.yml 
b/.github/workflows/release-4-publish-release.yml
new file mode 100644
index 000000000..b00f1e506
--- /dev/null
+++ b/.github/workflows/release-4-publish-release.yml
@@ -0,0 +1,379 @@
+#
+# 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.
+#
+
+name: Release - 4 - Publish Release After Vote Success
+
+on:
+  workflow_dispatch:
+    inputs:
+      dry_run:
+        description: 'Dry run mode (check to enable, uncheck to perform actual 
operations)'
+        required: false
+        type: boolean
+        default: true
+      staging_repository_id:
+        description: 'Nexus staging repository ID to release (e.g., 
orgapachepolaris-1234)'
+        required: true
+        type: string
+
+jobs:
+  publish-release:
+    name: Release - 4 - Publish Release After Vote Success
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+        with:
+          # Fetch full history for proper branch operations
+          fetch-depth: 0
+          # Use a token with write permissions
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Setup test environment
+        uses: ./.github/actions/setup-test-env
+
+      - name: Configure Git
+        run: |
+          git config --global user.name "github-actions[bot]"
+          git config --global user.email 
"github-actions[bot]@users.noreply.github.com"
+
+      - name: Set up environment variables
+        run: |
+          echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV
+          echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV
+
+          echo "## Mode" >> $GITHUB_STEP_SUMMARY
+          if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
+            echo "DRY_RUN=1" >> $GITHUB_ENV
+            echo "‼️ DRY_RUN mode enabled - No actual changes will be made" >> 
$GITHUB_STEP_SUMMARY
+          else
+            echo "DRY_RUN=0" >> $GITHUB_ENV
+            echo "DRY_RUN mode disabled - Performing actual operations" >> 
$GITHUB_STEP_SUMMARY
+          fi
+
+          # Validate staging repository ID parameter
+          staging_repo_id="${{ github.event.inputs.staging_repository_id }}"
+          if [[ -z "${staging_repo_id}" ]]; then
+            echo "❌ Staging repository ID is required but not provided." >> 
$GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+          echo "STAGING_REPOSITORY_ID=${staging_repo_id}" >> $GITHUB_ENV
+
+      - name: Install Subversion
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y subversion
+
+          echo "## Input Parameters" >> $GITHUB_STEP_SUMMARY
+          echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY
+          echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
+          echo "| Staging Repository ID | \`${staging_repo_id}\` |" >> 
$GITHUB_STEP_SUMMARY
+
+      - name: Auto-determine release parameters from branch and Git state
+        run: |
+          source "${LIBS_DIR}/_version.sh"
+
+          # Get the current branch name
+          current_branch=$(git branch --show-current)
+
+          echo "## Parameters" >> $GITHUB_STEP_SUMMARY
+
+          # Validate that we're on a release branch
+          if [[ ! "${current_branch}" =~ ^release/(.+)$ ]]; then
+            echo "❌ This workflow must be run from a release branch 
(release/major.minor.x). Current branch: \`${current_branch}\`." >> 
$GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Extract version from release branch name
+          branch_version="${BASH_REMATCH[1]}"
+
+          # Validate branch version format and extract components
+          if ! validate_and_extract_branch_version "${branch_version}"; then
+            echo "❌ Invalid release branch version format: 
\`${branch_version}\`. Expected format: major.minor.x." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Find the next patch number for this major.minor version by looking 
at existing tags
+          find_next_patch_number "${major}" "${minor}"
+          next_patch=$((patch))
+          latest_patch=$((next_patch - 1))
+
+          if [[ ${next_patch} -eq 0 ]]; then
+            echo "❌ No existing tags found for version 
\`${major}.${minor}.0\`. Expected at least one RC to be created before 
publishing a release." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Build the version string for the latest existing patch
+          version_without_rc="${major}.${minor}.${latest_patch}-incubating"
+
+          # Find the latest RC tag for this version
+          find_next_rc_number "${version_without_rc}"
+          latest_rc=$((rc_number - 1))
+
+          if [[ ${latest_rc} -lt 0 ]]; then
+            echo "❌ No RC tags found for version \`${version_without_rc}\`. 
Expected at least one RC to be created before publishing a release." >> 
$GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          rc_tag="apache-polaris-${version_without_rc}-rc${latest_rc}"
+
+          # Verify the RC tag exists
+          if ! git rev-parse "${rc_tag}" >/dev/null 2>&1; then
+            echo "❌ RC tag \`${rc_tag}\` does not exist in repository." >> 
$GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Create final release tag name
+          final_release_tag="apache-polaris-${version_without_rc}"
+
+          # Check if final release tag already exists
+          if git rev-parse "${final_release_tag}" >/dev/null 2>&1; then
+            echo "❌ Final release tag \`${final_release_tag}\` already exists. 
This release may have already been published." >> $GITHUB_STEP_SUMMARY
+            exit 1
+          fi
+
+          # Export variables for next steps
+          echo "version_without_rc=${version_without_rc}" >> $GITHUB_ENV
+          echo "rc_tag=${rc_tag}" >> $GITHUB_ENV
+          echo "final_release_tag=${final_release_tag}" >> $GITHUB_ENV
+          echo "release_branch=${current_branch}" >> $GITHUB_ENV
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          | Parameter | Value |
+          | --- | --- |
+          | Version | \`${version_without_rc}\` |
+          | RC tag to promote | \`${rc_tag}\` |
+          | Final release tag | \`${final_release_tag}\` |
+          | Release branch | \`${current_branch}\` |
+          EOT
+
+      - name: Copy distribution from SVN dev to release space
+        env:
+          SVN_USERNAME: ${{ secrets.APACHE_USERNAME }}
+          SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          echo "::add-mask::$SVN_PASSWORD"
+
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Define source and destination URLs
+          
dev_artifacts_url="${APACHE_DIST_URL}/dev/incubator/polaris/${version_without_rc}"
+          
release_artifacts_url="${APACHE_DIST_URL}/release/incubator/polaris/${version_without_rc}"
+
+          
dev_helm_url="${APACHE_DIST_URL}/dev/incubator/polaris/helm-chart/${version_without_rc}"
+          
release_helm_url="${APACHE_DIST_URL}/release/incubator/polaris/helm-chart/${version_without_rc}"
+
+          exec_process svn mv --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive \
+            "${dev_artifacts_url}" "${release_artifacts_url}" \
+            -m "Release Apache Polaris ${version_without_rc}"
+
+          exec_process svn mv --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive \
+            "${dev_helm_url}" "${release_helm_url}" \
+            -m "Release Apache Polaris Helm chart ${version_without_rc}"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Distribution
+          Artifacts and Helm chart moved from dist dev to dist release
+          EOT
+
+      - name: Set up Helm
+        uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # 
v4.3.1
+        with:
+          version: 'latest'
+
+      - name: Update Helm index in release space
+        env:
+          SVN_USERNAME: ${{ secrets.APACHE_USERNAME }}
+          SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          echo "::add-mask::$SVN_PASSWORD"
+
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Checkout the release Helm chart directory
+          release_helm_dir="${RELEASEY_DIR}/polaris-dist-release-helm-chart"
+          
release_helm_url="${APACHE_DIST_URL}/release/incubator/polaris/helm-chart"
+
+          exec_process svn checkout --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive "${release_helm_url}" "${release_helm_dir}"
+
+          exec_process cd "${release_helm_dir}"
+          exec_process helm repo index .
+          exec_process svn add index.yaml
+          exec_process svn commit --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive -m "Update Helm index for 
${version_without_rc} release"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Helm Index
+          Helm index updated in dist release
+          EOT
+
+      - name: Create final release tag and push to Git repository
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Get the commit SHA that the RC tag points to
+          rc_commit=$(git rev-parse "${rc_tag}")
+          echo "rc_commit=${rc_commit}" >> $GITHUB_ENV
+
+          exec_process git tag -a "${final_release_tag}" "${rc_commit}" -m 
"Apache Polaris ${version_without_rc} Release"
+          exec_process git push apache "${final_release_tag}"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Git Release Tag
+          Final release tag \`${final_release_tag}\` created and pushed
+          EOT
+
+      - name: Set up Docker Buildx
+        run: |
+          docker buildx use default
+          docker buildx create \
+          --platform linux/amd64,linux/arm64 \
+          --use \
+          --name polarisbuild \
+          --driver-opt network=host || docker buildx use polarisbuild
+          docker buildx inspect
+
+      - name: Set up Java
+        uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5
+        with:
+          distribution: 'temurin'
+          java-version: '21'
+
+      - name: Log in to Docker Hub
+        if: env.DRY_RUN == '0'
+        run: |
+          echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login ghcr.io -u "${{ 
secrets.DOCKERHUB_USERNAME }}" --password-stdin
+
+      - name: Publish Polaris Server Docker image to Docker Hub
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          exec_process ./gradlew :polaris-server:assemble 
:polaris-server:quarkusAppPartsBuild --rerun \
+            -Dquarkus.container-image.build=true \
+            -Dquarkus.container-image.push=true \
+            -Dquarkus.docker.buildx.platform="linux/amd64,linux/arm64" \
+            -Dquarkus.container-image.tag="${final_release_tag}"
+
+      - name: Publish Polaris Admin Tool Docker image to Docker Hub
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          exec_process ./gradlew :polaris-admin:assemble 
:polaris-admin:quarkusAppPartsBuild --rerun \
+            -Dquarkus.container-image.build=true \
+            -Dquarkus.container-image.push=true \
+            -Dquarkus.docker.buildx.platform="linux/amd64,linux/arm64" \
+            -Dquarkus.container-image.tag="${final_release_tag}"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Docker Images
+          ✅ Polaris Server and Admin Tool Docker images published to Docker Hub
+          EOT
+
+      - name: Create GitHub release with artifacts
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          SVN_USERNAME: ${{ secrets.APACHE_USERNAME }}
+          SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          echo "::add-mask::$SVN_PASSWORD"
+
+          source "${LIBS_DIR}/_constants.sh"
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Create a temporary directory for downloading artifacts
+          artifacts_dir="${RELEASEY_DIR}/release-artifacts"
+          exec_process mkdir -p "${artifacts_dir}"
+
+          # Download artifacts from Apache dist release space
+          
release_artifacts_url="${APACHE_DIST_URL}/release/incubator/polaris/${version_without_rc}"
+          
release_helm_url="${APACHE_DIST_URL}/release/incubator/polaris/helm-chart/${version_without_rc}"
+
+          # Download main artifacts
+          exec_process svn export --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive \
+            "${release_artifacts_url}" "${artifacts_dir}/artifacts"
+
+          # Download Helm chart artifacts
+          exec_process svn export --username "$SVN_USERNAME" --password 
"$SVN_PASSWORD" --non-interactive \
+            "${release_helm_url}" "${artifacts_dir}/helm"
+
+          # Prepare the content of the Github Release
+          # 
********************************************************************************
+          release_title="Release ${version_without_rc}"
+          release_notes="Apache Polaris ${version_without_rc} Release
+
+          ## Release Artifacts
+          This release includes:
+          - Source and binary distributions
+          - Helm chart package
+          - Docker images published to Docker Hub
+          - Maven artifacts published to Maven Central
+
+          ## Verification
+          All artifacts have been signed with GPG and include SHA-512 
checksums for verification.
+
+          ## Docker Images
+          - \`apache/polaris:${final_release_tag}\` - Polaris Server
+          - \`apache/polaris-admin:${final_release_tag}\` - Polaris Admin Tool"
+          # 
********************************************************************************
+
+          # Create GitHub release
+          exec_process gh release create "${final_release_tag}" \
+            --title "${release_title}" \
+            --notes "${release_notes}" \
+            --target "${rc_commit}"
+
+          # Attach all artifacts from the artifacts directory
+          find "${artifacts_dir}" -type f -name "*.tar.gz" -o -name "*.tgz" -o 
-name "*.asc" -o -name "*.sha512" -o -name "*.prov" | while read -r file; do
+            exec_process gh release upload "${final_release_tag}" "${file}"
+          done
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## GitHub Release
+          GitHub release created: \`${final_release_tag}\`
+          EOT
+
+      - name: Release candidate repository on Nexus
+        env:
+          ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }}
+          ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }}
+        run: |
+          source "${LIBS_DIR}/_exec.sh"
+
+          # Use the Gradle task to release the Apache staging repository with 
the specific staging repository ID
+          exec_process ./gradlew releaseApacheStagingRepository 
--staging-repository-id "${STAGING_REPOSITORY_ID}"
+
+          cat <<EOT >> $GITHUB_STEP_SUMMARY
+          ## Summary
+          🎉 Release published successfully:
+
+          | Component | Status |
+          | --- | --- |
+          | Distribution artifacts | ✅ Moved to release space |
+          | Helm chart and index | ✅ Updated in release space |
+          | Git release tag | ✅ Created and pushed |
+          | GitHub release | ✅ Created with artifacts attached |
+          | Docker images | ✅ Published to Docker Hub |
+          | Nexus repository | ✅ Released |
+          | Version | \`${version_without_rc}\` |
+          | Final release tag | \`${final_release_tag}\` |
+          EOT
diff --git a/.github/workflows/spark_client_regtests.yml 
b/.github/workflows/spark_client_regtests.yml
index 450b11f53..12fe5c95d 100644
--- a/.github/workflows/spark_client_regtests.yml
+++ b/.github/workflows/spark_client_regtests.yml
@@ -22,7 +22,7 @@ on:
   push:
     branches: [ "main" ]
   pull_request:
-    branches: [ "main" ]
+    branches: [ "main", "release/*" ]
 
 env:
   GRADLE_TOS_ACCEPTED: ${{ vars.GRADLE_TOS_ACCEPTED }}
diff --git a/releasey/README.md b/releasey/README.md
new file mode 100644
index 000000000..9dc1ed826
--- /dev/null
+++ b/releasey/README.md
@@ -0,0 +1,61 @@
+<!--
+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.
+-->
+
+# Releasey Scripts
+
+## Important Notice
+
+⚠️ **These scripts are designed for automation and should only be used by 
GitHub workflows.**
+
+The scripts in this directory are:
+- Optimized for the GitHub Actions environment
+- Designed to work with specific workflow contexts and environment variables
+- Not intended for manual execution by release managers
+
+## Release Process Overview
+
+The Polaris release automation follows a structured workflow as illustrated in 
the flowchart below. The process involves both automated GitHub workflows and 
manual steps performed by release managers.
+
+![Release Process Flowchart](release-process-flowchart.png)
+
+## GitHub Workflows
+
+The release automation is implemented through the following GitHub workflows:
+
+1. **[Create Release 
Branch](../.github/workflows/release-1-create-release-branch.yml)** - Creates a 
new release branch from a specified Git SHA
+2. **[Update Release 
Candidate](../.github/workflows/release-2-update-release-candidate.yml)** - 
Updates version files, finalizes changelog, and creates RC tags
+3. **[Build and Publish 
Artifacts](../.github/workflows/release-3-build-and-publish-artifacts.yml)** - 
Consolidated workflow that:
+   - Performs prerequisite checks (tag validation, version extraction)
+   - Builds source/binary artifacts and publishes to Nexus staging
+   - Builds Docker images for server and admin tool
+   - Builds Helm charts and stages them to dist dev repository
+4. **[Publish Release](../.github/workflows/release-4-publish-release.yml)** - 
Finalizes the release:
+   - Copies distribution from dist dev to dist release space
+   - Creates a final release tag and GitHub release
+   - Publishes Docker images to Docker Hub
+   - Releases the candidate repository on Nexus
+
+## Directory Structure
+
+- `libs/` - Shared library functions used across release scripts
+  - `_constants.sh` - Common constants and configuration
+  - `_exec.sh` - Command execution utilities
+  - `_github.sh` - GitHub API interaction functions
+  - `_log.sh` - Logging utilities
+  - `_version.sh` - Version handling functions
\ No newline at end of file
diff --git a/releasey/libs/_constants.sh b/releasey/libs/_constants.sh
new file mode 100644
index 000000000..a59175ac6
--- /dev/null
+++ b/releasey/libs/_constants.sh
@@ -0,0 +1,40 @@
+#!/bin/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.
+#
+
+LIBS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Git/SVN repository constants
+APACHE_DIST_URL=${APACHE_DIST_URL:-"https://dist.apache.org/repos/dist"}
+APACHE_DIST_PATH=${APACHE_DIST_PATH:-"/dev/incubator/polaris"}
+
+# Execution mode constants
+DRY_RUN=${DRY_RUN:-1}
+
+# Version validation regex patterns
+VERSION_REGEX="([0-9]+)\.([0-9]+)\.([0-9]+)-incubating"
+VERSION_REGEX_GIT_TAG="^apache-polaris-$VERSION_REGEX-rc([0-9]+)$"
+# Branch validation regex pattern for major.minor.x format
+BRANCH_VERSION_REGEX="([0-9]+)\.([0-9]+)\.x"
+
+# Project file constants
+VERSION_FILE="$LIBS_DIR/../../version.txt"
+CHANGELOG_FILE="$LIBS_DIR/../../CHANGELOG.md"
+HELM_CHART_YAML_FILE="$LIBS_DIR/../../helm/polaris/Chart.yaml"
+HELM_README_FILE="$LIBS_DIR/../../helm/polaris/README.md"
diff --git a/releasey/libs/_exec.sh b/releasey/libs/_exec.sh
new file mode 100644
index 000000000..ae83c3fad
--- /dev/null
+++ b/releasey/libs/_exec.sh
@@ -0,0 +1,46 @@
+#!/bin/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.
+#
+
+LIBS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+source "$LIBS_DIR/_constants.sh"
+source "$LIBS_DIR/_log.sh"
+
+function exec_process {
+  if [[ ${DRY_RUN:-1} -ne 1 ]]; then
+    print_command "Executing '${*}'"
+    "$@"
+  else
+    print_command "Dry-run, WOULD execute '${*}'"
+  fi
+}
+
+function calculate_sha512 {
+  local source_file="$1"
+  local target_file="${source_file}.sha512"
+  # This function is only there for dry-run support.  Because of the
+  # redirection, we cannot use exec_process with the exact command that will be
+  # executed.
+  if [[ ${DRY_RUN:-1} -ne 1 ]]; then
+    exec_process shasum -a 512 "${source_file}" > "${target_file}"
+  else
+    exec_process "shasum -a 512 ${source_file} > ${target_file}"
+  fi
+}
diff --git a/releasey/libs/_github.sh b/releasey/libs/_github.sh
new file mode 100755
index 000000000..18002def7
--- /dev/null
+++ b/releasey/libs/_github.sh
@@ -0,0 +1,72 @@
+#!/bin/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.
+#
+
+LIBS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Source common logging functions and constants
+source "${LIBS_DIR}/_log.sh"
+source "${LIBS_DIR}/_constants.sh"
+source "${LIBS_DIR}/_exec.sh"
+
+function get_remote_url() {
+  # Get the URL for a given remote, return non-zero if remote doesn't exist
+  local remote_name="$1"
+  git remote get-url "${remote_name}" 2>/dev/null || return 1
+}
+
+function check_github_checks_passed() {
+  # Check that all GitHub checks have passed for a specific commit SHA.
+  # More specifically, we check that all check runs have a conclusion of 
"success" or "skipped".
+  # Returns 0 if all checks are "success" or "skipped", 1 otherwise
+  local commit_sha="$1"
+
+  print_info "Checking that all Github checks have passed for commit 
${commit_sha}..."
+
+  # Get repository information from the current Git repository
+  local repo_info="$GITHUB_REPOSITORY"
+
+  local num_invalid_checks
+  local num_invalid_checks_retrieval_command="gh api 
repos/${repo_info}/commits/${commit_sha}/check-runs --jq '[.check_runs[] | 
select(.conclusion != \"success\" and .conclusion != \"skipped\" and (.name | 
startswith(\"Release - \") | not))] | length'"
+  if [ ${DRY_RUN} -eq 1 ]; then
+    print_info "DRY_RUN is enabled, skipping GitHub check verification"
+    print_command "${num_invalid_checks_retrieval_command}"
+    num_invalid_checks=0
+  else
+    # Ideally, we should be able to use 
num_invalid_checks=$(${num_invalid_checks_retrieval_command}) but it seems 
Github does not like that.  Use a temporary script instead.
+    local temp_script=$(mktemp)
+    echo "${num_invalid_checks_retrieval_command}" > "${temp_script}"
+    num_invalid_checks=$(bash "${temp_script}")
+    script_exit_code=$?
+    rm -f "${temp_script}"
+  fi
+
+  if [[ $script_exit_code -ne 0 ]]; then
+    print_error "Failed to fetch GitHub check runs for commit ${commit_sha}"
+    return 1
+  fi
+
+  if [[ ${num_invalid_checks} -ne 0 ]]; then
+    print_error "Found ${num_invalid_checks} failed or in-progress GitHub 
checks for commit ${commit_sha}"
+    return 1
+  fi
+
+  print_info "All GitHub checks passed for commit ${commit_sha}"
+  return 0
+}
\ No newline at end of file
diff --git a/releasey/libs/_log.sh b/releasey/libs/_log.sh
new file mode 100644
index 000000000..a599c2420
--- /dev/null
+++ b/releasey/libs/_log.sh
@@ -0,0 +1,63 @@
+#!/bin/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.
+#
+
+#
+# Common Logging Functions
+#
+
+# Colors for output - only use colors if terminal supports them
+if [[ -t 2 ]] &&
+   [[ "${NO_COLOR:-}" != "1" ]] &&
+   [[ "${TERM:-}" != "dumb" ]] &&
+   command -v tput >/dev/null; then
+  RED=${RED:-$(tput setaf 1)}
+  GREEN=${GREEN:-$(tput setaf 2)}
+  YELLOW=${YELLOW:-$(tput bold; tput setaf 3)}
+  BLUE=${BLUE:-$(tput setaf 4)}
+  RESET=${RESET:-$(tput sgr0)}
+else
+  RED=${RED:-''}
+  GREEN=${GREEN:-''}
+  YELLOW=${YELLOW:-''}
+  BLUE=${BLUE:-''}
+  RESET=${RESET:-''}
+fi
+
+function print_error() {
+  echo -e "${RED}ERROR: $*${RESET}" >&2
+}
+
+function print_warning() {
+  echo -e "${YELLOW}WARNING: $*${RESET}" >&2
+}
+
+function print_info() {
+  echo "INFO: $*" >&2
+}
+
+function print_success() {
+  echo -e "${GREEN}SUCCESS: $*${RESET}" >&2
+}
+
+function print_command() {
+  # This function prints the bash commands that are executed by a script
+  # with a DEBUG prefix to stderr for visibility.
+  echo -e "${BLUE}DEBUG: $*${RESET}" >&2
+}
diff --git a/releasey/libs/_version.sh b/releasey/libs/_version.sh
new file mode 100644
index 000000000..7d812f130
--- /dev/null
+++ b/releasey/libs/_version.sh
@@ -0,0 +1,177 @@
+#!/bin/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.
+#
+
+#
+# Utility functions for version validation and version.txt manipulation
+#
+
+LIBS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+source "$LIBS_DIR/_constants.sh"
+source "$LIBS_DIR/_exec.sh"
+
+function validate_and_extract_branch_version {
+  # This function validates the format of a release branch version and 
extracts its components (major.minor).
+  # It now accepts the major.minor.x format (e.g., "1.1.x") instead of exact 
version format.
+  # It returns 0 if the version is valid and sets the global variables major, 
minor.
+  # The patch version is not extracted from the branch name as it uses the "x" 
placeholder.
+  # Otherwise, it returns 1.
+  local version="$1"
+
+  if [[ ! ${version} =~ ${BRANCH_VERSION_REGEX} ]]; then
+    return 1
+  fi
+
+  major="${BASH_REMATCH[1]}"
+  minor="${BASH_REMATCH[2]}"
+  # patch is not set from branch name since it uses "x" placeholder
+
+  return 0
+}
+
+function validate_and_extract_git_tag_version {
+  # This function validates the format of a git tag version and extracts its 
components (major.minor.patch and rc number).
+  # It is similar to validate_and_extract_rc_version, but for git tag format.
+  # It returns 0 if the version is valid and sets the global variables major, 
minor, patch, and rc_number.
+  # It also sets the global variable version_without_rc to the 
"major.minor.patch-incubating" format without the rc number.
+  # Otherwise, it returns 1.
+  local version="$1"
+
+  if [[ ! ${version} =~ ${VERSION_REGEX_GIT_TAG} ]]; then
+    return 1
+  fi
+
+  major="${BASH_REMATCH[1]}"
+  minor="${BASH_REMATCH[2]}"
+  patch="${BASH_REMATCH[3]}"
+  rc_number="${BASH_REMATCH[4]}"
+  version_without_rc="${major}.${minor}.${patch}-incubating"
+
+  return 0
+}
+
+function validate_and_extract_polaris_version {
+  # This function validates the format of a Polaris version and extracts its 
components (major.minor.patch).
+  # It accepts the full version format (e.g., "1.0.0-incubating") and sets the 
global variables major, minor, patch.
+  # It also sets the global variable version_without_rc to the 
"major.minor.patch-incubating" format.
+  # Returns 0 if the version is valid, 1 otherwise.
+  local version="$1"
+
+  if [[ ! ${version} =~ ${VERSION_REGEX} ]]; then
+    return 1
+  fi
+
+  major="${BASH_REMATCH[1]}"
+  minor="${BASH_REMATCH[2]}"
+  patch="${BASH_REMATCH[3]}"
+  version_without_rc="${major}.${minor}.${patch}-incubating"
+
+  return 0
+}
+
+function update_version {
+  local version="$1"
+  update_version_txt "${version}"
+  update_helm_version "${version}"
+}
+
+function update_version_txt {
+  local version="$1"
+  # This function is only there for dry-run support.  Because of the
+  # redirection, we cannot use exec_process with the exact command that will be
+  # executed.
+  if [[ ${DRY_RUN:-1} -ne 1 ]]; then
+    exec_process echo "${version}" >$VERSION_FILE
+  else
+    exec_process "echo ${version} > $VERSION_FILE"
+  fi
+}
+
+function update_helm_version {
+  local new_version="$1"
+  exec_process sed -E -i~ "s/^version: .+/version: ${new_version}/g" 
"$HELM_CHART_YAML_FILE"
+  exec_process sed -E -i~ "s/^appVersion: .+/appVersion: ${new_version}/g" 
"$HELM_CHART_YAML_FILE"
+  exec_process sed -E -i~ 
"s/[0-9]+[.][0-9]+([.][0-9]+)?(-incubating)-SNAPSHOT/${new_version}/g" 
"$HELM_README_FILE"
+  # The readme file may contain version with double dash for shields.io badges
+  # We need a second `sed` command to ensure that the version replacement 
preserves this double-dash syntax.
+  local current_version_with_dash
+  local version_with_dash
+  current_version_with_dash="${old_version//-/--}"
+  version_with_dash="${version//-/--}"
+  exec_process sed -E -i~ 
"s/[0-9]+[.][0-9]+([.][0-9]+)?(--incubating)--SNAPSHOT/${version_with_dash}/g" 
"$HELM_README_FILE"
+}
+
+function find_next_rc_number {
+  # This function finds the next available RC number for a given version.
+  # It returns 0 and sets the global variable rc_number to the next available 
RC number.
+  # RC numbers start from 0. It takes the version_without_rc as input (e.g., 
"1.0.0-incubating").
+  local version_without_rc="$1"
+
+  # Get all existing RC tags for this version
+  local tag_pattern="apache-polaris-${version_without_rc}-rc*"
+  local existing_tags
+  existing_tags=$(git tag -l "${tag_pattern}" | sort -V)
+
+  if [[ -z "${existing_tags}" ]]; then
+    # No existing RC tags, start with RC0
+    rc_number=0
+  else
+    # Extract the highest RC number and increment
+    local highest_rc
+    highest_rc=$(echo "${existing_tags}" | sed 
"s/apache-polaris-${version_without_rc}-rc//" | sort -n | tail -1)
+    rc_number=$((highest_rc + 1))
+  fi
+
+  return 0
+}
+
+function find_next_patch_number {
+  # This function finds the next available patch number for a given 
major.minor version.
+  # It returns 0 and sets the global variable patch to the next available 
patch number.
+  # Patch numbers start from 0. It takes major and minor as input (e.g., "1", 
"0").
+  local major="$1"
+  local minor="$2"
+
+  # Get all existing tags for this major.minor version
+  local tag_pattern="apache-polaris-${major}.${minor}.*-incubating-rc*"
+  local existing_tags
+  existing_tags=$(git tag -l "${tag_pattern}" | sort -V)
+
+  if [[ -z "${existing_tags}" ]]; then
+    # No existing tags, start with patch 0
+    patch=0
+  else
+    # Extract all patch numbers and find the highest
+    local highest_patch=-1
+    while IFS= read -r tag; do
+      if [[ ${tag} =~ 
apache-polaris-${major}\.${minor}\.([0-9]+)-incubating-rc[0-9]+ ]]; then
+        local current_patch="${BASH_REMATCH[1]}"
+        if [[ ${current_patch} -gt ${highest_patch} ]]; then
+          highest_patch=${current_patch}
+        fi
+      fi
+    done <<< "${existing_tags}"
+
+    # Increment the highest patch number found
+    patch=$((highest_patch + 1))
+  fi
+
+  return 0
+}
\ No newline at end of file
diff --git a/releasey/release-process-flowchart.mmd 
b/releasey/release-process-flowchart.mmd
new file mode 100644
index 000000000..61e33933f
--- /dev/null
+++ b/releasey/release-process-flowchart.mmd
@@ -0,0 +1,39 @@
+%% 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.
+
+flowchart TD
+    B["🤖 GitHub Workflow<br/>🔧 Manual trigger from release manager<br/><br/>• 
Create a new branch from the Git SHA<br/>• Push the branch to the remote 
repository"]
+
+    B --> C["👤 Release Manager<br/><br/>• Cherry pick the commits from main 
branch to release branch<br/>• Ensure that the changelog is up to date<br/>• 
Create a pull request to add the commits<br/>• Get the pull request approved 
and merged"]
+
+    C --> D["🤖 GitHub Workflow<br/>🔧 Manual trigger from release 
manager<br/><br/>• Verify that all Github checks are passing<br/>• Update the 
project version files<br/>• Finalize the changelog for the release<br/>• Commit 
the changes to release branch<br/>• Create the RC tag<br/>• Push the branch and 
the tag"]
+
+    D --> E["🤖 GitHub Workflow<br/>🔧 Manual trigger from release manager<br/>• 
Build the source and binary artifacts<br/>• Stage artifacts to dist dev 
repository<br/>• Publish artifacts to Nexus staging repository<br/>• Close 
Nexus staging repository<br/>• Build Docker images for server and admin 
tool<br/>• Build the Helm chart<br/>• Create signature and checksum for Helm 
package files<br/>• Copy package files to dist dev repository"]
+
+    E --> H["👤 Release Manager<br/><br/>• Start vote thread on dev mailing 
list<br/>• Wait for the vote to pass<br/>• Start vote thread on incubator 
mailing list<br/>• Wait for the vote to pass"]
+
+    H --> I["🤖 GitHub Workflow<br/>🔧 Manual trigger from release 
manager<br/><br/>• Copy distribution from dist dev to dist release space<br/>• 
Create a final release tag to Git repository<br/>• Create the GitHub 
release<br/>• Publish Docker images to DockerHub<br/>• Release the candidate 
repository on Nexus"]
+
+    I --> J["👤 Release Manager<br/><br/>• Update documentation - TBD - Process 
yet to be determined"]
+
+    I --> K["👤 Release Manager<br/><br/>• Send announcement e-mail to dev 
mailing list<br/>• Send announcement to announce Apache mailing list"]
+
+    %% Class definition for left-aligned text
+    classDef leftAlign text-align:left;
+
+    %% Apply left-align class to all nodes
+    class B,C,D,E,H,I,J,K leftAlign;
diff --git a/releasey/release-process-flowchart.png 
b/releasey/release-process-flowchart.png
new file mode 100644
index 000000000..e5814512d
Binary files /dev/null and b/releasey/release-process-flowchart.png differ

Reply via email to