diff --git a/.github/workflows/release-2-update-release-candidate.yml b/.github/workflows/release-2-update-release-candidate.yml index b760b5287e..83d75b6463 100644 --- a/.github/workflows/release-2-update-release-candidate.yml +++ b/.github/workflows/release-2-update-release-candidate.yml @@ -146,6 +146,7 @@ jobs: java-version: '21' - name: Update project versions + if: env.rc_number == '0' run: | source "${LIBS_DIR}/_version.sh" @@ -156,6 +157,7 @@ jobs: EOT - name: Update changelog + if: env.rc_number == '0' run: | source "${LIBS_DIR}/_exec.sh" exec_process ./gradlew patchChangelog @@ -166,6 +168,7 @@ jobs: EOT - name: Commit and push changes + if: env.rc_number == '0' run: | source "${LIBS_DIR}/_constants.sh" source "${LIBS_DIR}/_exec.sh" @@ -176,19 +179,19 @@ jobs: "$HELM_CHART_YAML_FILE" \ "$HELM_README_FILE" \ "$CHANGELOG_FILE" - exec_process git commit -m "[chore] Bump version to ${version_without_rc} for release candidate ${rc_number}" + exec_process git commit -m "[chore] Bump version to ${version_without_rc}" # Push the changes exec_process git push origin "${release_branch}" - # Get the new commit SHA after our changes - new_tag_ref=$(git rev-parse HEAD) - echo "new_tag_ref=${new_tag_ref}" >> $GITHUB_ENV - - name: Create RC tag at new commit run: | source "${LIBS_DIR}/_exec.sh" + # Get the new commit SHA after our changes + new_tag_ref=$(git rev-parse HEAD) + echo "new_tag_ref=${new_tag_ref}" >> $GITHUB_ENV + # Create the tag at the new commit exec_process git tag "${release_tag}" "${new_tag_ref}" exec_process git push origin "${release_tag}" diff --git a/.github/workflows/release-3-build-and-publish-artifacts.yml b/.github/workflows/release-3-build-and-publish-artifacts.yml index 3577a4cc86..0c759a0bb8 100644 --- a/.github/workflows/release-3-build-and-publish-artifacts.yml +++ b/.github/workflows/release-3-build-and-publish-artifacts.yml @@ -73,8 +73,19 @@ jobs: echo "## Parameters" >> $GITHUB_STEP_SUMMARY - if ! git_tag=$(git describe --tags --exact-match HEAD 2>/dev/null); then - echo "❌ Current HEAD is not on a release candidate tag. Please checkout a release candidate tag first." >> $GITHUB_STEP_SUMMARY + # Extract the ref name from github.ref + # github.ref format: refs/heads/branch-name or refs/tags/tag-name + ref="${{ github.ref }}" + + if [[ "${ref}" =~ ^refs/tags/(.+)$ ]]; then + # Running from a tag + git_tag="${BASH_REMATCH[1]}" + else + echo "❌ Workflow must be run from a release candidate tag, not a branch." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Current ref: \`${ref}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please select a release candidate tag (e.g., \`apache-polaris-1.0.0-incubating-rc0\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY exit 1 fi diff --git a/.github/workflows/release-4-publish-release.yml b/.github/workflows/release-4-publish-release.yml index 9e2750da03..36a0042fb0 100644 --- a/.github/workflows/release-4-publish-release.yml +++ b/.github/workflows/release-4-publish-release.yml @@ -92,38 +92,38 @@ jobs: run: | source "${LIBS_DIR}/_version.sh" - # Get the current branch name - current_branch=$(git branch --show-current) - echo "## Parameters" >> $GITHUB_STEP_SUMMARY - # Validate that we're on a release branch - if [[ ! "${current_branch}" =~ ^release/(.+)$ ]]; then - echo "❌ This workflow must be run from a release branch (release/major.minor.x). Current branch: \`${current_branch}\`." >> $GITHUB_STEP_SUMMARY + # Extract the ref name from github.ref + # github.ref format: refs/heads/branch-name or refs/tags/tag-name + ref="${{ github.ref }}" + + if [[ "${ref}" =~ ^refs/heads/release/(.+)$ ]]; then + # Running from a release branch + branch_version="${BASH_REMATCH[1]}" + current_branch="release/${branch_version}" + else + echo "❌ This workflow must be run from a release branch (release/major.minor.x)." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Current ref: \`${ref}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please select a release branch (e.g., \`release/1.0.x\`) from the 'Use workflow from' dropdown in the GitHub UI." >> $GITHUB_STEP_SUMMARY exit 1 fi - # Extract version from release branch name - branch_version="${BASH_REMATCH[1]}" - # Validate branch version format and extract components if ! validate_and_extract_branch_version "${branch_version}"; then echo "❌ Invalid release branch version format: \`${branch_version}\`. Expected format: major.minor.x." >> $GITHUB_STEP_SUMMARY exit 1 fi - # Find the next patch number for this major.minor version by looking at existing tags + # Find the patch number for this major.minor version by looking at existing tags + # Note: find_next_patch_number returns the current patch if no final tag exists, + # which is exactly what we need for publishing (we publish from an RC that has no final tag yet) find_next_patch_number "${major}" "${minor}" - next_patch=$((patch)) - latest_patch=$((next_patch - 1)) - - if [[ ${next_patch} -eq 0 ]]; then - echo "❌ No existing tags found for version \`${major}.${minor}.0\`. Expected at least one RC to be created before publishing a release." >> $GITHUB_STEP_SUMMARY - exit 1 - fi - # Build the version string for the latest existing patch - version_without_rc="${major}.${minor}.${latest_patch}-incubating" + # Build the version string for the patch with RC tags + version_without_rc="${major}.${minor}.${patch}-incubating" # Find the latest RC tag for this version find_next_rc_number "${version_without_rc}" @@ -142,6 +142,21 @@ jobs: exit 1 fi + # Verify that current HEAD is at the RC tag commit + rc_commit=$(git rev-parse "${rc_tag}") + current_commit=$(git rev-parse HEAD) + + if [[ "${current_commit}" != "${rc_commit}" ]]; then + echo "❌ Current HEAD (\`${current_commit}\`) does not match RC tag \`${rc_tag}\` (\`${rc_commit}\`)." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This means that some commits have been made on the release branch after the last RC was created." >> $GITHUB_STEP_SUMMARY + echo "You should not publish a release from a branch that has received additional commits after the last RC was created." >> $GITHUB_STEP_SUMMARY + echo "Either remove the commits from the release branch so that it points to the last RC that was voted on, or create a new RC from the current state of the branch." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + echo "✅ Current HEAD matches RC tag \`${rc_tag}\`" >> $GITHUB_STEP_SUMMARY + # Create final release tag name final_release_tag="apache-polaris-${version_without_rc}" diff --git a/releasey/libs/_version.sh b/releasey/libs/_version.sh index 7d812f1304..d882af4631 100644 --- a/releasey/libs/_version.sh +++ b/releasey/libs/_version.sh @@ -146,19 +146,23 @@ function find_next_patch_number { # This function finds the next available patch number for a given major.minor version. # It returns 0 and sets the global variable patch to the next available patch number. # Patch numbers start from 0. It takes major and minor as input (e.g., "1", "0"). + # + # The patch number should only be incremented if there is a final release tag (without -rc suffix) + # for the current highest patch. If only RC tags exist for the highest patch, we should reuse + # that patch number (allowing for additional RCs like rc1, rc2, etc.). local major="$1" local minor="$2" - # Get all existing tags for this major.minor version - local tag_pattern="apache-polaris-${major}.${minor}.*-incubating-rc*" - local existing_tags - existing_tags=$(git tag -l "${tag_pattern}" | sort -V) + # Get all existing RC tags for this major.minor version + local rc_tag_pattern="apache-polaris-${major}.${minor}.*-incubating-rc*" + local existing_rc_tags + existing_rc_tags=$(git tag -l "${rc_tag_pattern}" | sort -V) - if [[ -z "${existing_tags}" ]]; then - # No existing tags, start with patch 0 + if [[ -z "${existing_rc_tags}" ]]; then + # No existing RC tags, start with patch 0 patch=0 else - # Extract all patch numbers and find the highest + # Extract all patch numbers from RC tags and find the highest local highest_patch=-1 while IFS= read -r tag; do if [[ ${tag} =~ apache-polaris-${major}\.${minor}\.([0-9]+)-incubating-rc[0-9]+ ]]; then @@ -167,10 +171,17 @@ function find_next_patch_number { highest_patch=${current_patch} fi fi - done <<< "${existing_tags}" - - # Increment the highest patch number found - patch=$((highest_patch + 1)) + done <<< "${existing_rc_tags}" + + # Check if a final release tag exists for the highest patch (without -rc suffix) + local final_tag="apache-polaris-${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 diff --git a/site/content/community/release-guides/semi-automated-release-guide.md b/site/content/community/release-guides/semi-automated-release-guide.md index d59bdae49c..5d85832ee2 100644 --- a/site/content/community/release-guides/semi-automated-release-guide.md +++ b/site/content/community/release-guides/semi-automated-release-guide.md @@ -44,16 +44,17 @@ Polaris follows a schedule-driven release model. The first thing to do is to sen Note that the tentative date is only a suggestion. The actual date of the release will be determined by taking community feedback into account. For instance, if specific pull requests are about to be merged, the release may be delayed by a couple of days to include them. ``` -[DISCUSS] Apache Polaris x.y.z +[DISCUSS] Apache Polaris [major].[minor].[patch] ``` ``` Hello everyone, The purpose of this e-mail is to collect feedback on the upcoming Apache -Polaris x.y.z release. The tentative release date is YYYY-MM-DD. Please let -me know if you have any concerns or comments, or if there are some specific -pull requests that you would like to see included in the release. +Polaris [major].[minor].[patch] release. The tentative release date is +YYYY-MM-DD. Please let me know if you have any concerns or comments, or if +there are some specific pull requests that you would like to see included in +the release. Thanks, ``` @@ -71,17 +72,20 @@ Once the workflow has run, the run details page contains a recap of the main inf ![Screenshot of a detailed run of the first release workflow for 1.3.0-incubating](/img/release-guides/github-workflow-1-detail.png "Screenshot of a detailed run of the first release workflow for 1.3.0-incubating") -## RC0 only - Changelog and version update workflow +## Release candidate tag creation workflow The second Github workflow to run is [`Release - 2 - Update version and Changelog for Release Candidate`](https://github.com/apache/polaris/actions/workflows/release-2-update-release-candidate.yml). This workflow will: * Verify that all Github checks are green for the release branch. -* Increase the patch version number by 1 if the workflow has already been run for that `major.minor` version. -* Update the project version files with the final version number -* Commit the changes to the release branch -* Create the `major.minor.patch-rc0` tag +* For RC0 only: update the project version files with the final version number, update the changelog, and commit the changes to the release branch. +* Create the `apache-polaris-[major].[minor].[patch]-incubating-rc[N]` tag -This workflow can only be run from a `release/x.y.z` branch. Selecting any other branch in the Github Actions UI will result in a failure. +The rules to create the `[major].[minor].[patch]-rc[N]` tag are as follows: +* Find the last patch number for the `[major].[minor]` version by looking at existing tags, or 0 if no tag exists. +* Find the last RC number for the `[major].[minor].[patch]` version by looking at existing tags, or 0 if no tag exists. +* If a final release tag exists for the `[major].[minor].[patch]` version, then use `[major].[minor].[patch+1]-rc0` +* Else if an RC tag exists for the `[major].[minor].[patch]-rc[N]` version, then create `[major].[minor].[patch]-rc[N+1]` +* Else create `[major].[minor].[patch]-rc0` -Note that the tag that is created is for RC0. This workflow should **only be executed only once** per `major.minor.patch` version. +This workflow can only be run from a `release/[major].[minor].x` branch. Selecting any other branch in the Github Actions UI will result in a failure. ![Screenshot of the second release workflow for 1.3.0-incubating](/img/release-guides/github-workflow-2.png "Screenshot of the second release workflow for 1.3.0-incubating") @@ -90,22 +94,11 @@ Like for the other workflow runs, the run details page contains a recap of the m ![Screenshot of a detailed run of the second release workflow for 1.3.0-incubating](/img/release-guides/github-workflow-2-detail.png "Screenshot of a detailed run of the second release workflow for 1.3.0-incubating") ## RC≥1 only - Update release branch and create tag -If the first release candidate is rejected, additional code changes may be needed. These steps have not been automated yet. +If the first release candidate is rejected, additional code changes may be needed. Each code change that should be added to the release branch must be cherry-picked from the main branch and proposed in a dedicated pull request. The pull request must be reviewed and approved before being merged. This step is mandatory so that Github runs the CI checks. The subsequent workflows will verify that those checks passed. -Once the pull requests have been merged, create a new `apache-polaris-[major].[minor].[patch]-incubating-rc[N]` tag. The commands below assume that the `apache/polaris` Git repository corresponds to the `apache` remote. - -```bash -# Ensure you're on the correct release branch and have the latest changes -git fetch apache -git checkout release/[major].[minor].x -git pull apache release/[major].[minor].x - -# Create and push the release candidate tag -git tag -a apache-polaris-[major].[minor].[patch]-incubating-rc[N] -m "Apache Polaris [major].[minor].[patch] (incubating) release candidate [N]" -git push apache apache-polaris-[major].[minor].[patch]-incubating-rc[N] -``` +Once the pull requests have been merged, run the second workflow again to create a new RC tag. The workflow will automatically determine the next RC number. ## Build and publish release artifacts The third Github workflow to run is [`Release - 3 - Build and publish release artifacts`](https://github.com/apache/polaris/actions/workflows/release-3-build-and-publish-artifacts.yml). This workflow will: @@ -118,7 +111,7 @@ The third Github workflow to run is [`Release - 3 - Build and publish release ar * Create signature and checksum for all package files * Copy package files to Apache dist dev repository -This workflow can only be run from a `release/x.y.z` branch. Selecting any other branch in the Github Actions UI will result in a failure. +This workflow must be run from an RC tag (e.g., `apache-polaris-1.3.0-incubating-rc0`). Select the tag from the `Use workflow from` dropdown in the Github Actions UI. Selecting any other reference in the Github Actions UI will result in a failure. ![Screenshot of the third release workflow for 1.3.0-incubating](/img/release-guides/github-workflow-3.png "Screenshot of the third release workflow for 1.3.0-incubating") @@ -129,7 +122,7 @@ The last step for a release candidate is to create a VOTE thread on the dev mail Recommended title subject: ``` -[VOTE] Release Apache Polaris x.y.z (rci) +[VOTE] Release Apache Polaris [major].[minor].[patch] (rc[N]) ``` Recommended content: @@ -137,18 +130,18 @@ Recommended content: ``` Hi everyone, -I propose that we release the following RC as the official Apache Polaris x.y.z +I propose that we release the following RC as the official Apache Polaris [major].[minor].[patch] release. -* This corresponds to the tag: apache-polaris-x.y.z-incubating-rci -* https://github.com/apache/polaris/commits/apache-polaris-x.y.z-incubating-rci +* This corresponds to the tag: apache-polaris-[major].[minor].[patch]-incubating-rc[N] +* https://github.com/apache/polaris/commits/apache-polaris-[major].[minor].[patch]-incubating-rc[N] * https://github.com/apache/polaris/tree/ The release tarball, signature, and checksums are here: -* https://dist.apache.org/repos/dist/dev/incubator/polaris/x.y.z-incubating +* https://dist.apache.org/repos/dist/dev/incubator/polaris/[major].[minor].[patch]-incubating Helm charts are available on: -* https://dist.apache.org/repos/dist/dev/incubator/polaris/helm-chart/x.y.z-incubating/ +* https://dist.apache.org/repos/dist/dev/incubator/polaris/helm-chart/[major].[minor].[patch]-incubating/ NB: you have to build the Docker images locally in order to test Helm charts. @@ -164,7 +157,7 @@ https://polaris.apache.org/community/release-verify/. Please vote in the next 72 hours. -[ ] +1 Release this as Apache polaris x.y.z +[ ] +1 Release this as Apache polaris [major].[minor].[patch] [ ] +0 [ ] -1 Do not release this because... @@ -183,13 +176,13 @@ The next steps depend on the vote result. When a release candidate is rejected, reply with the vote result: ``` -[RESULT][VOTE] Release Apache Polaris x.y.z (rci) +[RESULT][VOTE] Release Apache Polaris [major].[minor].[patch] (rc[N]) ``` ``` Hello everyone, -Thanks to all who participated in the vote for Release Apache Polaris x.y.z (rci). +Thanks to all who participated in the vote for Release Apache Polaris [major].[minor].[patch] (rc[N]). The vote failed due to [reason]. @@ -202,11 +195,11 @@ Thanks, When the release candidate vote passes, send a new e-mail with the vote result: ``` -[RESULT][VOTE] Release Apache Polaris x.y.z (rci) +[RESULT][VOTE] Release Apache Polaris [major].[minor].[patch] (rc[N]) ``` ``` -Thanks everyone who participated in the vote for Release Apache Polaris x.y.z (rci). +Thanks everyone who participated in the vote for Release Apache Polaris [major].[minor].[patch] (rc[N]). The vote result is: @@ -223,13 +216,13 @@ As Polaris is an Apache Incubator project, you now have to start a new vote on t You have to send this email to general@incubator.apache.org: ``` -[VOTE] Release Apache Polaris x.y.z (rci) +[VOTE] Release Apache Polaris [major].[minor].[patch] (rc[N]) ``` ``` Hello everyone, -The Apache Polaris community has voted and approved the release of Apache Polaris x.y.z (rci). +The Apache Polaris community has voted and approved the release of Apache Polaris [major].[minor].[patch] (rc[N]). We now kindly request the IPMC members review and vote for this release. Polaris community vote thread: @@ -238,12 +231,12 @@ Polaris community vote thread: Vote result thread: * https://lists.apache.org/thread/ -* This corresponds to the tag: apache-polaris-x.y.z-rci -* https://github.com/apache/polaris/commits/apache-polaris-x.y.z-rci +* This corresponds to the tag: apache-polaris-[major].[minor].[patch]-incubating-rc[N] +* https://github.com/apache/polaris/commits/apache-polaris-[major].[minor].[patch]-rc[N] * https://github.com/apache/polaris/tree/ The release tarball, signature, and checksums are here: -* https://dist.apache.org/repos/dist/dev/incubator/polaris/x.y.z +* https://dist.apache.org/repos/dist/dev/incubator/polaris/[major].[minor].[patch] Helm charts are available on: * https://dist.apache.org/repos/dist/dev/incubator/polaris/helm-chart @@ -286,7 +279,7 @@ When a release candidate is rejected, reply in the same thread with the vote res ``` Hello everyone, -Thanks to all who participated in the vote for Release Apache Polaris x.y.z (rci). +Thanks to all who participated in the vote for Release Apache Polaris [major].[minor].[patch] (rc[N]). The vote failed due to [reason]. @@ -297,13 +290,13 @@ Thanks, When the release candidate vote passes, send a new e-mail with the vote result: ``` -[RESULT][VOTE] Release Apache Polaris x.y.z (rci) +[RESULT][VOTE] Release Apache Polaris [major].[minor].[patch] (rc[N]) ``` ``` Hello everyone, -The vote to release Apache Polaris x.y.z (rci) has passed with [N] +1 binding and [M] +1 non-binding votes. +The vote to release Apache Polaris [major].[minor].[patch] (rc[N]) has passed with [N] +1 binding and [M] +1 non-binding votes. Binding +1 votes: * [NAME] @@ -322,6 +315,7 @@ We will proceed with publishing the approved artifacts and sending out the annou ## Publish the release The final workflow to run is [`Release - 4 - Publish Release After Vote Success`](https://github.com/apache/polaris/actions/workflows/release-4-publish-release.yml). This workflow will: +* Verify that the release branch HEAD matches the last RC tag * Copy artifacts from the dist dev to the dist release SVN repository * Update the Helm index in dist release repository accordingly * Create a final release tag @@ -329,7 +323,7 @@ The final workflow to run is [`Release - 4 - Publish Release After Vote Success` * Create a Github release with the release artifacts * Release the candidate repository on Apache Nexus -This workflow can only be run from the `release/x.y.z` branch for which a vote has passed. It also requires the Nexus staging repository id (`orgapachepolaris-`) that was created by the previous workflow. +This workflow can only be run from the `release/[major].[minor].x` branch for which a vote has passed. The workflow verifies that no commits have been added to the release branch since the last RC was created. It also requires the Nexus staging repository id (`orgapachepolaris-`) that was created by the previous workflow. ![Screenshot of the fourth release workflow for 1.3.0-incubating](/img/release-guides/github-workflow-4.png "Screenshot of the fourth release workflow for 1.3.0-incubating") diff --git a/site/static/img/release-guides/github-workflow-3.png b/site/static/img/release-guides/github-workflow-3.png index d8e0e1c0e5..3385f4b026 100644 Binary files a/site/static/img/release-guides/github-workflow-3.png and b/site/static/img/release-guides/github-workflow-3.png differ