mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-02-25 05:02:44 -05:00
Added backport.yml to automate backport process (#1452)
Signed-off-by: siddharthasonker95 <158144589+siddharthasonker95@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
9d9a7aab06
commit
bb1176d3a6
21
.github/scripts/backport/README.md
vendored
Normal file
21
.github/scripts/backport/README.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Backport Merged Pull Request
|
||||
|
||||
## Overview
|
||||
|
||||
This feature automates the process of creating pull requests for backporting merged changes to a designated target branch.
|
||||
|
||||
### How to Use
|
||||
|
||||
1. **Label Your Pull Request**: To initiate a backport, simply add a label to your pull request in the format `backport <target_branch>`, where `<target_branch>` is the branch you want to backport changes to (eg. `backport v1.x`). The backport labels can be added to both open and closed pull requests.
|
||||
|
||||
2. **Automatic Backport Pull Request Creation**: After the pull request is merged and closed, or if the backport label is added to an already closed pull request, backport Github Action will execute the `.github/scripts/backport/main.sh` script, which will automatically create a new pull request with the changes backported to the specified `<target_branch>`. A comment linking to the new Backport Pull Request will be added to the original pull request for easy navigation.
|
||||
|
||||
### Handling Merge Conflicts
|
||||
|
||||
- In some cases, the automatic backport process may encounter merge conflicts that cannot be resolved automatically.
|
||||
- If this occurs, a comment will be posted on the original pull request indicating the conflict and providing the commit SHA causing the issue.
|
||||
- When a merge conflict arises, you'll need to manually backport your changes to the target branch and resolve any conflicts as part of the manual pull request process. [Please see the contributing guide for more details](https://github.com/opentofu/opentofu/blob/main/CONTRIBUTING.md#backporting)
|
||||
|
||||
### Note
|
||||
|
||||
- **Use Clear Labels**: Ensure the `backport <target_branch>` label is correctly formatted.
|
||||
237
.github/scripts/backport/main.sh
vendored
Normal file
237
.github/scripts/backport/main.sh
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
# Copyright (c) The OpenTofu Authors
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
# Copyright (c) 2023 HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -eou pipefail
|
||||
|
||||
# Color codes for logs
|
||||
COLOR_RESET="\033[0m"
|
||||
COLOR_GREEN="\033[32m"
|
||||
COLOR_RED="\033[31m"
|
||||
|
||||
# This function outputs error logs.
|
||||
log_error() {
|
||||
if [ -z "$1" ]; then
|
||||
echo -e "${COLOR_RED}Error: No error message provided.${COLOR_RESET}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${COLOR_RED}Error: $1${COLOR_RESET}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# This function outputs info logs.
|
||||
log_info() {
|
||||
if [ -z "$1" ]; then
|
||||
echo -e "${COLOR_RED}Error: No info message provided.${COLOR_RESET}"
|
||||
return
|
||||
fi
|
||||
echo -e "${COLOR_GREEN}$1${COLOR_RESET}"
|
||||
}
|
||||
|
||||
# This function sets up the committer identity.
|
||||
setup_committer_identity() {
|
||||
log_info "Setting up committer identity..."
|
||||
if ! git config user.email "noreply@github.com"; then
|
||||
log_error "Failed to set email."
|
||||
fi
|
||||
|
||||
if ! git config user.name "GitHub Actions"; then
|
||||
log_error "Failed to set a username."
|
||||
fi
|
||||
log_info "Successfully set the committer identity."
|
||||
}
|
||||
|
||||
# This function checks the GITHUB_TOKEN, gh auth and environment variables are set or not.
|
||||
validate_github_auth_and_env_vars() {
|
||||
# Check if GitHub token is set
|
||||
log_info "Checking if GITHUB_TOKEN is set..."
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
log_error "GitHub token is not available. Please ensure a secret named 'GITHUB_TOKEN' is defined."
|
||||
fi
|
||||
log_info "GITHUB_TOKEN is set successfully."
|
||||
|
||||
log_info "Checking if required environment variables are set..."
|
||||
if [ -z "$OWNER" ] || [ -z "$REPO" ] || [ -z "$PR_NUMBER" ] || [ -z "$ISSUE_NUMBER" ] || [ -z "$HEAD_BRANCH" ]; then
|
||||
log_error "One or more required environment variables (OWNER, REPO, PR_NUMBER, ISSUE_NUMBER, HEAD_BRANCH) are not set."
|
||||
fi
|
||||
log_info "All the environment variables are set successfully."
|
||||
|
||||
# Check if gh is logged in
|
||||
log_info "Checking if 'gh' auth is set..."
|
||||
if ! gh auth status >/dev/null 2>&1; then
|
||||
log_error "Authentication check for 'gh' failed."
|
||||
fi
|
||||
log_info "'gh' authentication is set up successfully."
|
||||
}
|
||||
|
||||
# This function cherry-picks the commits onto the new backport branch.
|
||||
cherry_pick_commits() {
|
||||
# Get the commit SHAs associated with the pull request
|
||||
log_info "Fetching commit SHAs from the pull request..."
|
||||
if ! commit_shas=$(gh api "/repos/$OWNER/$REPO/pulls/$PR_NUMBER/commits" --jq '.[].sha'); then
|
||||
log_error "Failed to fetch commit SHAs from the pull request #${PR_NUMBER}."
|
||||
fi
|
||||
|
||||
# Check if commit_shas is empty
|
||||
log_info "Checking if any commit SHAs were fetched..."
|
||||
if [ -z "$commit_shas" ]; then
|
||||
log_error "No commit SHAs were found in the pull request #${PR_NUMBER}."
|
||||
fi
|
||||
log_info "Commit SHAs were successfully fetched."
|
||||
|
||||
while IFS= read -r commit_sha; do
|
||||
log_info "Fetching the commit id: '${commit_sha}'..."
|
||||
if ! git fetch origin "$commit_sha"; then
|
||||
log_error "Failed to fetch the commit id: '${commit_sha}'"
|
||||
fi
|
||||
log_info "Successfully fetched the commit id: '${commit_sha}'"
|
||||
|
||||
log_info "Cherry-picking the commit id: '${commit_sha}'..."
|
||||
if git cherry-pick "$commit_sha" ; then
|
||||
log_info "Successfully cherry-picked the commit id: '${commit_sha}'"
|
||||
else
|
||||
# Add a failure comment to the pull request
|
||||
failureComment="Error: Cherry-pick commit '$commit_sha' failed during the backport process to '$1'. Please resolve any conflicts and manually apply the changes."
|
||||
if ! gh pr comment "$PR_NUMBER" --body "$failureComment"; then
|
||||
log_error "Failed to add failure comment to the pull request #${PR_NUMBER}."
|
||||
fi
|
||||
log_info "Successfully added failure comment to the pull request."
|
||||
|
||||
log_error "Failed to cherry-pick commit '${commit_sha}'. Added failure comment to the pull request."
|
||||
fi
|
||||
done <<< "$commit_shas"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# This function creates the pull request.
|
||||
create_pull_request() {
|
||||
local target_branch=$1
|
||||
local backport_branch=$2
|
||||
title="Backport PR #$ISSUE_NUMBER: '$HEAD_BRANCH' to '$target_branch'"
|
||||
body="This pull request backports changes from branch '$HEAD_BRANCH' to branch '$target_branch'."
|
||||
if ! url=$(gh pr create --title "$title" --body "$body" --base "$target_branch" --head "$backport_branch"); then
|
||||
return 1
|
||||
fi
|
||||
echo "${url}"
|
||||
}
|
||||
|
||||
# This function performs all the operations required to backport a pull request to a target branch.
|
||||
backport_changes() {
|
||||
local target_branch=$1
|
||||
local backport_branch=$2
|
||||
|
||||
# Fetch and checkout the target branch.
|
||||
log_info "Fetching branch '$target_branch'..."
|
||||
if ! git fetch origin "$target_branch"; then
|
||||
log_error "Failed to fetch branch: '$target_branch'."
|
||||
fi
|
||||
log_info "Successfully fetched branch: '$target_branch'."
|
||||
|
||||
log_info "Checking out branch '$target_branch'..."
|
||||
if ! git checkout "$target_branch"; then
|
||||
log_error "Failed to checkout branch: '$target_branch'"
|
||||
fi
|
||||
log_info "Successfully checked out branch: '$target_branch'."
|
||||
|
||||
# Check whether the new backport branch already exists. If it does, delete the branch to remove any unnecessary changes.
|
||||
log_info "Checking if new backport branch already exists '$backport_branch'..."
|
||||
if git ls-remote --exit-code --heads origin "$backport_branch" >/dev/null 2>&1; then
|
||||
if git push origin --delete "$backport_branch" >/dev/null 2>&1; then
|
||||
log_info "Successfully deleted existing backport branch: '$backport_branch'"
|
||||
else
|
||||
log_error "Failed to delete existing backport branch: '$backport_branch'"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Checking out new backport branch
|
||||
log_info "Checking out new backport branch '$backport_branch'..."
|
||||
if ! git checkout -b "$backport_branch"; then
|
||||
log_error "Failed to create new backport branch: '$backport_branch'"
|
||||
fi
|
||||
log_info "Successfully checked out new backport branch: '$backport_branch'."
|
||||
|
||||
# Cherry-pick commits
|
||||
log_info "Starting the cherry-pick process for the pull request #${PR_NUMBER}..."
|
||||
if ! cherry_pick_commits "$target_branch"; then
|
||||
log_error "Failed to cherry-pick commits for the pull request."
|
||||
fi
|
||||
log_info "Cherry-pick completed successfully for the pull request #${PR_NUMBER}."
|
||||
|
||||
# Push final changes to the backport branch
|
||||
log_info "Pushing changes to the branch: '$backport_branch'..."
|
||||
if ! git push origin "$backport_branch"; then
|
||||
log_error "Failed to push changes to the branch: '$backport_branch'."
|
||||
fi
|
||||
log_info "Successfully pushed changes to the branch: '$backport_branch'"
|
||||
|
||||
# Create the pull request
|
||||
log_info "Creating pull request between '$target_branch' and '$backport_branch'..."
|
||||
if ! pull_request_url=$(create_pull_request "$target_branch" "$backport_branch"); then
|
||||
log_error "Failed to create the pull request."
|
||||
fi
|
||||
log_info "Pull request created successfully. You can view the pull request at: ${pull_request_url}."
|
||||
|
||||
# Add comment to the pull request
|
||||
comment="This pull request has been successfully backported. Link to the new pull request: ${pull_request_url}."
|
||||
log_info "Adding a comment to the original pull request #${PR_NUMBER} created between '$target_branch' and '$backport_branch'..."
|
||||
if ! gh pr comment "$PR_NUMBER" --body "$comment"; then
|
||||
log_error "Failed to add a comment to the original pull request #${PR_NUMBER} created between '$target_branch' and '$backport_branch'."
|
||||
fi
|
||||
log_info "Successfully added a comment to the original pull request #${PR_NUMBER} created between '$target_branch' and '$backport_branch'."
|
||||
}
|
||||
|
||||
main() {
|
||||
log_info "Starting the process of creating pull requests for backporting merged changes to a designated target branch."
|
||||
|
||||
# Set up committer identity
|
||||
setup_committer_identity
|
||||
# Validate GitHub token, gh auth, and environment variables
|
||||
validate_github_auth_and_env_vars
|
||||
|
||||
# Retrieve the list of branches for backporting the pull request changes.
|
||||
log_info "Starting the process of identifying a list of branches where pull request changes will be backported to... "
|
||||
target_branches=()
|
||||
readarray -t labelArray < <(echo "$LABELS" | jq -r '.[]')
|
||||
for label in "${labelArray[@]}"; do
|
||||
if [[ $label == "backport"* ]]; then
|
||||
target_branches+=("${label#backport }")
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Validate all the target branches to which the pull request changes will be backported..."
|
||||
if [ ${#target_branches[@]} -eq 0 ]; then
|
||||
log_info "No target branches found. The backport label is not found in any of the labels on the pull request. Skipping the backport process."
|
||||
exit 0
|
||||
fi
|
||||
log_info "Successfully found the following target branches: ${target_branches[*]}"
|
||||
|
||||
# Iterate over each target branch and attempt to backport the changes
|
||||
for branch in "${target_branches[@]}"; do
|
||||
(
|
||||
new_backport_branch="backport/$branch/$ISSUE_NUMBER"
|
||||
|
||||
log_info "Checking if pull request already exists between the branch '$branch' and '$new_backport_branch'."
|
||||
pull_request_list=$(gh pr list --base="$branch" --head="$new_backport_branch" --json number,title)
|
||||
|
||||
# If a pull request already exists between branch and new_backport_branch,
|
||||
# then skip backporting the pull request changes.
|
||||
if [ "$(echo "$pull_request_list" | jq length)" -eq 0 ]; then
|
||||
log_info "Starting the backport process for branch: $branch."
|
||||
if ! backport_changes "$branch" "$new_backport_branch"; then
|
||||
log_error "Failed to backport changes to the branch: $branch."
|
||||
fi
|
||||
log_info "Successfully backported the pull request changes to the branch '$branch'."
|
||||
else
|
||||
log_info "Pull requests already exist between the branch '$branch' and '$new_backport_branch'. Skipping backport for '$branch'"
|
||||
fi
|
||||
)
|
||||
done
|
||||
|
||||
log_info "Successfully, completed the backporting for the pull request #${PR_NUMBER}."
|
||||
}
|
||||
|
||||
main
|
||||
182
.github/scripts/backport/manual_test.md
vendored
Normal file
182
.github/scripts/backport/manual_test.md
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
# Manual Test Cases #
|
||||
|
||||
## Successful Test Cases ##
|
||||
|
||||
**Test Case 1**: Valid backport pull request with single backport label.
|
||||
|
||||
**Objective**: Verify that the script backports a valid pull request without issues after adding a backport label.
|
||||
|
||||
**Steps**:
|
||||
- Add a backport label to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x).
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Info logs confirming the successful backporting process are displayed, indicating the completion of backporting to *<target_branch>*.
|
||||
- For eg: Successfully backported the pull request changes to the branch *<target_branch>*.
|
||||
- The `Backport Merged Pull Request` workflow completes without encountering any errors.
|
||||
- A new pull request is created successfully, incorporating only the changes from the labeled pull request that need to be backported.
|
||||
- Format: Backport PR *#<pull_request_number>*: *<source_branch>* to *<target_branch>*.
|
||||
- A comment is added to the original pull request, indicating the completion of the backporting process.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 2**: Valid backport pull request with multiple backport label.
|
||||
|
||||
**Objective**: Verify that the script backports a valid pull request without issues after adding multiple backport labels.
|
||||
|
||||
**Steps**:
|
||||
- Add multiple backport labels to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x)
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Info log about the successful backporting to each *<target_branch>* can be found.
|
||||
- For eg: Successfully backported the pull request changes to the branch *<target_branch>*.
|
||||
- Info logs confirming the successful backporting process to all target branches are displayed, indicating the completion of backporting to *<target_branch>*.
|
||||
Successfully, completed the backporting for the pull request *#<pull_request_number>*.
|
||||
- The `Backport Merged Pull Request` workflow completes without encountering any errors.
|
||||
- A new pull request is created successfully for all the target branches, incorporating only the changes from the labeled pull request that need to be backported.
|
||||
- Format: Backport PR *#<pull_request_number>*: *<source_branch>* to *<target_branch>*.
|
||||
- A comment is added to the original pull request, indicating the completion of the backporting process.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 3**: No backport labels provided.
|
||||
|
||||
**Objective**: Verify that the script doesn’t fail when no backport label is provided.
|
||||
|
||||
**Steps**:
|
||||
- Ensure that the pull request is fully approved and merged into the main branch without adding any backport labels.
|
||||
- Backport labels should have a `backport` prefix followed by the *<target_branch>*.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- `Run custom bash script for backporting` step should be skipped in the workflow.
|
||||
- The script */backport/main.sh* should be skipped.
|
||||
- The `Backport Merged Pull Request` workflow completes without encountering any errors.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 4**: Pull request already exists.
|
||||
|
||||
**Objective**: Verify that the script successfully shows the info log that pull request already exists between new backport branch and target branch where original pull request changes will be backported to.
|
||||
|
||||
**Steps**:
|
||||
- The pull request should already exists between target branch and new backport branch.
|
||||
- Add a backport label to your original pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x).
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Info log mentioning details about skipping backport process can be found.
|
||||
- For eg: Pull requests already exist between the branch *<source_branch>* and *<target_branch>*. Skipping backport for *<target_branch>*.
|
||||
- The `Backport Merged Pull Request` workflow completes without encountering any errors.
|
||||
|
||||
|
||||
## Failure Test Cases ##
|
||||
|
||||
**Test Case 1**: Merge conflict while cherry picking.
|
||||
|
||||
**Objective**: Verify the script add a comment to the original pull request when merge conflict happens while cherry picking.
|
||||
|
||||
**Steps**:
|
||||
- Add multiple backport labels to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x)
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Error log**: Failed to cherry-pick commit <commit_id>. Added failure comment to the pull request.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Error logs detailing the backport failure are generated.
|
||||
- Backport Merged Pull Request workflow failed with errors.
|
||||
- A comment is added to the original pull request, indicating the completion of the backporting process.
|
||||
- Error: Cherry-pick commit <commit_id> failed during the backport process to *<target_branch>*. Please resolve any conflicts and manually apply the changes.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 2**: Unable to fetch commit id.
|
||||
|
||||
**Objective**: Verify that the script failed when commit id was not successfully fetched and proper error log should be generated to identify the issue.
|
||||
|
||||
**Steps**:
|
||||
- Add multiple backport labels to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x).
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Error log**: Failed to fetch the commit id: '<commit_id>.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Error log mentioning details about backport failure can be found.
|
||||
- Backport Merged Pull Request workflow failed with errors.
|
||||
- No comments will be added.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 3**: Unable to fetch target branch.
|
||||
|
||||
**Objective**: Verify that the script failed when the target branch was not found.
|
||||
|
||||
**Steps**:
|
||||
- Add a backport label to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x)
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Error log**: Failed to fetch branch: '*<target_branch>*’.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Error log mentioning details about backport failure can be found.
|
||||
- Backport Merged Pull Request workflow failed with errors.
|
||||
- No comments will be added.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 4**: Failed to create the pull request.
|
||||
|
||||
**Objective**: Verify that the script failed when the pull request was not created.
|
||||
|
||||
**Steps**:
|
||||
- Add a backport label to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x)
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Error log**: Failed to create the pull request.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Error log mentioning details about backport failure can be found.
|
||||
- Backport Merged Pull Request workflow failed with errors.
|
||||
- No comments will be added.
|
||||
|
||||
---
|
||||
|
||||
**Test Case 5**: Failed to add comment to the original pull request.
|
||||
|
||||
**Objective**: Verify that the script failed when the comment was not added to the original pull request.
|
||||
|
||||
**Steps**:
|
||||
- Add a backport label to your pull request.
|
||||
- Format backport *<target_branch>*, where *<target_branch>* is the branch you want to backport changes to (eg. backport v1.x)
|
||||
- Ensure that the pull request is fully approved and merged into the main branch.
|
||||
|
||||
**Error log**: Failed to add a comment to the original pull request *#<pull_request_number>* created between *<target_branch>* and <backport_branch>.
|
||||
|
||||
**Expected Results**:
|
||||
- The `Backport Merged Pull Request` workflow initiates automatically upon the addition of the backport label.
|
||||
- The script *backport/main.sh* begins backporting the changes from the labeled pull request to the specified target branch (*<target_branch>*).
|
||||
- Error log mentioning details about backport failure can be found.
|
||||
- Backport Merged Pull Request workflow failed with errors.
|
||||
- No comments will be added.
|
||||
Reference in New Issue
Block a user