From f1f41ff2611fb3b45d527a8e0af2cecbbfddf68d Mon Sep 17 00:00:00 2001 From: Trung Date: Thu, 19 Oct 2023 16:03:59 -0700 Subject: [PATCH] Move cherry-pick automation to ADO (#6387) Co-authored-by: Pawel Winogrodzki --- .github/CODEOWNERS | 2 +- .github/workflows/cherry-pick.yml | 129 --------------------------- .pipelines/cherrypick/CherryPick.yml | 32 +++++++ toolkit/scripts/cherry_pick.sh | 85 ------------------ 4 files changed, 33 insertions(+), 215 deletions(-) delete mode 100644 .github/workflows/cherry-pick.yml create mode 100644 .pipelines/cherrypick/CherryPick.yml delete mode 100755 toolkit/scripts/cherry_pick.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4ae51c8a8b..6b77276fac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,7 +5,7 @@ /.github/CODEOWNERS @microsoft/cbl-mariner-admins # Modifications to the build pipelines require admin approval. -/pipelines/* @microsoft/cbl-mariner-admins +/.pipelines/* @microsoft/cbl-mariner-admins # Modifications to the CredScan exceptions require admin approval. /.config/CredScanSuppressions.json @microsoft/cbl-mariner-admins diff --git a/.github/workflows/cherry-pick.yml b/.github/workflows/cherry-pick.yml deleted file mode 100644 index b95012c0ce..0000000000 --- a/.github/workflows/cherry-pick.yml +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# This action cherry-picks CVE fix commits from fast-track branches to corresponding -# development branches for each version of Mariner. By default, the workflow is run -# whenever a CVE fix PR to a fast-track branch is merged. Additional target branches -# for cherry-pick can be specified by adding a comment to the original PR with the -# following syntax: -# /cherry-pick -# Note the list of target branches is space-separated, and the commenter must be a -# member or owner of the repository for the action to work. -# After cherry-pick succeeds for a branch, a new PR will be created to merge the commit -# to that target branch, and a label will be added to the original PR to indicate that -# the commit has been cherry-picked to that branch. -name: Cherry pick commits from PR -on: - pull_request: - types: - - closed - branches: - - 'fasttrack/*' - -permissions: - contents: read - pull-requests: read -defaults: - run: - shell: bash - -env: - CHERRY_PICK_BRANCH_MAPPING: '{"fasttrack/2.0": "main"}' - -jobs: - # Scans the PR for cherry pick comments, in addition to the default cherry pick target - collect_target_branches: - if: github.event.pull_request.merged == true - runs-on: ubuntu-latest - name: Collect target branches from Pull Request - outputs: - cherry_pick_target_branches: ${{ steps.target_branches.outputs.cherry_pick_target_branches }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - # Collect target branches in a list. The list includes: - # - default target branch - # - any branch specified in PR comments with the syntax - # /cherry-pick - # The final list will only contain unique branches. - # The jq filter does the following from a JSON list of comments - # [{"author": "a", "authorAssociation": "MEMBER", "body": "hello"}, - # {"author": "c", "authorAssociation": "CONTRIBUTOR", "body": "hi"}, - # {"author": "a", "authorAssociation": "MEMBER", "body": "/cherry-pick a b c"}, - # {"author": "b", "authorAssociation": "MEMBER", "body": "/cherry-pick a b"}] - # - Select only comments where the author is a member or owner of the repository - # - Collect all comments body to a list of string - # - Select only comments that starts with "/cherry-pick " - # - Remove the "/cherry-pick " prefix from each comment - # ["a b c", "a b"] - # - Split the remaining content of each comment by whitespace into a list - # [["a", "b", "c"], ["a", "b"]] - # - Flatten the list - # ["a", "b", "c", "a", "b"] - # - Add the default target branch to the list - # ["a", "b", "c", "a", "b", "default-target"] - # - Remove all duplicate values - # ["a", "b", "c", "default-target"] - - name: Create list of target branches - id: target_branches - run: | - cherry_pick_branches_json=$(gh pr view ${{ github.event.number }} \ - --repo ${{ github.repository }} \ - --json comments \ - --jq '.comments - | map(select(.authorAssociation == "MEMBER" or .authorAssociation == "OWNER") - | .body - | select(startswith("/cherry-pick ")) - | ltrimstr("/cherry-pick ") - | split(" ")) - | flatten - | . += ["${{ fromJSON(env.CHERRY_PICK_BRANCH_MAPPING)[github.base_ref] }}"] - | unique') - branches_list=$(echo $cherry_pick_branches_json | jq -r @sh | tr -d "'") - echo "cherry_pick_target_branches=$branches_list" >> $GITHUB_OUTPUT - - # Actual cherry-pick work for each target branch - cherry_pick_commit: - needs: collect_target_branches - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - name: Cherry pick - steps: - - name: Workflow trigger checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.base_ref }} - fetch-depth: 0 - - - name: Configure local git repo - run: | - git config --local user.email "cblmargh@microsoft.com" - git config --local user.name "CBL-Mariner Servicing Account" - - # Run cherry-pick for each target branch and create new PR for it. If cherry-pick fails, - # output conflicts to the action logs and notify in the original PR. We want to run the - # cherry-pick script for all target branch, even if one fails. This step will succeed if - # all cherry-picks succeed. - - name: Run cherry-pick action - run: | - cp toolkit/scripts/cherry_pick.sh ${{ runner.temp }}/cherry_pick.sh - - for target_branch in ${{ needs.collect_target_branches.outputs.cherry_pick_target_branches }}; do - if ! ${{ runner.temp }}/cherry_pick.sh \ - -r "${{ github.repository }}" \ - -p "${{ github.event.pull_request.number }}" \ - -t "$target_branch" \ - -w $RUN_URL; then - failed=1 - fi - echo "================================================================================" - done - if [[ $failed == 1 ]]; then - echo "Cherry-pick failed for at least 1 target branch" - exit 1 - fi - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.pipelines/cherrypick/CherryPick.yml b/.pipelines/cherrypick/CherryPick.yml new file mode 100644 index 0000000000..70b883b2f4 --- /dev/null +++ b/.pipelines/cherrypick/CherryPick.yml @@ -0,0 +1,32 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +name: Cherry Pick fast-track commits from PR + +trigger: +- fasttrack/* +pr: none + +variables: + - group: "Agent pools (DEV)" + +resources: + repositories: + - repository: CBL-Mariner-Pipelines + type: git + name: mariner/CBL-Mariner-Pipelines + +jobs: + - job: 'CherryPick' + pool: + type: linux + isCustom: true + name: "$(DEV_AMD64_Ubuntu_Managed)" + timeoutInMinutes: 15 + displayName: 'Cherry-pick commit' + + steps: + - template: spec-auto-patch-update/pipelines/CherryPick.yml@CBL-Mariner-Pipelines + parameters: + commitId: $(Build.SourceVersion) + sourceBranch: $(Build.SourceBranch) diff --git a/toolkit/scripts/cherry_pick.sh b/toolkit/scripts/cherry_pick.sh deleted file mode 100755 index 694cf7057e..0000000000 --- a/toolkit/scripts/cherry_pick.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -set -e - -function help { - echo "Cherry-pick commit to a specific branch and create Github PR" - echo "Usage:" - echo "[MANDATORY] -r REPOSITORY -> name of the repository in the format /" - echo "[MANDATORY] -p PR_NUMBER -> number of the PR to cherry-pick commit from" - echo "[MANDATORY] -t TARGET_BRANCH -> target branch to cherry-pick commit to" - echo "[MANDATORY] -w WORKFLOW_RUN_URL -> URL of the workflow run that triggers this script" -} - -function cherry_pick_from_pull_request { - repo=$1 - pr_number=$2 - target_branch=$3 - workflow_run_url=$4 - - # Collect merge commit hash and title from the original PR - commit_hash=$(gh pr view $pr_number --repo $repo --json mergeCommit --jq '.mergeCommit.oid') - original_pr_title=$(gh pr view $pr_number --repo $repo --json title --jq '.title') - tmp_branch="cherry-pick-$target_branch-$commit_hash" - - echo "Cherry picking commit ($commit_hash) to target branch ($target_branch)" - - # reset the current working tree to clean state - git reset --hard - git clean -df - git checkout -- . - - # create a temporary branch from target branch to perform cherry pick - git fetch --all - git checkout -b "$tmp_branch" origin/"$target_branch" - - if ! git cherry-pick -x "$commit_hash"; then - echo "Cherry pick failed. Displaying conflicts below" - git diff --diff-filter=U - gh pr comment "$pr_number" \ - --repo "$repo" \ - --body "Cherry-pick failed for branch \`$target_branch\`. See run logs for more details: $workflow_run_url" - exit 1 - fi - - echo "Pushing to remote" - git push -u origin "$tmp_branch" - echo "Done pushing to remote" - new_pr=$(gh pr create \ - -B "$target_branch" \ - -H "$tmp_branch" \ - --repo "$repo" \ - --title "[AUTO-CHERRY-PICK] $original_pr_title - branch $target_branch" \ - --body "This is an auto-generated pull request to cherry pick commit $commit_hash to $target_branch. Original PR: #$pr_number") - gh pr comment "$pr_number" \ - --repo "$repo" \ - --body "Cherry-pick succeeded for branch \`$target_branch\`. See pull request #$new_pr" - gh pr edit "$pr_number" \ - --repo "$repo" \ - --add-label "cherry_pick-$target_branch" -} - -repo= -pr_number= -target_branch= -workflow_run_url= - -while getopts "r:p:t:w:" opt; do - case ${opt} in - r ) repo="$OPTARG" ;; - p ) pr_number="$OPTARG" ;; - t ) target_branch="$OPTARG" ;; - w ) workflow_run_url="${OPTARG,,}" ;; - ? ) echo -e "ERROR: Invalid option.\n\n"; help; exit 1 ;; - esac -done - -if [[ -z "$repo" ]] || [[ -z "$pr_number" ]] || [[ -z "$target_branch" ]] || [[ -z "$workflow_run_url" ]] ; then - echo "Error: missing required arguments" - help - exit 1 -fi - -cherry_pick_from_pull_request "$repo" "$pr_number" "$target_branch" "$workflow_run_url"