From 7b771ec5fce21284f19dca31f5c7512c3f9a0441 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 19 Nov 2025 17:53:31 +0100 Subject: [PATCH 1/4] Step 1 - copy state from apache/polaris repo as-is --- .../release-1-create-release-branch.yml | 122 ++++++ .../release-2-update-release-candidate.yml | 206 +++++++++ .../release-3-build-and-publish-artifacts.yml | 403 ++++++++++++++++++ .../workflows/release-4-publish-release.yml | 379 ++++++++++++++++ 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 ++++++++ 9 files changed, 1508 insertions(+) create mode 100644 .github/workflows/release-1-create-release-branch.yml create mode 100644 .github/workflows/release-2-update-release-candidate.yml create mode 100644 .github/workflows/release-3-build-and-publish-artifacts.yml create mode 100644 .github/workflows/release-4-publish-release.yml create mode 100644 releasey/libs/_constants.sh create mode 100644 releasey/libs/_exec.sh create mode 100755 releasey/libs/_github.sh create mode 100644 releasey/libs/_log.sh create mode 100644 releasey/libs/_version.sh 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 00000000..55a0ba82 --- /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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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 00000000..ee4bae02 --- /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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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 <> $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 <> $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 <> $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 00000000..0b93100f --- /dev/null +++ b/.github/workflows/release-3-build-and-publish-artifacts.yml @@ -0,0 +1,403 @@ +# +# 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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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 <> $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 <> $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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 + # Prerequisite for reproducible helm packages: file modification time must be deterministic + # Works with helm since version 4.0.0 + exec_process find polaris -exec touch -d "1980-01-01 00:00:00" {} + + 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 <> $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 00000000..879c7643 --- /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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # 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 <> $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 <> $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 <> $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 <> $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 <> $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 <> $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 <> $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/releasey/libs/_constants.sh b/releasey/libs/_constants.sh new file mode 100644 index 00000000..a59175ac --- /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 00000000..ae83c3fa --- /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 00000000..18002def --- /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 00000000..a599c242 --- /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 00000000..7d812f13 --- /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 From 4662bc93436ee8dd0660819201cab49e9eb7a9fe Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 19 Nov 2025 19:01:32 +0100 Subject: [PATCH 2/4] DRAFT IDEA of "pluggable" tools release workflows --- .github/workflows/apprunner-ci.yml | 3 +- .../release-1-create-release-branch.yml | 17 +- .../release-2-update-release-candidate.yml | 32 +- .../release-3-build-and-publish-artifacts.yml | 371 +++++++++--------- .../workflows/release-4-publish-release.yml | 258 ++---------- apprunner/releasey/maven-build.sh | 33 ++ apprunner/releasey/maven-release.sh | 33 ++ apprunner/releasey/maven-stage.sh | 52 +++ apprunner/releasey/update_changelog.sh | 38 ++ apprunner/releasey/update_version.sh | 44 +++ releasey/libs/_constants.sh | 8 +- releasey/libs/_gh_determine_built_flavors.sh | 52 +++ releasey/libs/_gh_rc_build_helm.sh | 47 +++ releasey/libs/_gh_rc_build_prepare.sh | 47 +++ releasey/libs/_gh_rc_email_proposal.sh | 95 +++++ releasey/libs/_gh_rc_prepare.sh | 62 +++ releasey/libs/_gh_rc_publish_nexus.sh | 50 +++ releasey/libs/_gh_rc_stage_dist_artifacts.sh | 47 +++ releasey/libs/_gh_rc_stage_helm.sh | 55 +++ releasey/libs/_gh_rc_stage_source.sh | 54 +++ releasey/libs/_gh_release_copy_dist.sh | 36 ++ releasey/libs/_gh_release_create_github.sh | 83 ++++ releasey/libs/_gh_release_email_proposal.sh | 61 +++ releasey/libs/_gh_release_helm.sh | 46 +++ releasey/libs/_gh_release_prepare.sh | 102 +++++ releasey/libs/_gh_release_push_git_tag.sh | 32 ++ releasey/libs/_version.sh | 54 +-- 27 files changed, 1350 insertions(+), 462 deletions(-) create mode 100644 apprunner/releasey/maven-build.sh create mode 100644 apprunner/releasey/maven-release.sh create mode 100644 apprunner/releasey/maven-stage.sh create mode 100644 apprunner/releasey/update_changelog.sh create mode 100644 apprunner/releasey/update_version.sh create mode 100644 releasey/libs/_gh_determine_built_flavors.sh create mode 100644 releasey/libs/_gh_rc_build_helm.sh create mode 100644 releasey/libs/_gh_rc_build_prepare.sh create mode 100644 releasey/libs/_gh_rc_email_proposal.sh create mode 100644 releasey/libs/_gh_rc_prepare.sh create mode 100644 releasey/libs/_gh_rc_publish_nexus.sh create mode 100644 releasey/libs/_gh_rc_stage_dist_artifacts.sh create mode 100644 releasey/libs/_gh_rc_stage_helm.sh create mode 100644 releasey/libs/_gh_rc_stage_source.sh create mode 100644 releasey/libs/_gh_release_copy_dist.sh create mode 100644 releasey/libs/_gh_release_create_github.sh create mode 100644 releasey/libs/_gh_release_email_proposal.sh create mode 100644 releasey/libs/_gh_release_helm.sh create mode 100644 releasey/libs/_gh_release_prepare.sh create mode 100644 releasey/libs/_gh_release_push_git_tag.sh diff --git a/.github/workflows/apprunner-ci.yml b/.github/workflows/apprunner-ci.yml index 225567d2..ab548f62 100644 --- a/.github/workflows/apprunner-ci.yml +++ b/.github/workflows/apprunner-ci.yml @@ -28,11 +28,12 @@ name: Apprunner CI on: push: - branches: [ "main" ] + branches: [ "main", "release/*" ] paths: - 'apprunner/**' - '.github/**' pull_request: + branches: [ "main", "release/*" ] paths: - 'apprunner/**' - '.github/**' diff --git a/.github/workflows/release-1-create-release-branch.yml b/.github/workflows/release-1-create-release-branch.yml index 55a0ba82..a47f492d 100644 --- a/.github/workflows/release-1-create-release-branch.yml +++ b/.github/workflows/release-1-create-release-branch.yml @@ -22,6 +22,10 @@ name: Release - 1 - Create Release Branch on: workflow_dispatch: inputs: + tool: + description: 'The tool directory to release (e.g., apprunner or iceberg-catalog-migrator)' + required: true + type: string version: description: 'Release version without RC number (e.g., 1.0.0-incubating)' required: true @@ -48,9 +52,6 @@ jobs: # 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 @@ -75,22 +76,30 @@ jobs: source "${LIBS_DIR}/_version.sh" # Extract release parameters from workflow inputs + tool="${{ github.event.inputs.tool }}" version="${{ github.event.inputs.version }}" echo "## Parameters" >> $GITHUB_STEP_SUMMARY + if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + # 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 "Tool: \`${tool}\`" >> $GITHUB_STEP_SUMMARY echo "Version: \`${version}\`" >> $GITHUB_STEP_SUMMARY # Create branch name in major.minor.x format - branch_name="${major}.${minor}.x" + branch_name="${tool}-${major}.${minor}.x" # Export parameters for next step + echo "tool=${tool}" >> $GITHUB_ENV echo "version=${version}" >> $GITHUB_ENV echo "branch_name=${branch_name}" >> $GITHUB_ENV diff --git a/.github/workflows/release-2-update-release-candidate.yml b/.github/workflows/release-2-update-release-candidate.yml index ee4bae02..6ceb110d 100644 --- a/.github/workflows/release-2-update-release-candidate.yml +++ b/.github/workflows/release-2-update-release-candidate.yml @@ -44,9 +44,6 @@ jobs: # 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]" @@ -76,13 +73,19 @@ jobs: echo "## Parameters" >> $GITHUB_STEP_SUMMARY # Validate that we're on a release branch - if [[ ! "${current_branch}" =~ ^release/(.+)$ ]]; then + if [[ ! "${current_branch}" =~ ^release/([a-z-]+)-(.+)$ ]]; 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]}" + # Extract tool name and version from release branch name + tool="${BASH_REMATCH[1]}" + branch_version="${BASH_REMATCH[2]}" + + if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + exit 1 + fi # Validate branch version format and extract components if ! validate_and_extract_branch_version "${branch_version}"; then @@ -100,10 +103,11 @@ jobs: find_next_rc_number "${version_without_rc}" # Build the new release tag - release_tag="apache-polaris-${version_without_rc}-rc${rc_number}" + release_tag="apache-polaris-${tool}-${version_without_rc}-rc${rc_number}" # Export all variables for next steps echo "release_tag=${release_tag}" >> $GITHUB_ENV + echo "tool=${tool}" >> $GITHUB_ENV echo "major=${major}" >> $GITHUB_ENV echo "minor=${minor}" >> $GITHUB_ENV echo "patch=${patch}" >> $GITHUB_ENV @@ -114,6 +118,7 @@ jobs: cat <> $GITHUB_STEP_SUMMARY | Parameter | Value | | --- | --- | + | Tool | \`${tool}\` | | Release branch | \`${current_branch}\` | | Version without RC | \`${version_without_rc}\` | | RC number | \`${rc_number}\` | @@ -149,7 +154,9 @@ jobs: run: | source "${LIBS_DIR}/_version.sh" - update_version "${version_without_rc}" + # Call tool-specific version update script + (cd ${tool} ; ./releasey/update_version.sh) + cat <> $GITHUB_STEP_SUMMARY ## Version update All version files updated to \`${version_without_rc}\` @@ -157,8 +164,8 @@ jobs: - name: Update changelog run: | - source "${LIBS_DIR}/_exec.sh" - exec_process ./gradlew patchChangelog + # Call tool-specific update changelog script + (cd ${tool} ; ./releasey/update_changelog.sh) cat <> $GITHUB_STEP_SUMMARY ## Changelog @@ -171,11 +178,6 @@ jobs: 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 diff --git a/.github/workflows/release-3-build-and-publish-artifacts.yml b/.github/workflows/release-3-build-and-publish-artifacts.yml index 0b93100f..f4dda681 100644 --- a/.github/workflows/release-3-build-and-publish-artifacts.yml +++ b/.github/workflows/release-3-build-and-publish-artifacts.yml @@ -36,9 +36,14 @@ jobs: 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 }} + tool: ${{ steps.prepare.outputs.tool }} + git_tag: ${{ steps.prepare.outputs.git_tag }} + version_without_rc: ${{ steps.prepare.outputs.version_without_rc }} + rc_number: ${{ steps.prepare.outputs.rc_number }} + skip_maven: ${{ steps.flavors.outputs.skip_maven }} + skip_docker: ${{ steps.flavors.outputs.skip_docker }} + skip_helm: ${{ steps.flavors.outputs.skip_helm }} + skip_python: ${{ steps.flavors.outputs.skip_python }} steps: - name: Checkout repository @@ -46,9 +51,6 @@ jobs: with: fetch-depth: 0 - - name: Setup test environment - uses: ./.github/actions/setup-test-env - - name: Set up environment variables id: set-outputs run: | @@ -67,78 +69,109 @@ jobs: fi - name: Validate release candidate tag - id: validate-tag - run: | - source "${LIBS_DIR}/_version.sh" + id: prepare + run: source "${LIBS_DIR}/_gh_rc_prepare.sh" - echo "## Parameters" >> $GITHUB_STEP_SUMMARY + - name: Determine flavors to build + id: flavors + run: source "${LIBS_DIR}/_gh_determine_built_flavors.sh" - 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 + build-source-tarball: + name: Build and Stage Source Tarball + runs-on: ubuntu-latest + needs: prerequisite-checks + permissions: + contents: read + env: + DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} + 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 }} - # 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 + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 - # 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 + - name: Set up environment variables + run: | + echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV + echo "LIBS_DIR=$(pwd)/releasey/libs" >> $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 + - name: Extract information from release candidate tag + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" - cat <> $GITHUB_STEP_SUMMARY - | Parameter | Value | - | --- | --- | - | Git tag | \`${git_tag}\` | - | Version | \`${version_without_rc}\` | - | RC number | \`${rc_number}\` | - EOT + - 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 - build-and-publish-artifacts: - name: Build and Publish Release Artifacts + - name: Build and stage source tarball + env: + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: source "${LIBS_DIR}/_gh_rc_stage_source.sh" + + build-and-stage-maven: + name: Build and Publish Maven Artifacts runs-on: ubuntu-latest needs: prerequisite-checks permissions: contents: read env: DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} 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 }} + skip_maven: ${{ needs.prerequisite-checks.outputs.skip_maven }} + outputs: + staging_repo_id: ${{ steps.publish-nexus.publish.staging_repo_id }} + staging_repo_url: ${{ steps.publish-nexus.publish.staging_repo_url }} steps: - name: Checkout repository + if: env.skip_maven != '1' uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - name: Setup test environment - uses: ./.github/actions/setup-test-env - - name: Set up environment variables + if: env.skip_maven != '1' run: | echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV + - name: Extract information from release candidate tag + if: env.skip_maven != '1' + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" + - name: Set up Java + if: env.skip_maven != '1' uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5 with: distribution: 'temurin' java-version: '21' - name: Install Subversion + if: env.skip_maven != '1' run: | sudo apt-get update sudo apt-get install -y subversion - name: Import GPG key + if: env.skip_maven != '1' uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} @@ -146,146 +179,143 @@ jobs: git_user_signingkey: true git_commit_gpgsign: true - - name: Build source and binary distributions + - name: Assemble artifacts + if: env.skip_maven != '1' run: | source "${LIBS_DIR}/_exec.sh" - exec_process ./gradlew assemble sourceTarball -Prelease -PuseGpgAgent + # Call tool-specific maven-artifacts script + (cd ${tool} ; ./releasey/maven-build.sh) cat <> $GITHUB_STEP_SUMMARY ## Build - Source and binary distributions built successfully + Maven artifacts built successfully EOT - name: Stage artifacts to Apache dist dev repository + if: env.skip_maven != '1' 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 <> $GITHUB_STEP_SUMMARY - ## Staging to dist dev - Artifacts staged to Apache dist dev repository - EOT + source "${LIBS_DIR}/_gh_rc_stage_dist_artifacts.sh" - name: Publish and close Apache Nexus staging repository + id: publish + if: env.skip_maven != '1' env: ORG_GRADLE_PROJECT_apacheUsername: ${{ secrets.APACHE_USERNAME }} ORG_GRADLE_PROJECT_apachePassword: ${{ secrets.APACHE_PASSWORD }} - run: | - source "${LIBS_DIR}/_exec.sh" + run: source "${LIBS_DIR}/_gh_rc_publish_nexus.sh" - # Publish artifacts to staging repository - exec_process ./gradlew publishToApache closeApacheStagingRepository -Prelease -PuseGpgAgent --info 2>&1 | tee gradle_publish_output.txt + build-and-stage-python: + name: Build and Stage Python Packages + runs-on: ubuntu-latest + needs: prerequisite-checks + permissions: + contents: read + env: + DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} + 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 }} + skip_python: ${{ needs.prerequisite-checks.outputs.skip_python }} - # Extract staging repository ID and URL from Gradle output - staging_repo_id="" - staging_repo_url="" + steps: + - name: Checkout repository + if: env.skip_python != '1' + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 - # 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 + - name: Set up environment variables + if: env.skip_python != '1' + run: | + echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV + echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV - cat <> $GITHUB_STEP_SUMMARY - ## Nexus Staging Repository - Artifacts published and staging repository closed successfully + - name: Extract information from release candidate tag + if: env.skip_python != '1' + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" - | Property | Value | - | --- | --- | - | Staging Repository ID | \`${staging_repo_id:-"Not extracted"}\` | - | Staging Repository URL | ${staging_repo_url:-"Not extracted"} | + - name: Install Subversion + if: env.skip_python != '1' + run: | + sudo apt-get update + sudo apt-get install -y subversion + + - name: Import GPG key + if: env.skip_python != '1' + 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 - ## Summary - ๐ŸŽ‰ Artifacts built and published successfully: + - name: Build Python Packages + if: env.skip_python != '1' + run: | + # Call tool-specific build script + (cd ${tool} ; ./releasey/python-build.sh) - | 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 + - name: Stage artifacts to Apache dist dev repository + if: env.skip_python != '1' + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + echo "::add-mask::$SVN_PASSWORD" + source "${LIBS_DIR}/_gh_rc_stage_dist_artifacts.sh" build-docker: name: Build Docker Images runs-on: ubuntu-latest - needs: [prerequisite-checks, build-and-publish-artifacts] + needs: + - prerequisite-checks + - build-and-stage-maven + - build-and-stage-python permissions: contents: read env: DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} 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 }} + skip_docker: ${{ needs.prerequisite-checks.outputs.skip_docker }} steps: - name: Checkout repository + if: env.skip_docker != '1' uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - name: Setup test environment - uses: ./.github/actions/setup-test-env - - name: Set up environment variables + if: env.skip_docker != '1' 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: Extract information from release candidate tag + if: env.skip_docker != '1' + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" - name: Set up Java + if: env.skip_docker != '1' uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5 with: distribution: 'temurin' java-version: '21' - - name: Build Polaris Server Docker image + - name: Build and Push Polaris Server Docker images + if: env.skip_docker != '1' 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}" + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/docker-build.sh) echo "## Docker Images Summary" >> $GITHUB_STEP_SUMMARY cat <> $GITHUB_STEP_SUMMARY @@ -300,40 +330,50 @@ jobs: build-and-stage-helm-chart: name: Build and Stage Helm Chart runs-on: ubuntu-latest - needs: [prerequisite-checks, build-docker] + needs: + - prerequisite-checks + - build-docker permissions: contents: read env: DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} 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 }} + skip_helm: ${{ needs.prerequisite-checks.outputs.skip_helm }} steps: - name: Checkout repository + if: env.skip_helm != '1' uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 0 - - name: Setup test environment - uses: ./.github/actions/setup-test-env - - name: Set up environment variables + if: env.skip_helm != '1' run: | echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV + - name: Extract information from release candidate tag + if: env.skip_helm != '1' + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" + - name: Set up Helm + if: env.skip_helm != '1' uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: 'latest' - name: Install Subversion + if: env.skip_helm != '1' run: | sudo apt-get update sudo apt-get install -y subversion - name: Import GPG key + if: env.skip_helm != '1' uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} @@ -341,63 +381,44 @@ jobs: git_user_signingkey: true git_commit_gpgsign: true - - name: Create Helm package + - name: Build Helm Charts + if: env.skip_helm != '1' 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 + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/helm-build.sh) - 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 - # Prerequisite for reproducible helm packages: file modification time must be deterministic - # Works with helm since version 4.0.0 - exec_process find polaris -exec touch -d "1980-01-01 00:00:00" {} + - 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 + - name: Stage Helm Charts + if: env.skip_helm != '1' 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}/" + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/helm-stage.sh) - 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 <> $GITHUB_STEP_SUMMARY - ๐ŸŽ‰ Helm chart built and staged successfully: - - | Component | Status | - | --- | --- | - | Helm package | โœ… Created and signed | - | Apache dist dev repository | โœ… Staged | - EOT + finish: + name: Build and Publish Release Artifacts Finished + runs-on: ubuntu-latest + needs: + - prerequisite-checks + - build-source-tarball + - build-and-stage-maven + - build-and-stage-python + - build-docker + - build-and-stage-helm-chart + permissions: + contents: read + env: + DRY_RUN: ${{ needs.prerequisite-checks.outputs.dry_run }} + tool: ${{ needs.prerequisite-checks.outputs.tool }} + 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 }} + staging_repo_id: ${{ needs.build-and-stage-maven.outputs.staging_repo_id }} + staging_repo_url: ${{ needs.build-and-stage-maven.outputs.staging_repo_url }} + steps: + - name: Summary + run: source "${LIBS_DIR}/_gh_rc_email_proposal.sh" diff --git a/.github/workflows/release-4-publish-release.yml b/.github/workflows/release-4-publish-release.yml index 879c7643..3b173105 100644 --- a/.github/workflows/release-4-publish-release.yml +++ b/.github/workflows/release-4-publish-release.yml @@ -28,8 +28,8 @@ on: type: boolean default: true staging_repository_id: - description: 'Nexus staging repository ID to release (e.g., orgapachepolaris-1234)' - required: true + description: 'Nexus staging repository ID to release (e.g., orgapachepolaris-1234), required for tools that publish Maven artifacts' + required: false type: string jobs: @@ -48,9 +48,6 @@ jobs: # 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]" @@ -76,7 +73,7 @@ jobs: echo "โŒ Staging repository ID is required but not provided." >> $GITHUB_STEP_SUMMARY exit 1 fi - echo "STAGING_REPOSITORY_ID=${staging_repo_id}" >> $GITHUB_ENV + echo "staging_repo_id=${staging_repo_id}" >> $GITHUB_ENV - name: Install Subversion run: | @@ -88,162 +85,38 @@ jobs: 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 + - name: Check necessity of publishing artifacts + run: source "${LIBS_DIR}/_gh_determine_built_flavors.sh" - # 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 <> $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: Auto-determine release parameters from branch and Git state + run: source "${LIBS_DIR}/_gh_release_prepare.sh" - 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 <> $GITHUB_STEP_SUMMARY - ## Distribution - Artifacts and Helm chart moved from dist dev to dist release - EOT + run: source "${LIBS_DIR}/_gh_release_copy_dist.sh" - name: Set up Helm + if: env.skip_helm != '1' uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 with: version: 'latest' - name: Update Helm index in release space + if: env.skip_helm != '1' 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 <> $GITHUB_STEP_SUMMARY - ## Helm Index - Helm index updated in dist release - EOT + source "${LIBS_DIR}/_gh_release_helm.sh" - 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 <> $GITHUB_STEP_SUMMARY - ## Git Release Tag - Final release tag \`${final_release_tag}\` created and pushed - EOT + run: source "${LIBS_DIR}/_gh_release_push_git_tag.sh" - name: Set up Docker Buildx + if: env.skip_docker != '1' run: | docker buildx use default docker buildx create \ @@ -260,108 +133,54 @@ jobs: java-version: '21' - name: Log in to Docker Hub - if: env.DRY_RUN == '0' + if: ${{ env.DRY_RUN == '0' && env.skip_docker != '1' }} 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 + - name: Publish Docker images to Docker Hub + if: env.skip_docker != '1' 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}" + # Call tool-specific docker-artifacts script + (cd ${tool} ; ./releasey/docker-release.sh) cat <> $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 }} + - name: Release to PyPI + if: ${{ env.skip_python != '1' }} 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 <> $GITHUB_STEP_SUMMARY - ## GitHub Release - GitHub release created: \`${final_release_tag}\` - EOT + # Call tool-specific maven-artifacts script + (cd ${tool} ; ./releasey/python-release.sh) - name: Release candidate repository on Nexus + if: ${{ env.skip_maven != '1' }} 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}" + # Call tool-specific maven-artifacts script + (cd ${tool} ; ./releasey/maven-release.sh) + + - name: GitHub Release + 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}/_gh_release_create_github.sh" + - name: Summary + run: | cat <> $GITHUB_STEP_SUMMARY ## Summary ๐ŸŽ‰ Release published successfully: @@ -377,3 +196,6 @@ jobs: | Version | \`${version_without_rc}\` | | Final release tag | \`${final_release_tag}\` | EOT + + source "${LIBS_DIR}/_gh_rc_email_proposal.sh" + diff --git a/apprunner/releasey/maven-build.sh b/apprunner/releasey/maven-build.sh new file mode 100644 index 00000000..97a962db --- /dev/null +++ b/apprunner/releasey/maven-build.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Script to build Maven artifacts and the source tarball. +# +# Environment variables: +# DRY_RUN +# RELEASEY_DIR +# LIBS_DIR +# version_without_rc +# version_dir Directory for additional binary artifacts +# (and more) + +source "$LIBS_DIR/_exec.sh" + +exec_process ./gradlew assemble -Prelease -PuseGpgAgent diff --git a/apprunner/releasey/maven-release.sh b/apprunner/releasey/maven-release.sh new file mode 100644 index 00000000..a1079d34 --- /dev/null +++ b/apprunner/releasey/maven-release.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Script to release Maven artifacts. +# +# Environment variables: +# DRY_RUN +# RELEASEY_DIR +# LIBS_DIR +# version_without_rc +# (and more) + +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_repo_id}" diff --git a/apprunner/releasey/maven-stage.sh b/apprunner/releasey/maven-stage.sh new file mode 100644 index 00000000..bfbcdf0a --- /dev/null +++ b/apprunner/releasey/maven-stage.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Script to publish Maven artifacts. +# +# Arguments +# - file name to write the staging repo ID and URL to +# Environment variables: +# DRY_RUN +# RELEASEY_DIR +# LIBS_DIR +# version_without_rc +# (and more) + +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 + +out_file="$1" + +( echo "staging_repo_id=${staging_repo_id}" + echo "staging_repo_url=${staging_repo_url}" +) >> "${out_file}" + diff --git a/apprunner/releasey/update_changelog.sh b/apprunner/releasey/update_changelog.sh new file mode 100644 index 00000000..6c4f2065 --- /dev/null +++ b/apprunner/releasey/update_changelog.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Script to update the changelog. +# +# Every tool MUST have an update_changelog.sh script. +# +# Arguments: +# version +# Environment variables: +# DRY_RUN +# RELEASEY_DIR +# LIBS_DIR +# version_without_rc +# (and more) + +source "$LIBS_DIR/_exec.sh" + +exec_process ./gradlew patchChangelog + +exec_process git add CHANGELOG.md diff --git a/apprunner/releasey/update_version.sh b/apprunner/releasey/update_version.sh new file mode 100644 index 00000000..e27184ad --- /dev/null +++ b/apprunner/releasey/update_version.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Script to update the version. +# +# Every tool MUST have an update_version.sh script. +# +# Environment variables: +# DRY_RUN +# RELEASEY_DIR +# LIBS_DIR +# version_without_rc +# (and more) + +source "$LIBS_DIR/_exec.sh" + +if [[ ${DRY_RUN:-1} -ne 1 ]]; then + exec_process echo "$version_without_rc" > version.txt +else + exec_process "echo $version_without_rc > version.txt" +fi + +exec_process git add version.txt + "$VERSION_FILE" \ + "$HELM_CHART_YAML_FILE" \ + "$HELM_README_FILE" \ + "$CHANGELOG_FILE" diff --git a/releasey/libs/_constants.sh b/releasey/libs/_constants.sh index a59175ac..02302c19 100644 --- a/releasey/libs/_constants.sh +++ b/releasey/libs/_constants.sh @@ -29,12 +29,6 @@ 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]+)$" +VERSION_REGEX_GIT_TAG="^apache-polaris-([a-z+]+)-$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/_gh_determine_built_flavors.sh b/releasey/libs/_gh_determine_built_flavors.sh new file mode 100644 index 00000000..b4e4062d --- /dev/null +++ b/releasey/libs/_gh_determine_built_flavors.sh @@ -0,0 +1,52 @@ +# +# 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. +# + +skip_maven=0 +skip_docker=0 +skip_helm=0 +skip_python=0 + +if [[ ! -x "${tool}/releasey/maven-build.sh" ]]; then + skip_maven=1 + echo "## No Maven Artifacts built by ${tool}" >> $GITHUB_STEP_SUMMARY +fi +if [[ ! -x "${tool}/releasey/docker-build.sh" ]]; then + skip_docker=1 + echo "## No Docker Images built by ${tool}" >> $GITHUB_STEP_SUMMARY +fi +if [[ ! -x "${tool}/releasey/helm-build.sh" ]]; then + skip_helm=1 + echo "## No Helm Charts built by ${tool}" >> $GITHUB_STEP_SUMMARY +fi +if [[ ! -x "${tool}/releasey/python-build.sh" ]]; then + skip_python=1 + echo "## No Python packages built by ${tool}" >> $GITHUB_STEP_SUMMARY +fi + +( echo "skip_maven=$skip_maven" + echo "skip_docker=$skip_docker" + echo "skip_helm=$skip_helm" + echo "skip_python=$skip_python" +) >> $GITHUB_ENV + +( echo "skip_maven=$skip_maven" + echo "skip_docker=$skip_docker" + echo "skip_helm=$skip_helm" + echo "skip_python=$skip_python" +) >> $GITHUB_OUTPUT diff --git a/releasey/libs/_gh_rc_build_helm.sh b/releasey/libs/_gh_rc_build_helm.sh new file mode 100644 index 00000000..a2eda6cf --- /dev/null +++ b/releasey/libs/_gh_rc_build_helm.sh @@ -0,0 +1,47 @@ +# +# 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. +# + +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 + +find . -mindepth 1 -maxdepth 1 -type d | while read -r helm_dir; do + chart_name="$(basename "$helm_dir")" + exec_process cd "${chart_name}" + + # Prerequisite for reproducible helm packages: file modification time must be deterministic + # Works with helm since version 4.0.0 + exec_process find "${chart_name}" -exec touch -d "1980-01-01 00:00:00" {} + + exec_process helm package "${chart_name}" --sign --key "." --keyring /tmp/secring.gpg --passphrase-file /tmp/passphrase + exec_process helm verify "${chart_name}"-${version_without_rc}.tgz --keyring /tmp/pubring.gpg + + calculate_sha512 polaris-${version_without_rc}.tgz + exec_process gpg --armor --output "${chart_name}"-${version_without_rc}.tgz.asc --detach-sig "${chart_name}"-${version_without_rc}.tgz + calculate_sha512 polaris-${version_without_rc}.tgz.prov + exec_process gpg --armor --output "${chart_name}"-${version_without_rc}.tgz.prov.asc --detach-sig "${chart_name}"-${version_without_rc}.tgz.prov + + exec_process cd .. +done diff --git a/releasey/libs/_gh_rc_build_prepare.sh b/releasey/libs/_gh_rc_build_prepare.sh new file mode 100644 index 00000000..9bf7369a --- /dev/null +++ b/releasey/libs/_gh_rc_build_prepare.sh @@ -0,0 +1,47 @@ +# +# 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. +# + +source "${LIBS_DIR}/_version.sh" + +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 + +if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + exit 1 +fi + +dist_dev_dir="${RELEASEY_DIR}/polaris-tools-dist-dev" + +# Export variables for next steps and job outputs +( echo "tool=${tool}" + echo "git_tag=${git_tag}" + echo "version_without_rc=${version_without_rc}" + echo "rc_number=${rc_number}" + echo "dist_dev_dir=${dist_dev_dir}" + echo "version_dir=${dist_dev_dir}/${version_without_rc}" +) >> $GITHUB_ENV diff --git a/releasey/libs/_gh_rc_email_proposal.sh b/releasey/libs/_gh_rc_email_proposal.sh new file mode 100644 index 00000000..0a0c7166 --- /dev/null +++ b/releasey/libs/_gh_rc_email_proposal.sh @@ -0,0 +1,95 @@ +# +# 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. +# + +# The ^0 suffix "resolves" a Git tag SHA to a commit SHA, if necessary. +git_commit="$(git rev-parse ${git_tag}^0)" + +cat <> $GITHUB_STEP_SUMMARY + +## \`VOTE\` email proposal + +Subject: + +\`\`\` +[VOTE] Release Apache Polaris ${tool} ${version_without_rc} (rc${rc_number}) +\`\`\` + +Message body proposal, read carefully and adapt if necessary: + +\`\`\` +Hi everyone, + +I propose that we release the following RC${rc_number} as the official +Apache Polaris ${tool} ${version_without_rc} release. + +* This corresponds to the tag: ${git_tag} +* https://github.com/apache/polaris-tools/commits/${git_tag} +* https://github.com/apache/polaris-tools/tree/${git_commit} + +The release tarball, signature, and checksums are here: +* https://dist.apache.org/repos/dist/dev/incubator/polaris-tools/${tool}/${version_without_rc} +EOT + +if [[ ${skip_maven} != 1 ]]; then +cat <> $GITHUB_STEP_SUMMARY +Convenience binary artifacts are staged on Nexus. The Maven repositories URLs are: +* https://repository.apache.org/content/repositories/${staging_repo_id}/ +EOT +fi + +if [[ ${skip_python} != 1 ]]; then +cat <> $GITHUB_STEP_SUMMARY +## TODO ADD PYTHON +EOT +fi + +if [[ ${skip_docker} != 1 ]]; then +cat <> $GITHUB_STEP_SUMMARY +## TODO ADD DOCKER +EOT +fi + +if [[ ${skip_helm} != 1 ]]; then + cat <> $GITHUB_STEP_SUMMARY +Helm charts are available on: +* https://dist.apache.org/repos/dist/dev/incubator/polaris-tools/${tool}/$version_without_rc}/helm-charts +EOT +fi + +cat <> $GITHUB_STEP_SUMMARY +You can find the KEYS file here: +* https://downloads.apache.org/incubator/polaris-tools/KEYS + +Please download, verify, and test. + +Please vote in the next 72 hours. + +[ ] +1 Release this as Apache Polaris ${tool} ${version_without_rc} +[ ] +0 +[ ] -1 Do not release this because... + +Only PPMC members and mentors have binding votes, but other community +members are encouraged to cast non-binding votes. This vote will pass if +there are +3 binding +1 votes and more binding +1 votes than -1 votes. + +Best, +YOUR_NAME_HERE +\`\`\` +EOT diff --git a/releasey/libs/_gh_rc_prepare.sh b/releasey/libs/_gh_rc_prepare.sh new file mode 100644 index 00000000..8679b299 --- /dev/null +++ b/releasey/libs/_gh_rc_prepare.sh @@ -0,0 +1,62 @@ +# +# 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. +# + +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 + +if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + exit 1 +fi + +source "${LIBS_DIR}/_gh_determine_built_artifacts.sh" + +# Export variables for next steps and job outputs +( echo "tool=${tool}" + echo "git_tag=${git_tag}" + echo "version_without_rc=${version_without_rc}" + echo "rc_number=${rc_number}" +) >> $GITHUB_ENV + +( echo "tool=${tool}" + echo "git_tag=${git_tag}" + echo "version_without_rc=${version_without_rc}" + echo "rc_number=${rc_number}" +) >> $GITHUB_OUTPUT + +cat <> $GITHUB_STEP_SUMMARY +| Parameter | Value | +| --- | --- | +| Tool | \`${tool}\` | +| Git tag | \`${git_tag}\` | +| Version | \`${version_without_rc}\` | +| RC number | \`${rc_number}\` | +EOT diff --git a/releasey/libs/_gh_rc_publish_nexus.sh b/releasey/libs/_gh_rc_publish_nexus.sh new file mode 100644 index 00000000..016234dc --- /dev/null +++ b/releasey/libs/_gh_rc_publish_nexus.sh @@ -0,0 +1,50 @@ +# +# 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. +# + +source "${LIBS_DIR}/_exec.sh" + +out_params_file="$(mktemp --tmpdir maven-stage-XXXXXXXXXX)" + +# Call tool-specific maven-stage script +(cd ${tool} ; ./releasey/maven-stage.sh "$out_params_file") + +source "$out_params_file" + +echo "staging_repo_id=${staging_repo_id}" >> $GITHUB_OUTPUT +echo "staging_repo_url=${staging_repo_url}" >> $GITHUB_OUTPUT + +cat <> $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 diff --git a/releasey/libs/_gh_rc_stage_dist_artifacts.sh b/releasey/libs/_gh_rc_stage_dist_artifacts.sh new file mode 100644 index 00000000..fbdbfb5f --- /dev/null +++ b/releasey/libs/_gh_rc_stage_dist_artifacts.sh @@ -0,0 +1,47 @@ +# +# 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. +# + +if [[ -d "${tool}/build/distributions" ]]; then + source "${LIBS_DIR}/_constants.sh" + source "${LIBS_DIR}/_exec.sh" + + dist_dev_dir="${RELEASEY_DIR}/polaris-dist-dev/${tool}" + + exec_process svn checkout --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive "${APACHE_DIST_URL}${APACHE_DIST_PATH}" "${dist_dev_dir}" + + version_dir="${tool}/${dist_dev_dir}/${version_without_rc}" + exec_process mkdir -p "${version_dir}" + exec_process cp ${tool}/build/distributions/* "${version_dir}/" + + exec_process cd "${dist_dev_dir}" + exec_process svn add "${tool}/${version_without_rc}" + exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris ${tool} ${version_without_rc} RC${rc_number}" + + cat <> $GITHUB_STEP_SUMMARY + ## Staging to dist dev + Artifacts staged to Apache dist dev repository +EOT + +else + + cat <> $GITHUB_STEP_SUMMARY + ## No artifacts staged to dist dev +EOT + +fi diff --git a/releasey/libs/_gh_rc_stage_helm.sh b/releasey/libs/_gh_rc_stage_helm.sh new file mode 100644 index 00000000..f30c9afa --- /dev/null +++ b/releasey/libs/_gh_rc_stage_helm.sh @@ -0,0 +1,55 @@ +# +# 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. +# + +source "${LIBS_DIR}/_constants.sh" +source "${LIBS_DIR}/_exec.sh" + +dist_dev_dir=${RELEASEY_DIR}/polaris-tools-dist-dev +helm_base_dir="${dist_dev_dir}/${version_without_rc}/helm-charts" +exec_process svn checkout --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive "${APACHE_DIST_URL}${APACHE_DIST_PATH}" "${dist_dev_dir}" + +exec_process cd helm + +find . -mindepth 1 -maxdepth 1 -type d | while read -r helm_dir; do + chart_name="$(basename "$helm_dir")" + exec_process cd "${chart_name}" + + exec_process mkdir -p "${helm_base_dir}/${chart_name}" + exec_process cp helm/polaris-${version_without_rc}.tgz* "${helm_base_dir}/${chart_name}" + + exec_process cd "${helm_base_dir}/${chart_name}" + exec_process helm repo index . + + exec_process cd .. +done + +exec_process cd "${dist_dev_dir}" +exec_process svn add "${version_without_rc}/helm-charts" + +exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris ${tool} Helm chart(s) ${version_without_rc} RC${rc_number}" + +echo "## Helm Chart Summary" >> $GITHUB_STEP_SUMMARY +cat <> $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/releasey/libs/_gh_rc_stage_source.sh b/releasey/libs/_gh_rc_stage_source.sh new file mode 100644 index 00000000..118f7fcf --- /dev/null +++ b/releasey/libs/_gh_rc_stage_source.sh @@ -0,0 +1,54 @@ +# +# 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. +# + +source "${LIBS_DIR}/_constants.sh" +source "${LIBS_DIR}/_exec.sh" + +dist_dev_dir="${RELEASEY_DIR}/polaris-dist-dev" +version_dir="${dist_dev_dir}/${version_without_rc}" +tool_base_name="apache-polaris-${tool}" + +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 "${version_dir}" + +git archive \ + --prefix="${tool_base_name}/" \ + --format=tar \ + --mtime="1980-02-01 00:00:00" \ + HEAD | gzip -6 --no-name > "${version_dir}/${tool_base_name}.tar.gz" + +exec_process gpg \ + --sign \ + --armor \ + --passphrase "${GPG_PASSPHRASE}" \ + "${version_dir}/${tool_base_name}.tar.gz" +exec_process cd "${version_dir}" +exec_process md5sum -b "${tool_base_name}.tar.gz" > "${tool_base_name}.tar.gz.md5" +exec_process shasum -b -a 256 "${tool_base_name}.tar.gz" > "${tool_base_name}.tar.gz.sha256" +exec_process shasum -b -a 512 "${tool_base_name}.tar.gz" > "${tool_base_name}.tar.gz.sha512" + +exec_process cd "${dist_dev_dir}" +exec_process svn add "${tool}/${version_without_rc}/${tool_base_name}.tar.gz*" +exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage source tarball of Apache Polaris ${tool} ${version_without_rc} RC${rc_number}" + +cat <> $GITHUB_STEP_SUMMARY + ## Staging source tarball to dist dev + Source tarball staged to Apache dist dev repository +EOT diff --git a/releasey/libs/_gh_release_copy_dist.sh b/releasey/libs/_gh_release_copy_dist.sh new file mode 100644 index 00000000..94397aab --- /dev/null +++ b/releasey/libs/_gh_release_copy_dist.sh @@ -0,0 +1,36 @@ +# +# 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. +# + +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-${tool}/${version_without_rc}" +release_artifacts_url="${APACHE_DIST_URL}/release/incubator/polaris-${tool}/${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 ${tool} ${version_without_rc}" + +cat <> $GITHUB_STEP_SUMMARY +## Distribution +Artifacts and Helm chart moved from dist dev to dist release +EOT diff --git a/releasey/libs/_gh_release_create_github.sh b/releasey/libs/_gh_release_create_github.sh new file mode 100644 index 00000000..ef82f2d0 --- /dev/null +++ b/releasey/libs/_gh_release_create_github.sh @@ -0,0 +1,83 @@ +# +# 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. +# + +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-${tool}/${version_without_rc}" +exec_process svn export --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive \ + "${release_artifacts_url}" "${artifacts_dir}/artifacts" + +# 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" + +if [[ -n "${skip_helm}" ]]; then + release_notes="${release_notes} +- Helm chart package" +fi +if [[ -n "${skip_docker}" ]]; then + release_notes="${release_notes} +- Docker images published to Docker Hub" +fi +if [[ -n "${skip_maven}" ]]; then + release_notes="${release_notes} +- Maven artifacts published to Maven Central" +fi + +## Verification +release_notes="${release_notes} +All artifacts have been signed with GPG and include SHA-512 checksums for verification." + +if [[ -n "${skip_docker}" ]]; then +release_notes="${release_notes} +## Docker Images +- \`apache/polaris:${final_release_tag}\` - Polaris Server +- \`apache/polaris-admin:${final_release_tag}\` - Polaris Admin Tool" +fi +# ******************************************************************************** + +# 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 +artifacts_dir="${RELEASEY_DIR}/release-artifacts" +if [[ -d "${artifacts_dir}" ]]; then + 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 +fi + +cat <> $GITHUB_STEP_SUMMARY +## GitHub Release +GitHub release created: \`${final_release_tag}\` +EOT diff --git a/releasey/libs/_gh_release_email_proposal.sh b/releasey/libs/_gh_release_email_proposal.sh new file mode 100644 index 00000000..3d998c94 --- /dev/null +++ b/releasey/libs/_gh_release_email_proposal.sh @@ -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. +# + +# The ^0 suffix "resolves" a Git tag SHA to a commit SHA, if necessary. +git_commit="$(git rev-parse ${git_tag}^0)" + +cat <> $GITHUB_STEP_SUMMARY + +## \`[ANNOUNCE]\` email proposal + +Subject: + +\`\`\` +[ANNOUNCE] Apache Polaris ${tool} ${version_without_rc} has been released! +\`\`\` + +Message body proposal, read carefully and adapt if necessary: + +\`\`\` +The Apache Polaris team is pleased to announce Apache Polaris ${tool} ${version_without_rc}. + +This release includes: +## TODO ADD CHANGELOG + +This release can be downloaded: +* https://polaris.apache.org/downloads/ + +The artifacts are available on Maven Central: +* https://repo1.maven.org/maven2/org/apache/polaris/ + +The Docker images are available on Docker Hub: +* https://hub.docker.com/r/apache/polaris-${tool}/tags + +Apache Polaris is an open-source, fully-featured catalog for Apache +Icebergโ„ข. It implements Iceberg's REST API, enabling seamless +multi-engine interoperability across a wide range of platforms, +including Apache Dorisโ„ข, Apache Flinkยฎ, Apache Sparkโ„ข, Dremioยฎ OSS, +StarRocks, and Trino. + +Enjoy ! + +The Apache Polaris team. + +\`\`\` +EOT diff --git a/releasey/libs/_gh_release_helm.sh b/releasey/libs/_gh_release_helm.sh new file mode 100644 index 00000000..66b00af6 --- /dev/null +++ b/releasey/libs/_gh_release_helm.sh @@ -0,0 +1,46 @@ +# +# 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. +# + +source "${LIBS_DIR}/_constants.sh" +source "${LIBS_DIR}/_exec.sh" + +# Checkout the release Helm chart directory +release_artifacts_url="${APACHE_DIST_URL}/release/incubator/polaris-${tool}/${version_without_rc}" +release_helm_dir="${RELEASEY_DIR}/polaris-dist-release-helm-chart" +release_helm_url="${release_artifacts_url}/helm-charts" + +exec_process svn checkout --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive "${release_helm_url}" "${release_helm_dir}" + +exec_process cd "${release_helm_dir}" +find . -mindepth 1 -maxdepth 1 -type d | while read -r helm_dir; do + chart_name="$(basename "$helm_dir")" + exec_process cd "${chart_name}" + + exec_process helm repo index . + exec_process svn add index.yaml + + exec_process cd "${..}" +done + +exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Update Helm index for ${tool} ${version_without_rc} release" + +cat <> $GITHUB_STEP_SUMMARY +## Helm Index +Helm index updated in dist release +EOT diff --git a/releasey/libs/_gh_release_prepare.sh b/releasey/libs/_gh_release_prepare.sh new file mode 100644 index 00000000..9762795e --- /dev/null +++ b/releasey/libs/_gh_release_prepare.sh @@ -0,0 +1,102 @@ +# +# 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. +# + +source "${LIBS_DIR}/_version.sh" + +# Get the current branch name +current_branch=$(git branch --show-current) + +echo "## Parameters" >> $GITHUB_STEP_SUMMARY + +if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + exit 1 +fi + +# 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-${tool}-${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-${tool}-${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}" + echo "tool=${tool}" + echo "rc_tag=${rc_tag}" + echo "final_release_tag=${final_release_tag}" + echo "release_branch=${current_branch}" +) >> $GITHUB_ENV + +cat <> $GITHUB_STEP_SUMMARY +| Parameter | Value | +| --- | --- | +| Tool | \`${tool}\` | +| Version | \`${version_without_rc}\` | +| RC tag to promote | \`${rc_tag}\` | +| Final release tag | \`${final_release_tag}\` | +| Release branch | \`${current_branch}\` | +EOT diff --git a/releasey/libs/_gh_release_push_git_tag.sh b/releasey/libs/_gh_release_push_git_tag.sh new file mode 100644 index 00000000..fbf74eb1 --- /dev/null +++ b/releasey/libs/_gh_release_push_git_tag.sh @@ -0,0 +1,32 @@ +# +# 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. +# + +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 <> $GITHUB_STEP_SUMMARY +## Git Release Tag +Final release tag \`${final_release_tag}\` created and pushed +EOT diff --git a/releasey/libs/_version.sh b/releasey/libs/_version.sh index 7d812f13..af1b5a71 100644 --- a/releasey/libs/_version.sh +++ b/releasey/libs/_version.sh @@ -58,10 +58,11 @@ function validate_and_extract_git_tag_version { return 1 fi - major="${BASH_REMATCH[1]}" - minor="${BASH_REMATCH[2]}" - patch="${BASH_REMATCH[3]}" - rc_number="${BASH_REMATCH[4]}" + tool="${BASH_REMATCH[1]}" + major="${BASH_REMATCH[2]}" + minor="${BASH_REMATCH[3]}" + patch="${BASH_REMATCH[4]}" + rc_number="${BASH_REMATCH[5]}" version_without_rc="${major}.${minor}.${patch}-incubating" return 0 @@ -78,46 +79,15 @@ function validate_and_extract_polaris_version { return 1 fi - major="${BASH_REMATCH[1]}" - minor="${BASH_REMATCH[2]}" - patch="${BASH_REMATCH[3]}" + tool="${BASH_REMATCH[1]}" + major="${BASH_REMATCH[2]}" + minor="${BASH_REMATCH[3]}" + patch="${BASH_REMATCH[4]}" 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. @@ -135,7 +105,7 @@ function find_next_rc_number { 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) + highest_rc=$(echo "${existing_tags}" | sed "s/apache-polaris-${tool}-${version_without_rc}-rc//" | sort -n | tail -1) rc_number=$((highest_rc + 1)) fi @@ -150,7 +120,7 @@ function find_next_patch_number { local minor="$2" # Get all existing tags for this major.minor version - local tag_pattern="apache-polaris-${major}.${minor}.*-incubating-rc*" + local tag_pattern="apache-polaris-${tool}-${major}.${minor}.*-incubating-rc*" local existing_tags existing_tags=$(git tag -l "${tag_pattern}" | sort -V) @@ -161,7 +131,7 @@ function find_next_patch_number { # 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 + if [[ ${tag} =~ apache-polaris-${tool}-${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} From 40582c072206783af1a906db6d671eb97cc72711 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Sat, 20 Dec 2025 13:38:49 +0100 Subject: [PATCH 3/4] update from apache/polaris --- .../release-2-update-release-candidate.yml | 13 ++++-- .../release-3-build-and-publish-artifacts.yml | 2 + .../workflows/release-4-publish-release.yml | 2 + releasey/libs/_gh_rc_prepare.sh | 13 +++++- releasey/libs/_gh_release_prepare.sh | 46 +++++++++++++------ releasey/libs/_version.sh | 33 ++++++++----- 6 files changed, 77 insertions(+), 32 deletions(-) diff --git a/.github/workflows/release-2-update-release-candidate.yml b/.github/workflows/release-2-update-release-candidate.yml index 6ceb110d..2d38c381 100644 --- a/.github/workflows/release-2-update-release-candidate.yml +++ b/.github/workflows/release-2-update-release-candidate.yml @@ -151,6 +151,7 @@ jobs: java-version: '21' - name: Update project versions + if: env.rc_number == '0' run: | source "${LIBS_DIR}/_version.sh" @@ -163,6 +164,7 @@ jobs: EOT - name: Update changelog + if: env.rc_number == '0' run: | # Call tool-specific update changelog script (cd ${tool} ; ./releasey/update_changelog.sh) @@ -173,24 +175,25 @@ jobs: EOT - name: Commit and push changes + if: env.rc_number == '0' run: | source "${LIBS_DIR}/_constants.sh" source "${LIBS_DIR}/_exec.sh" # Commit version files and changelog - exec_process git commit -m "[chore] Bump version to ${version_without_rc} for release candidate ${rc_number}" + exec_process git commit -m "[chore] Bump version to ${version_without_rc}" # 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" + # Get the new commit SHA after our changes + new_tag_ref=$(git rev-parse HEAD) + echo "new_tag_ref=${new_tag_ref}" >> $GITHUB_ENV + # Create the tag at the new commit exec_process git tag "${release_tag}" "${new_tag_ref}" exec_process git push origin "${release_tag}" diff --git a/.github/workflows/release-3-build-and-publish-artifacts.yml b/.github/workflows/release-3-build-and-publish-artifacts.yml index f4dda681..fd495a8e 100644 --- a/.github/workflows/release-3-build-and-publish-artifacts.yml +++ b/.github/workflows/release-3-build-and-publish-artifacts.yml @@ -70,6 +70,8 @@ jobs: - name: Validate release candidate tag id: prepare + env: + git_ref: ${{ github.ref }} run: source "${LIBS_DIR}/_gh_rc_prepare.sh" - name: Determine flavors to build diff --git a/.github/workflows/release-4-publish-release.yml b/.github/workflows/release-4-publish-release.yml index 3b173105..ef2518f3 100644 --- a/.github/workflows/release-4-publish-release.yml +++ b/.github/workflows/release-4-publish-release.yml @@ -89,6 +89,8 @@ jobs: run: source "${LIBS_DIR}/_gh_determine_built_flavors.sh" - name: Auto-determine release parameters from branch and Git state + env: + git_ref: ${{ github.ref }} run: source "${LIBS_DIR}/_gh_release_prepare.sh" - name: Copy distribution from SVN dev to release space diff --git a/releasey/libs/_gh_rc_prepare.sh b/releasey/libs/_gh_rc_prepare.sh index 8679b299..2869f7e4 100644 --- a/releasey/libs/_gh_rc_prepare.sh +++ b/releasey/libs/_gh_rc_prepare.sh @@ -21,8 +21,17 @@ 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 +# Extract the ref name from github.ref +# github_ref environment format: refs/heads/branch-name or refs/tags/tag-name +if [[ "${git_ref}" =~ ^refs/tags/(.+)$ ]]; then + # Running from a tag + git_tag="${BASH_REMATCH[1]}" +else + echo "โŒ Workflow must be run from a release candidate tag, not a branch." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Current ref: \`${git_ref}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please select a release candidate tag (e.g., \`apache-polaris-1.0.0-incubating-rc0\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY exit 1 fi diff --git a/releasey/libs/_gh_release_prepare.sh b/releasey/libs/_gh_release_prepare.sh index 9762795e..a6922796 100644 --- a/releasey/libs/_gh_release_prepare.sh +++ b/releasey/libs/_gh_release_prepare.sh @@ -19,9 +19,6 @@ source "${LIBS_DIR}/_version.sh" -# Get the current branch name -current_branch=$(git branch --show-current) - echo "## Parameters" >> $GITHUB_STEP_SUMMARY if [[ ! -d "${tool}/releasey" ]]; then @@ -29,15 +26,26 @@ if [[ ! -d "${tool}/releasey" ]]; then exit 1 fi +# Extract the ref name from github.ref +# github_ref environment format: refs/heads/branch-name or refs/tags/tag-name +if [[ "${github_ref}" =~ ^refs/heads/release/(.+)$ ]]; then + # Running from a release branch + branch_version="${BASH_REMATCH[1]}" + current_branch="release/${branch_version}" +else + echo "โŒ This workflow must be run from a release branch (release/major.minor.x)." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Current ref: \`${github_ref}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please select a release branch (e.g., \`release/1.0.x\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY + exit 1 +fi # 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 @@ -45,17 +53,12 @@ if ! validate_and_extract_branch_version "${branch_version}"; then fi # Find the next patch number for this major.minor version by looking at existing tags +# Note: find_next_patch_number returns the current patch if no final tag exists, +# which is exactly what we need for publishing (we publish from an RC that has no final tag yet) 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" +version_without_rc="${major}.${minor}.${patch}-incubating" # Find the latest RC tag for this version find_next_rc_number "${version_without_rc}" @@ -74,6 +77,21 @@ if ! git rev-parse "${rc_tag}" >/dev/null 2>&1; then exit 1 fi +# Verify that current HEAD is at the RC tag commit +rc_commit=$(git rev-parse "${rc_tag}") +current_commit=$(git rev-parse HEAD) + +if [[ "${current_commit}" != "${rc_commit}" ]]; then + echo "โŒ Current HEAD (\`${current_commit}\`) does not match RC tag \`${rc_tag}\` (\`${rc_commit}\`)." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This means that some commits have been made on the release branch after the last RC was created." >> $GITHUB_STEP_SUMMARY + echo "You should not publish a release from a branch that has received additional commits after the last RC was created." >> $GITHUB_STEP_SUMMARY + echo "Either remove the commits from the release branch so that it points to the last RC that was voted on, or create a new RC from the current state of the branch." >> $GITHUB_STEP_SUMMARY + exit 1 +fi + +echo "โœ… Current HEAD matches RC tag \`${rc_tag}\`" >> $GITHUB_STEP_SUMMARY + # Create final release tag name final_release_tag="apache-polaris-${tool}-${version_without_rc}" diff --git a/releasey/libs/_version.sh b/releasey/libs/_version.sh index af1b5a71..dc2337e9 100644 --- a/releasey/libs/_version.sh +++ b/releasey/libs/_version.sh @@ -95,7 +95,7 @@ function find_next_rc_number { local version_without_rc="$1" # Get all existing RC tags for this version - local tag_pattern="apache-polaris-${version_without_rc}-rc*" + local tag_pattern="apache-polaris-${version_without_rc}-rc[0-9]+" local existing_tags existing_tags=$(git tag -l "${tag_pattern}" | sort -V) @@ -116,19 +116,23 @@ 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"). + # + # The patch number should only be incremented if there is a final release tag (without -rc suffix) + # for the current highest patch. If only RC tags exist for the highest patch, we should reuse + # that patch number (allowing for additional RCs like rc1, rc2, etc.). local major="$1" local minor="$2" # Get all existing tags for this major.minor version - local tag_pattern="apache-polaris-${tool}-${major}.${minor}.*-incubating-rc*" - local existing_tags - existing_tags=$(git tag -l "${tag_pattern}" | sort -V) + local rc_tag_pattern="apache-polaris-${tool}-${major}\.${minor}\.[0-9]+-incubating-rc[0-9]+" + local existing_rc_tags + existing_rc_tags=$(git tag -l "${rc_tag_pattern}" | sort -V) - if [[ -z "${existing_tags}" ]]; then - # No existing tags, start with patch 0 + if [[ -z "${existing_rc_tags}" ]]; then + # No existing RC tags, start with patch 0 patch=0 else - # Extract all patch numbers and find the highest + # Extract all patch numbers from RC tags and find the highest local highest_patch=-1 while IFS= read -r tag; do if [[ ${tag} =~ apache-polaris-${tool}-${major}\.${minor}\.([0-9]+)-incubating-rc[0-9]+ ]]; then @@ -137,10 +141,17 @@ function find_next_patch_number { highest_patch=${current_patch} fi fi - done <<< "${existing_tags}" - - # Increment the highest patch number found - patch=$((highest_patch + 1)) + done <<< "${existing_rc_tags}" + + # Check if a final release tag exists for the highest patch (without -rc suffix) + local final_tag="apache-polaris-${tool}-${major}.${minor}.${highest_patch}-incubating" + if git rev-parse "${final_tag}" >/dev/null 2>&1; then + # Final release tag exists, increment to next patch number + patch=$((highest_patch + 1)) + else + # No final release tag yet, reuse the same patch number for additional RCs + patch=${highest_patch} + fi fi return 0 From 760536bd85f728639421d6768c07f5bed09764cb Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 7 Jan 2026 14:56:32 +0100 Subject: [PATCH 4/4] more --- .../release-1-create-release-branch.yml | 2 +- .../release-X-cancel-release-candidate.yml | 106 +++++++++++++++ .../_gh_cancel_release_delete_artifacts.sh | 39 ++++++ .../_gh_cancel_release_drop_nexus_repo.sh | 64 +++++++++ .../libs/_gh_cancel_release_email_proposal.sh | 53 ++++++++ releasey/libs/_gh_cancel_release_prepare.sh | 125 ++++++++++++++++++ releasey/libs/_gh_determine_built_flavors.sh | 30 +++-- releasey/libs/_gh_rc_build_prepare.sh | 21 +-- releasey/libs/_gh_rc_email_proposal.sh | 12 +- releasey/libs/_gh_rc_prepare.sh | 42 +++--- releasey/libs/_gh_rc_publish_nexus.sh | 6 +- releasey/libs/_gh_rc_stage_dist_artifacts.sh | 4 +- releasey/libs/_gh_rc_stage_helm.sh | 4 +- releasey/libs/_gh_rc_stage_source.sh | 2 +- releasey/libs/_gh_release_copy_dist.sh | 2 +- releasey/libs/_gh_release_create_github.sh | 2 +- releasey/libs/_gh_release_email_proposal.sh | 2 +- releasey/libs/_gh_release_helm.sh | 2 +- releasey/libs/_gh_release_prepare.sh | 57 ++++---- releasey/libs/_gh_release_push_git_tag.sh | 4 +- 20 files changed, 489 insertions(+), 90 deletions(-) create mode 100644 .github/workflows/release-X-cancel-release-candidate.yml create mode 100644 releasey/libs/_gh_cancel_release_delete_artifacts.sh create mode 100644 releasey/libs/_gh_cancel_release_drop_nexus_repo.sh create mode 100644 releasey/libs/_gh_cancel_release_email_proposal.sh create mode 100644 releasey/libs/_gh_cancel_release_prepare.sh diff --git a/.github/workflows/release-1-create-release-branch.yml b/.github/workflows/release-1-create-release-branch.yml index a47f492d..f6480663 100644 --- a/.github/workflows/release-1-create-release-branch.yml +++ b/.github/workflows/release-1-create-release-branch.yml @@ -96,7 +96,7 @@ jobs: echo "Version: \`${version}\`" >> $GITHUB_STEP_SUMMARY # Create branch name in major.minor.x format - branch_name="${tool}-${major}.${minor}.x" + branch_name="${tool}/${major}.${minor}.x" # Export parameters for next step echo "tool=${tool}" >> $GITHUB_ENV diff --git a/.github/workflows/release-X-cancel-release-candidate.yml b/.github/workflows/release-X-cancel-release-candidate.yml new file mode 100644 index 00000000..936e96fd --- /dev/null +++ b/.github/workflows/release-X-cancel-release-candidate.yml @@ -0,0 +1,106 @@ +# +# 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. +# + +# Note: This workflow uses "X" instead of a number because it's an exceptional +# workflow. It may be run after the third workflow has been run for a given RC. +name: Release - X - Cancel Release Candidate After Vote Failure + +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 drop (e.g., orgapachepolaris-1234)' + required: true + type: string + +jobs: + cancel-release-candidate: + name: Release - X - Cancel Release Candidate After Vote Failure + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + 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 + + 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_repo_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 + env: + git_ref: ${{ github.ref }} + run: source "${LIBS_DIR}/_gh_cancel_release_prepare.sh" + + - name: Drop Apache Nexus staging repository + env: + NEXUS_USERNAME: ${{ secrets.APACHE_USERNAME }} + NEXUS_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + echo "::add-mask::$NEXUS_PASSWORD" + + source "${LIBS_DIR}/_gh_cancel_release_drop_nexus_repo.sh" + + - name: Delete artifacts from Apache dist dev repository + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + echo "::add-mask::$SVN_PASSWORD" + + source "${LIBS_DIR}/_gh_cancel_release_delete_artifacts.sh" + + - name: Generate vote failure email + run: source "${LIBS_DIR}/_gh_cancel_release_email_proposal.sh" diff --git a/releasey/libs/_gh_cancel_release_delete_artifacts.sh b/releasey/libs/_gh_cancel_release_delete_artifacts.sh new file mode 100644 index 00000000..40c71019 --- /dev/null +++ b/releasey/libs/_gh_cancel_release_delete_artifacts.sh @@ -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. +# + +source "${LIBS_DIR}/_constants.sh" +source "${LIBS_DIR}/_exec.sh" + +# Define URLs for artifacts and Helm chart in dist dev +dev_artifacts_url="${APACHE_DIST_URL}${APACHE_DIST_PATH}/${tool}/${version_without_rc}" + +# Check if artifacts directory exists and delete it +if svn ls --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive "${dev_artifacts_url}" >/dev/null 2>&1; then + exec_process svn rm --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive \ + "${dev_artifacts_url}" \ + -m "Cancel Apache Polaris ${version_without_rc} RC${rc_number}" + echo "โœ… Deleted artifacts from ${dev_artifacts_url}" >> "$GITHUB_STEP_SUMMARY" +else + echo "โš ๏ธ Artifacts directory not found at ${dev_artifacts_url}" >> "$GITHUB_STEP_SUMMARY" +fi + +cat <> "$GITHUB_STEP_SUMMARY" +## Distribution Cleanup +Artifacts and Helm chart removed from dist dev repository +EOT diff --git a/releasey/libs/_gh_cancel_release_drop_nexus_repo.sh b/releasey/libs/_gh_cancel_release_drop_nexus_repo.sh new file mode 100644 index 00000000..7c86b605 --- /dev/null +++ b/releasey/libs/_gh_cancel_release_drop_nexus_repo.sh @@ -0,0 +1,64 @@ +# +# 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. +# + +source "${LIBS_DIR}/_exec.sh" + +# Drop the staging repository using Nexus REST API +# The Gradle nexus-publish plugin doesn't provide a drop task, so we use the REST API directly +nexus_url="https://repository.apache.org/service/local" +drop_url="${nexus_url}/staging/bulk/drop" + +# Create the JSON payload for dropping the repository +drop_payload=$(cat <> "$GITHUB_STEP_SUMMARY" +## Nexus Staging Repository +โœ… Staging repository \`${STAGING_REPOSITORY_ID}\` dropped successfully +EOT diff --git a/releasey/libs/_gh_cancel_release_email_proposal.sh b/releasey/libs/_gh_cancel_release_email_proposal.sh new file mode 100644 index 00000000..7259629e --- /dev/null +++ b/releasey/libs/_gh_cancel_release_email_proposal.sh @@ -0,0 +1,53 @@ +# +# 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. +# + +# The ^0 suffix "resolves" a Git tag SHA to a commit SHA, if necessary. +git_commit="$(git rev-parse ${git_tag}^0)" + +cat <> "$GITHUB_STEP_SUMMARY" + +## \`[RESULT][VOTE]\` email proposal + +### Subject +\`\`\` +[RESULT][VOTE] Release Apache Polaris ${tool}${version_without_rc} (rc${rc_number}) +\`\`\` + +### Body +\`\`\` +Hello everyone, + +Thanks to all who participated in the vote for Release Apache Polaris ${tool} ${version_without_rc} (rc${rc_number}). + +The vote failed due to [REASON - TO BE FILLED BY RELEASE MANAGER]. + +A new release candidate will be proposed soon once the issues are addressed. + +Thanks, +\`\`\` + +## Summary +๐Ÿ”„ Release candidate cancellation completed: + +| Component | Status | +| --- | --- | +| Nexus staging repository | โœ… Dropped | +| Distribution artifacts (dist dev) | โœ… Deleted | +| Helm chart (dist dev) | โœ… Deleted | +EOT diff --git a/releasey/libs/_gh_cancel_release_prepare.sh b/releasey/libs/_gh_cancel_release_prepare.sh new file mode 100644 index 00000000..3903982e --- /dev/null +++ b/releasey/libs/_gh_cancel_release_prepare.sh @@ -0,0 +1,125 @@ +# +# 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. +# + +source "${LIBS_DIR}/_version.sh" + +echo "## Parameters" >> "$GITHUB_STEP_SUMMARY" + +if [[ ! -d "${tool}/releasey" ]]; then + echo "โŒ The directory ${tool}/releasey does not exist." >> "$GITHUB_STEP_SUMMARY" + exit 1 +fi + +# Extract the ref name from github.ref +# github_ref environment format: refs/heads/branch-name or refs/tags/tag-name +if [[ "${github_ref}" =~ ^refs/heads/release/(.+)$ ]]; then + # Running from a release branch + branch_version="${BASH_REMATCH[1]}" + current_branch="release/${branch_version}" +else + cat <> "$GITHUB_STEP_SUMMARY" +โŒ This workflow must be run from a release branch (release/major.minor.x). + +Current ref: \`${github_ref}\` + +Please select a release branch (e.g., \`release/1.0.x\`) from the 'Use workflow from' dropdown in the GitHub UI. +EOT + exit 1 +fi +# 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 + +# 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 +# Note: find_next_patch_number returns the current patch if no final tag exists, +# which is exactly what we need for publishing (we publish from an RC that has no final tag yet) +find_next_patch_number "${major}" "${minor}" + +# Build the version string for the latest existing patch +version_without_rc="${major}.${minor}.${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-${tool}-${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 + +# Verify that current HEAD is at the RC tag commit +rc_commit=$(git rev-parse "${rc_tag}") +current_commit=$(git rev-parse HEAD) + +if [[ "${current_commit}" != "${rc_commit}" ]]; then + cat <> "$GITHUB_STEP_SUMMARY" +โŒ Current HEAD (\`${current_commit}\`) does not match RC tag \`${rc_tag}\` (\`${rc_commit}\`). + +This means that some commits have been made on the release branch after the last RC was created. +You should not publish a release from a branch that has received additional commits after the last RC was created. +Either remove the commits from the release branch so that it points to the last RC that was voted on, or create a new RC from the current state of the branch. +EOT + exit 1 +fi + +echo "โœ… Current HEAD matches RC tag \`${rc_tag}\`" >> "$GITHUB_STEP_SUMMARY" + +# Create final release tag name +final_release_tag="apache-polaris-${tool}-${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 +cat <> "$GITHUB_ENV" +version_without_rc=${version_without_rc} +tool=${tool} +rc_tag=${rc_tag} +final_release_tag=${final_release_tag} +release_branch=${current_branch} +EOT + +cat <> "$GITHUB_STEP_SUMMARY" +| Parameter | Value | +| --- | --- | +| Tool | \`${tool}\` | +| Version | \`${version_without_rc}\` | +| RC tag to promote | \`${rc_tag}\` | +| Final release tag | \`${final_release_tag}\` | +| Release branch | \`${current_branch}\` | +EOT diff --git a/releasey/libs/_gh_determine_built_flavors.sh b/releasey/libs/_gh_determine_built_flavors.sh index b4e4062d..285b7514 100644 --- a/releasey/libs/_gh_determine_built_flavors.sh +++ b/releasey/libs/_gh_determine_built_flavors.sh @@ -24,29 +24,31 @@ skip_python=0 if [[ ! -x "${tool}/releasey/maven-build.sh" ]]; then skip_maven=1 - echo "## No Maven Artifacts built by ${tool}" >> $GITHUB_STEP_SUMMARY + echo "## No Maven Artifacts built by ${tool}" >> "$GITHUB_STEP_SUMMARY" fi if [[ ! -x "${tool}/releasey/docker-build.sh" ]]; then skip_docker=1 - echo "## No Docker Images built by ${tool}" >> $GITHUB_STEP_SUMMARY + echo "## No Docker Images built by ${tool}" >> "$GITHUB_STEP_SUMMARY" fi if [[ ! -x "${tool}/releasey/helm-build.sh" ]]; then skip_helm=1 - echo "## No Helm Charts built by ${tool}" >> $GITHUB_STEP_SUMMARY + echo "## No Helm Charts built by ${tool}" >> "$GITHUB_STEP_SUMMARY" fi if [[ ! -x "${tool}/releasey/python-build.sh" ]]; then skip_python=1 - echo "## No Python packages built by ${tool}" >> $GITHUB_STEP_SUMMARY + echo "## No Python packages built by ${tool}" >> "$GITHUB_STEP_SUMMARY" fi -( echo "skip_maven=$skip_maven" - echo "skip_docker=$skip_docker" - echo "skip_helm=$skip_helm" - echo "skip_python=$skip_python" -) >> $GITHUB_ENV +cat << EOT >> "$GITHUB_ENV" +skip_maven=$skip_maven +skip_docker=$skip_docker +skip_helm=$skip_helm +skip_python=$skip_python +EOT -( echo "skip_maven=$skip_maven" - echo "skip_docker=$skip_docker" - echo "skip_helm=$skip_helm" - echo "skip_python=$skip_python" -) >> $GITHUB_OUTPUT +cat << EOT >> "$GITHUB_OUTPUT" +skip_maven=$skip_maven +skip_docker=$skip_docker +skip_helm=$skip_helm +skip_python=$skip_python +EOT diff --git a/releasey/libs/_gh_rc_build_prepare.sh b/releasey/libs/_gh_rc_build_prepare.sh index 9bf7369a..0431fffe 100644 --- a/releasey/libs/_gh_rc_build_prepare.sh +++ b/releasey/libs/_gh_rc_build_prepare.sh @@ -20,28 +20,29 @@ source "${LIBS_DIR}/_version.sh" 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 + 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 + echo "โŒ Invalid git tag format: \`${git_tag}\`. Expected format: apache-polaris-x.y.z-incubating-rcN." >> "$GITHUB_STEP_SUMMARY" exit 1 fi if [[ ! -d "${tool}/releasey" ]]; then - echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + echo "โŒ The directory ${tool}/releasey does not exist." >> "$GITHUB_STEP_SUMMARY" exit 1 fi dist_dev_dir="${RELEASEY_DIR}/polaris-tools-dist-dev" # Export variables for next steps and job outputs -( echo "tool=${tool}" - echo "git_tag=${git_tag}" - echo "version_without_rc=${version_without_rc}" - echo "rc_number=${rc_number}" - echo "dist_dev_dir=${dist_dev_dir}" - echo "version_dir=${dist_dev_dir}/${version_without_rc}" -) >> $GITHUB_ENV +cat << EOT >> "$GITHUB_ENV" +tool=${tool} +git_tag=${git_tag} +version_without_rc=${version_without_rc} +rc_number=${rc_number} +dist_dev_dir=${dist_dev_dir} +version_dir=${dist_dev_dir}/${version_without_rc} +EOT diff --git a/releasey/libs/_gh_rc_email_proposal.sh b/releasey/libs/_gh_rc_email_proposal.sh index 0a0c7166..2ab6567f 100644 --- a/releasey/libs/_gh_rc_email_proposal.sh +++ b/releasey/libs/_gh_rc_email_proposal.sh @@ -20,7 +20,7 @@ # The ^0 suffix "resolves" a Git tag SHA to a commit SHA, if necessary. git_commit="$(git rev-parse ${git_tag}^0)" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## \`VOTE\` email proposal @@ -47,32 +47,32 @@ The release tarball, signature, and checksums are here: EOT if [[ ${skip_maven} != 1 ]]; then -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" Convenience binary artifacts are staged on Nexus. The Maven repositories URLs are: * https://repository.apache.org/content/repositories/${staging_repo_id}/ EOT fi if [[ ${skip_python} != 1 ]]; then -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## TODO ADD PYTHON EOT fi if [[ ${skip_docker} != 1 ]]; then -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## TODO ADD DOCKER EOT fi if [[ ${skip_helm} != 1 ]]; then - cat <> $GITHUB_STEP_SUMMARY + cat <> "$GITHUB_STEP_SUMMARY" Helm charts are available on: * https://dist.apache.org/repos/dist/dev/incubator/polaris-tools/${tool}/$version_without_rc}/helm-charts EOT fi -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" You can find the KEYS file here: * https://downloads.apache.org/incubator/polaris-tools/KEYS diff --git a/releasey/libs/_gh_rc_prepare.sh b/releasey/libs/_gh_rc_prepare.sh index 2869f7e4..910237e6 100644 --- a/releasey/libs/_gh_rc_prepare.sh +++ b/releasey/libs/_gh_rc_prepare.sh @@ -19,7 +19,7 @@ source "${LIBS_DIR}/_version.sh" -echo "## Parameters" >> $GITHUB_STEP_SUMMARY +echo "## Parameters" >> "$GITHUB_STEP_SUMMARY" # Extract the ref name from github.ref # github_ref environment format: refs/heads/branch-name or refs/tags/tag-name @@ -27,41 +27,45 @@ if [[ "${git_ref}" =~ ^refs/tags/(.+)$ ]]; then # Running from a tag git_tag="${BASH_REMATCH[1]}" else - echo "โŒ Workflow must be run from a release candidate tag, not a branch." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Current ref: \`${git_ref}\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Please select a release candidate tag (e.g., \`apache-polaris-1.0.0-incubating-rc0\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY + ( + echo "โŒ Workflow must be run from a release candidate tag, not a branch." + echo "" + echo "Current ref: \`${git_ref}\`" + echo "" + echo "Please select a release candidate tag (e.g., \`apache-polaris-1.0.0-incubating-rc0\`) from the 'Use workflow from' dropdown in the GitHub UI." + ) >> "$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 + echo "โŒ Invalid git tag format: \`${git_tag}\`. Expected format: apache-polaris-x.y.z-incubating-rcN." >> "$GITHUB_STEP_SUMMARY" exit 1 fi if [[ ! -d "${tool}/releasey" ]]; then - echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + echo "โŒ The directory ${tool}/releasey does not exist." >> "$GITHUB_STEP_SUMMARY" exit 1 fi source "${LIBS_DIR}/_gh_determine_built_artifacts.sh" # Export variables for next steps and job outputs -( echo "tool=${tool}" - echo "git_tag=${git_tag}" - echo "version_without_rc=${version_without_rc}" - echo "rc_number=${rc_number}" -) >> $GITHUB_ENV +cat << EOT >> "$GITHUB_ENV" +tool=${tool} +git_tag=${git_tag} +version_without_rc=${version_without_rc} +rc_number=${rc_number} +EOT -( echo "tool=${tool}" - echo "git_tag=${git_tag}" - echo "version_without_rc=${version_without_rc}" - echo "rc_number=${rc_number}" -) >> $GITHUB_OUTPUT +cat << EOT >> "$GITHUB_OUTPUT" +tool=${tool} +git_tag=${git_tag} +version_without_rc=${version_without_rc} +rc_number=${rc_number} +EOT -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" | Parameter | Value | | --- | --- | | Tool | \`${tool}\` | diff --git a/releasey/libs/_gh_rc_publish_nexus.sh b/releasey/libs/_gh_rc_publish_nexus.sh index 016234dc..919284dd 100644 --- a/releasey/libs/_gh_rc_publish_nexus.sh +++ b/releasey/libs/_gh_rc_publish_nexus.sh @@ -26,10 +26,10 @@ out_params_file="$(mktemp --tmpdir maven-stage-XXXXXXXXXX)" source "$out_params_file" -echo "staging_repo_id=${staging_repo_id}" >> $GITHUB_OUTPUT -echo "staging_repo_url=${staging_repo_url}" >> $GITHUB_OUTPUT +echo "staging_repo_id=${staging_repo_id}" >> "$GITHUB_OUTPUT" +echo "staging_repo_url=${staging_repo_url}" >> "$GITHUB_OUTPUT" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## Nexus Staging Repository Artifacts published and staging repository closed successfully diff --git a/releasey/libs/_gh_rc_stage_dist_artifacts.sh b/releasey/libs/_gh_rc_stage_dist_artifacts.sh index fbdbfb5f..726ad943 100644 --- a/releasey/libs/_gh_rc_stage_dist_artifacts.sh +++ b/releasey/libs/_gh_rc_stage_dist_artifacts.sh @@ -33,14 +33,14 @@ if [[ -d "${tool}/build/distributions" ]]; then exec_process svn add "${tool}/${version_without_rc}" exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris ${tool} ${version_without_rc} RC${rc_number}" - cat <> $GITHUB_STEP_SUMMARY + cat <> "$GITHUB_STEP_SUMMARY" ## Staging to dist dev Artifacts staged to Apache dist dev repository EOT else - cat <> $GITHUB_STEP_SUMMARY + cat <> "$GITHUB_STEP_SUMMARY" ## No artifacts staged to dist dev EOT diff --git a/releasey/libs/_gh_rc_stage_helm.sh b/releasey/libs/_gh_rc_stage_helm.sh index f30c9afa..f147f655 100644 --- a/releasey/libs/_gh_rc_stage_helm.sh +++ b/releasey/libs/_gh_rc_stage_helm.sh @@ -44,8 +44,8 @@ exec_process svn add "${version_without_rc}/helm-charts" exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage Apache Polaris ${tool} Helm chart(s) ${version_without_rc} RC${rc_number}" -echo "## Helm Chart Summary" >> $GITHUB_STEP_SUMMARY -cat <> $GITHUB_STEP_SUMMARY +echo "## Helm Chart Summary" >> "$GITHUB_STEP_SUMMARY" +cat <> "$GITHUB_STEP_SUMMARY" ๐ŸŽ‰ Helm chart built and staged successfully: | Component | Status | diff --git a/releasey/libs/_gh_rc_stage_source.sh b/releasey/libs/_gh_rc_stage_source.sh index 118f7fcf..b599072c 100644 --- a/releasey/libs/_gh_rc_stage_source.sh +++ b/releasey/libs/_gh_rc_stage_source.sh @@ -48,7 +48,7 @@ exec_process cd "${dist_dev_dir}" exec_process svn add "${tool}/${version_without_rc}/${tool_base_name}.tar.gz*" exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Stage source tarball of Apache Polaris ${tool} ${version_without_rc} RC${rc_number}" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## Staging source tarball to dist dev Source tarball staged to Apache dist dev repository EOT diff --git a/releasey/libs/_gh_release_copy_dist.sh b/releasey/libs/_gh_release_copy_dist.sh index 94397aab..d649beb3 100644 --- a/releasey/libs/_gh_release_copy_dist.sh +++ b/releasey/libs/_gh_release_copy_dist.sh @@ -30,7 +30,7 @@ exec_process svn mv --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non- "${dev_artifacts_url}" "${release_artifacts_url}" \ -m "Release Apache Polaris ${tool} ${version_without_rc}" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## Distribution Artifacts and Helm chart moved from dist dev to dist release EOT diff --git a/releasey/libs/_gh_release_create_github.sh b/releasey/libs/_gh_release_create_github.sh index ef82f2d0..92213e92 100644 --- a/releasey/libs/_gh_release_create_github.sh +++ b/releasey/libs/_gh_release_create_github.sh @@ -77,7 +77,7 @@ if [[ -d "${artifacts_dir}" ]]; then done fi -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## GitHub Release GitHub release created: \`${final_release_tag}\` EOT diff --git a/releasey/libs/_gh_release_email_proposal.sh b/releasey/libs/_gh_release_email_proposal.sh index 3d998c94..41b62cd9 100644 --- a/releasey/libs/_gh_release_email_proposal.sh +++ b/releasey/libs/_gh_release_email_proposal.sh @@ -20,7 +20,7 @@ # The ^0 suffix "resolves" a Git tag SHA to a commit SHA, if necessary. git_commit="$(git rev-parse ${git_tag}^0)" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## \`[ANNOUNCE]\` email proposal diff --git a/releasey/libs/_gh_release_helm.sh b/releasey/libs/_gh_release_helm.sh index 66b00af6..d1585075 100644 --- a/releasey/libs/_gh_release_helm.sh +++ b/releasey/libs/_gh_release_helm.sh @@ -40,7 +40,7 @@ done exec_process svn commit --username "$SVN_USERNAME" --password "$SVN_PASSWORD" --non-interactive -m "Update Helm index for ${tool} ${version_without_rc} release" -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## Helm Index Helm index updated in dist release EOT diff --git a/releasey/libs/_gh_release_prepare.sh b/releasey/libs/_gh_release_prepare.sh index a6922796..3903982e 100644 --- a/releasey/libs/_gh_release_prepare.sh +++ b/releasey/libs/_gh_release_prepare.sh @@ -19,10 +19,10 @@ source "${LIBS_DIR}/_version.sh" -echo "## Parameters" >> $GITHUB_STEP_SUMMARY +echo "## Parameters" >> "$GITHUB_STEP_SUMMARY" if [[ ! -d "${tool}/releasey" ]]; then - echo "โŒ The directory ${tool}/releasey does not exist." >> $GITHUB_STEP_SUMMARY + echo "โŒ The directory ${tool}/releasey does not exist." >> "$GITHUB_STEP_SUMMARY" exit 1 fi @@ -33,22 +33,24 @@ if [[ "${github_ref}" =~ ^refs/heads/release/(.+)$ ]]; then branch_version="${BASH_REMATCH[1]}" current_branch="release/${branch_version}" else - echo "โŒ This workflow must be run from a release branch (release/major.minor.x)." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Current ref: \`${github_ref}\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Please select a release branch (e.g., \`release/1.0.x\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY + cat <> "$GITHUB_STEP_SUMMARY" +โŒ This workflow must be run from a release branch (release/major.minor.x). + +Current ref: \`${github_ref}\` + +Please select a release branch (e.g., \`release/1.0.x\`) from the 'Use workflow from' dropdown in the GitHub UI. +EOT exit 1 fi # 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 + echo "โŒ This workflow must be run from a release branch (release/major.minor.x). Current branch: \`${current_branch}\`." >> "$GITHUB_STEP_SUMMARY" exit 1 fi # 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 + echo "โŒ Invalid release branch version format: \`${branch_version}\`. Expected format: major.minor.x." >> "$GITHUB_STEP_SUMMARY" exit 1 fi @@ -65,7 +67,7 @@ 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 + 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 @@ -73,7 +75,7 @@ rc_tag="apache-polaris-${tool}-${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 + echo "โŒ RC tag \`${rc_tag}\` does not exist in repository." >> "$GITHUB_STEP_SUMMARY" exit 1 fi @@ -82,34 +84,37 @@ rc_commit=$(git rev-parse "${rc_tag}") current_commit=$(git rev-parse HEAD) if [[ "${current_commit}" != "${rc_commit}" ]]; then - echo "โŒ Current HEAD (\`${current_commit}\`) does not match RC tag \`${rc_tag}\` (\`${rc_commit}\`)." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "This means that some commits have been made on the release branch after the last RC was created." >> $GITHUB_STEP_SUMMARY - echo "You should not publish a release from a branch that has received additional commits after the last RC was created." >> $GITHUB_STEP_SUMMARY - echo "Either remove the commits from the release branch so that it points to the last RC that was voted on, or create a new RC from the current state of the branch." >> $GITHUB_STEP_SUMMARY + cat <> "$GITHUB_STEP_SUMMARY" +โŒ Current HEAD (\`${current_commit}\`) does not match RC tag \`${rc_tag}\` (\`${rc_commit}\`). + +This means that some commits have been made on the release branch after the last RC was created. +You should not publish a release from a branch that has received additional commits after the last RC was created. +Either remove the commits from the release branch so that it points to the last RC that was voted on, or create a new RC from the current state of the branch. +EOT exit 1 fi -echo "โœ… Current HEAD matches RC tag \`${rc_tag}\`" >> $GITHUB_STEP_SUMMARY +echo "โœ… Current HEAD matches RC tag \`${rc_tag}\`" >> "$GITHUB_STEP_SUMMARY" # Create final release tag name final_release_tag="apache-polaris-${tool}-${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 + 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}" - echo "tool=${tool}" - echo "rc_tag=${rc_tag}" - echo "final_release_tag=${final_release_tag}" - echo "release_branch=${current_branch}" -) >> $GITHUB_ENV - -cat <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_ENV" +version_without_rc=${version_without_rc} +tool=${tool} +rc_tag=${rc_tag} +final_release_tag=${final_release_tag} +release_branch=${current_branch} +EOT + +cat <> "$GITHUB_STEP_SUMMARY" | Parameter | Value | | --- | --- | | Tool | \`${tool}\` | diff --git a/releasey/libs/_gh_release_push_git_tag.sh b/releasey/libs/_gh_release_push_git_tag.sh index fbf74eb1..3e1eb112 100644 --- a/releasey/libs/_gh_release_push_git_tag.sh +++ b/releasey/libs/_gh_release_push_git_tag.sh @@ -21,12 +21,12 @@ 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 +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 <> $GITHUB_STEP_SUMMARY +cat <> "$GITHUB_STEP_SUMMARY" ## Git Release Tag Final release tag \`${final_release_tag}\` created and pushed EOT