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 new file mode 100644 index 00000000..f6480663 --- /dev/null +++ b/.github/workflows/release-1-create-release-branch.yml @@ -0,0 +1,131 @@ +# +# 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: + 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 + 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: 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 + 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="${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 + + - 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..2d38c381 --- /dev/null +++ b/.github/workflows/release-2-update-release-candidate.yml @@ -0,0 +1,211 @@ +# +# 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: 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/([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 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 + 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-${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 + 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 | + | --- | --- | + | Tool | \`${tool}\` | + | 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 + if: env.rc_number == '0' + run: | + source "${LIBS_DIR}/_version.sh" + + # 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}\` + EOT + + - name: Update changelog + if: env.rc_number == '0' + run: | + # Call tool-specific update changelog script + (cd ${tool} ; ./releasey/update_changelog.sh) + + cat <> $GITHUB_STEP_SUMMARY + ## Changelog + Changelog patched successfully + 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}" + + # Push the changes + exec_process git push origin "${release_branch}" + + - 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}" + + 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..fd495a8e --- /dev/null +++ b/.github/workflows/release-3-build-and-publish-artifacts.yml @@ -0,0 +1,426 @@ +# +# 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 }} + 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 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 + + - name: Set up environment variables + id: set-outputs + run: | + echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV + echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV + + echo "## Mode" >> $GITHUB_STEP_SUMMARY + if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then + echo "DRY_RUN=1" >> $GITHUB_ENV + echo "dry_run=1" >> $GITHUB_OUTPUT + echo "‼️ DRY_RUN mode enabled - No actual changes will be made" >> $GITHUB_STEP_SUMMARY + else + echo "DRY_RUN=0" >> $GITHUB_ENV + echo "dry_run=0" >> $GITHUB_OUTPUT + echo "DRY_RUN mode disabled - Performing actual operations" >> $GITHUB_STEP_SUMMARY + fi + + - name: Validate release candidate tag + id: prepare + env: + git_ref: ${{ github.ref }} + run: source "${LIBS_DIR}/_gh_rc_prepare.sh" + + - name: Determine flavors to build + id: flavors + run: source "${LIBS_DIR}/_gh_determine_built_flavors.sh" + + 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 }} + + steps: + - name: Checkout repository + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 + + - name: Set up environment variables + run: | + echo "RELEASEY_DIR=$(pwd)/releasey" >> $GITHUB_ENV + echo "LIBS_DIR=$(pwd)/releasey/libs" >> $GITHUB_ENV + + - name: Extract information from release candidate tag + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" + + - 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 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: 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 }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Assemble artifacts + if: env.skip_maven != '1' + run: | + source "${LIBS_DIR}/_exec.sh" + + # Call tool-specific maven-artifacts script + (cd ${tool} ; ./releasey/maven-build.sh) + + cat <> $GITHUB_STEP_SUMMARY + ## Build + 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}/_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}/_gh_rc_publish_nexus.sh" + + 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 }} + + steps: + - name: Checkout repository + if: env.skip_python != '1' + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 + + - 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 + + - name: Extract information from release candidate tag + if: env.skip_python != '1' + run: source "${LIBS_DIR}/_gh_rc_build_prepare.sh" + + - 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 + + - name: Build Python Packages + if: env.skip_python != '1' + run: | + # Call tool-specific build script + (cd ${tool} ; ./releasey/python-build.sh) + + - 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-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: 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: 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 and Push Polaris Server Docker images + if: env.skip_docker != '1' + run: | + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/docker-build.sh) + + 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 }} + 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: 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 }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Build Helm Charts + if: env.skip_helm != '1' + env: + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/helm-build.sh) + + - name: Stage Helm Charts + if: env.skip_helm != '1' + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + run: | + # Call tool-specific maven-publish script + (cd ${tool} ; ./releasey/helm-stage.sh) + + + 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 new file mode 100644 index 00000000..ef2518f3 --- /dev/null +++ b/.github/workflows/release-4-publish-release.yml @@ -0,0 +1,203 @@ +# +# 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 for tools that publish Maven artifacts' + required: false + 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: 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_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: Check necessity of publishing artifacts + 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 + env: + SVN_USERNAME: ${{ secrets.APACHE_USERNAME }} + SVN_PASSWORD: ${{ secrets.APACHE_PASSWORD }} + 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}/_gh_release_helm.sh" + + - name: Create final release tag and push to Git repository + 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 \ + --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' && env.skip_docker != '1' }} + run: | + echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login ghcr.io -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin + + - name: Publish Docker images to Docker Hub + if: env.skip_docker != '1' + run: | + source "${LIBS_DIR}/_exec.sh" + + # 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: Release to PyPI + if: ${{ env.skip_python != '1' }} + run: | + source "${LIBS_DIR}/_exec.sh" + + # 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" + + # 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: + + | 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 + + source "${LIBS_DIR}/_gh_rc_email_proposal.sh" + 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/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 new file mode 100644 index 00000000..02302c19 --- /dev/null +++ b/releasey/libs/_constants.sh @@ -0,0 +1,34 @@ +#!/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-([a-z+]+)-$VERSION_REGEX-rc([0-9]+)$" +# Branch validation regex pattern for major.minor.x format +BRANCH_VERSION_REGEX="([0-9]+)\.([0-9]+)\.x" 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/_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 new file mode 100644 index 00000000..285b7514 --- /dev/null +++ b/releasey/libs/_gh_determine_built_flavors.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. +# + +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 + +cat << EOT >> "$GITHUB_ENV" +skip_maven=$skip_maven +skip_docker=$skip_docker +skip_helm=$skip_helm +skip_python=$skip_python +EOT + +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_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..0431fffe --- /dev/null +++ b/releasey/libs/_gh_rc_build_prepare.sh @@ -0,0 +1,48 @@ +# +# 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 +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 new file mode 100644 index 00000000..2ab6567f --- /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..910237e6 --- /dev/null +++ b/releasey/libs/_gh_rc_prepare.sh @@ -0,0 +1,75 @@ +# +# 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" + +# 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." + 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" + 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 +cat << EOT >> "$GITHUB_ENV" +tool=${tool} +git_tag=${git_tag} +version_without_rc=${version_without_rc} +rc_number=${rc_number} +EOT + +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" +| 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..919284dd --- /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..726ad943 --- /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..f147f655 --- /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..b599072c --- /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..d649beb3 --- /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..92213e92 --- /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..41b62cd9 --- /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..d1585075 --- /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..3903982e --- /dev/null +++ b/releasey/libs/_gh_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_release_push_git_tag.sh b/releasey/libs/_gh_release_push_git_tag.sh new file mode 100644 index 00000000..3e1eb112 --- /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/_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..dc2337e9 --- /dev/null +++ b/releasey/libs/_version.sh @@ -0,0 +1,158 @@ +#!/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 + + 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 +} + +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 + + 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 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[0-9]+" + 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-${tool}-${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"). + # + # 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 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_rc_tags}" ]]; then + # No existing RC tags, start with patch 0 + patch=0 + else + # 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 + local current_patch="${BASH_REMATCH[1]}" + if [[ ${current_patch} -gt ${highest_patch} ]]; then + highest_patch=${current_patch} + fi + fi + 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 +} \ No newline at end of file