snazy commented on code in PR #2383: URL: https://github.com/apache/polaris/pull/2383#discussion_r2367637435
########## releasey/libs/_github.sh: ########## @@ -0,0 +1,102 @@ +#!/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 get_github_repo_info() { Review Comment: Why not just use [`GITHUB_REPOSITORY`](https://docs.github.com/en/actions/reference/workflows-and-actions/variables)? ########## .github/workflows/release-3-build-and-publish-artifacts.yml: ########## @@ -0,0 +1,352 @@ +# +# 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + 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: | + 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 artifacts to 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" + + exec_process ./gradlew publishToApache -Prelease -PuseGpgAgent + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## Staging to Nexus + Artifacts published to Apache Nexus staging repository + EOT + + - name: Close Nexus staging repository + env: + ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }} + ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }} + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process ./gradlew closeApacheStagingRepository + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + 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="${git_tag}" + + - 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="${git_tag}" + + 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Install Helm GPG plugin + run: | + helm plugin install https://github.com/technosophos/helm-gpg || true + helm plugin list + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Create Helm package + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process cd helm + exec_process helm package polaris + exec_process helm gpg sign polaris-${version_without_rc}.tgz + calculate_sha512 polaris-${version_without_rc}.tgz polaris-${version_without_rc}.tgz.sha512 + 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 polaris-${version_without_rc}.tgz.prov.sha512 + 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: | + 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}" Review Comment: (same `cd` comment as above) ########## .github/workflows/release-4-publish-release.yml: ########## @@ -0,0 +1,340 @@ +# +# 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 + +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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + # Fetch full history for proper branch operations + fetch-depth: 0 + # Use a token with write permissions + token: ${{ secrets.GITHUB_TOKEN }} + + - 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 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: | + 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Update Helm index in release space + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + 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 Java + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Log in to Docker Hub + if: env.DRY_RUN == '0' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - 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: | + 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" + + # Create 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 the 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 + exec_process ./gradlew releaseApacheStagingRepository Review Comment: I'm not sure this works without additional information like the staging repository ID (see [here](https://github.com/apache/polaris/pull/485/files#diff-064f97061477cc170b443791d7292896086582753d2a3b8ebf74bb69196a562aR287-R288)) and capturing those values [in release-3...](https://github.com/apache/polaris/pull/2383/files#diff-b103fc0293df91eb81d72ea7c5bcf5ab5b3fa9ea7b943ae09ad5791431087a9cR182) (see [here](https://github.com/apache/polaris/pull/485/files#diff-4d004b07a60437298ae0dad3ddfb2d0eed301a1a1a5a277420286ef7f60b0836R331-R345)). ########## releasey/libs/_log.sh: ########## @@ -0,0 +1,79 @@ +#!/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 and user hasn't disabled them Review Comment: Super nit: ```suggestion # Colors for output - only use colors if terminal supports them ``` ########## releasey/libs/_github.sh: ########## @@ -0,0 +1,102 @@ +#!/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 get_github_repo_info() { + # Extract GitHub repository owner and name from the origin remote URL + # Returns the info in format "owner/repo", or exits with error if not a GitHub repo + local origin_url + origin_url=$(get_remote_url "origin" 2>/dev/null) || { + print_error "Failed to get origin remote URL" + return 1 + } + + # Parse GitHub URL patterns: + # - https://github.com/owner/repo.git + # - [email protected]:owner/repo.git + # - https://github.com/owner/repo (without .git) + local repo_info + if [[ "$origin_url" =~ ^https://github\.com/([^/]+)/([^/]+)(\.git)?/?$ ]]; then + repo_info="${BASH_REMATCH[1]}/${BASH_REMATCH[2]}" + elif [[ "$origin_url" =~ ^git@github\.com:([^/]+)/([^/]+)\.git/?$ ]]; then + repo_info="${BASH_REMATCH[1]}/${BASH_REMATCH[2]}" + else + print_error "Origin remote URL is not a GitHub repository: ${origin_url}" + return 1 + fi + + echo "$repo_info" +} + +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 + repo_info=$(get_github_repo_info) || { + print_error "Failed to determine GitHub repository information" + return 1 + } + + local num_invalid_checks + local num_invalid_checks_retrieval_command="curl -s -H \"Authorization: Bearer ${GITHUB_TOKEN}\" -H \"Accept: application/vnd.github+json\" -H \"X-GitHub-Api-Version: 2022-11-28\" https://api.github.com/repos/${repo_info}/commits/${commit_sha}/check-runs | jq -r '[.check_runs[] | select(.conclusion != \"success\" and .conclusion != \"skipped\" and (.name | startswith(\"Release - \") | not))] | length'" Review Comment: Nit: Can be simplified by using `gh api` command. ########## .github/workflows/release-3-build-and-publish-artifacts.yml: ########## @@ -0,0 +1,352 @@ +# +# 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + 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: | + 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 artifacts to 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" + + exec_process ./gradlew publishToApache -Prelease -PuseGpgAgent + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## Staging to Nexus + Artifacts published to Apache Nexus staging repository + EOT + + - name: Close Nexus staging repository + env: + ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }} + ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }} + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process ./gradlew closeApacheStagingRepository + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + 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="${git_tag}" + + - 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="${git_tag}" + + 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Install Helm GPG plugin + run: | + helm plugin install https://github.com/technosophos/helm-gpg || true + helm plugin list + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Create Helm package + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process cd helm Review Comment: (same `cd` comment as above) ########## .github/workflows/release-4-publish-release.yml: ########## @@ -0,0 +1,340 @@ +# +# 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 + +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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + # Fetch full history for proper branch operations + fetch-depth: 0 + # Use a token with write permissions + token: ${{ secrets.GITHUB_TOKEN }} + + - 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 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: | + 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Update Helm index in release space + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + 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}" Review Comment: (same `cd` comment as above) ########## .github/workflows/release-3-build-and-publish-artifacts.yml: ########## @@ -0,0 +1,352 @@ +# +# 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + 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: | + 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}" Review Comment: Does the `cd` become effective in this shell? (IIRC a shell function execution runs in its own shell.) ########## .github/workflows/release-3-build-and-publish-artifacts.yml: ########## @@ -0,0 +1,352 @@ +# +# 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + 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: | + 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 artifacts to 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" + + exec_process ./gradlew publishToApache -Prelease -PuseGpgAgent + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## Staging to Nexus + Artifacts published to Apache Nexus staging repository + EOT + + - name: Close Nexus staging repository + env: + ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }} + ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }} + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process ./gradlew closeApacheStagingRepository + + cat <<EOT >> $GITHUB_STEP_SUMMARY + ## 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + 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="${git_tag}" + + - 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="${git_tag}" + + 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Install Helm GPG plugin + run: | + helm plugin install https://github.com/technosophos/helm-gpg || true + helm plugin list + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Create Helm package + run: | + source "${LIBS_DIR}/_exec.sh" + + exec_process cd helm + exec_process helm package polaris + exec_process helm gpg sign polaris-${version_without_rc}.tgz + calculate_sha512 polaris-${version_without_rc}.tgz polaris-${version_without_rc}.tgz.sha512 + 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 polaris-${version_without_rc}.tgz.prov.sha512 + 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: | + 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" Review Comment: (same `cd` comment as above) ########## .github/workflows/release-4-publish-release.yml: ########## @@ -0,0 +1,340 @@ +# +# 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 + +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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + # Fetch full history for proper branch operations + fetch-depth: 0 + # Use a token with write permissions + token: ${{ secrets.GITHUB_TOKEN }} + + - 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 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: | + 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@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4 + with: + version: 'latest' + + - name: Update Helm index in release space + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + 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 Java + uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Log in to Docker Hub + if: env.DRY_RUN == '0' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - 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: | + 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" + + # Create GitHub release + release_title="Release ${version_without_rc}" + release_notes="Apache Polaris ${version_without_rc} Release Review Comment: Can you replace this variable assignment with a `cat << EOT` or so? Or surround with some `# ************************` comment lines? It's a bit hard to find the closing/ending `"`. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
