From 628bdd90012bdd58b894b4d0f811c7bd899c3cae Mon Sep 17 00:00:00 2001
From: Egor_P <egor@parity.io>
Date: Thu, 20 Mar 2025 12:50:34 +0100
Subject: [PATCH] [CI/CD] Refactor backports flow so that it can determine
 automatically where to do a backport based on labels (#7976)

This PR changes the command-backport.yml flow so that the branch names
are not hardcoded in the pipeline file but will be parsed from the
labels.
The idea is that there are going to be a label representing each stable
branch:
- A4-backport-stable2407
- A4-backport-stable2409
- A4-backport-stable2412
- A4-backport-stable2503

If the backport is needed to any of those branches or to all of them,
the corresponding label can be set on the PR and the branch name will be
parsed from it.
The labels need to be created in the repo and adjusted as soon as there
is a new release appears or an old one disappears.
---
 .github/scripts/common/lib.sh          | 20 ++++++++++
 .github/workflows/command-backport.yml | 53 +++++++++++++++++++++-----
 docs/BACKPORT.md                       |  6 ++-
 3 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh
index 11436f8f33a..97530bfdc90 100755
--- a/.github/scripts/common/lib.sh
+++ b/.github/scripts/common/lib.sh
@@ -508,6 +508,7 @@ validate_stable_tag() {
 }
 
 # Prepare docker stable tag form the polkadot stable tag
+#
 # input: tag (polkaodot-stableYYMM(-X) or polkadot-stableYYMM(-X)-rcX)
 # output: stableYYMM(-X) or stableYYMM(-X)-rcX
 prepare_docker_stable_tag() {
@@ -519,3 +520,22 @@ prepare_docker_stable_tag() {
       exit 1
   fi
 }
+
+# Parse names of the branches from the github labels based on the pattern
+#
+# input: labels (array of lables like ("A3-backport" "RO-silent" "A4-backport-stable2407" "A4-backport-stable2503"))
+# output: BRANCHES (array of the branch names)
+parse_branch_names_from_backport_labels() {
+  labels="$1"
+  BRANCHES=""
+
+  for label in $labels; do
+    if [[ "$label" =~ ^A4-backport-stable[0-9]{4}$ ]]; then
+      branch_name=$(sed 's/A4-backport-//' <<< "$label")
+      BRANCHES+=" ${branch_name}"
+    fi
+  done
+
+  BRANCHES=$(echo "$BRANCHES" | sed 's/^ *//')
+  echo "$BRANCHES"
+}
diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml
index 67de5418434..2df80bc54a1 100644
--- a/.github/workflows/command-backport.yml
+++ b/.github/workflows/command-backport.yml
@@ -13,9 +13,13 @@ permissions:
   actions: write # It may have to backport changes to the CI as well.
 
 jobs:
-  backport:
-    name: Backport pull request
+  check-labels:
     runs-on: ubuntu-latest
+    env:
+      GH_TOKEN: ${{ github.token }}
+    outputs:
+      LABELS: ${{ steps.check_labels.outputs.LABELS}}
+      found: ${{ steps.check_labels.outputs.found}}
 
     # The 'github.event.pull_request.merged' ensures that it got into master:
     if: >
@@ -23,24 +27,54 @@ jobs:
       (
         github.event_name == 'pull_request_target' &&
         github.event.pull_request.merged &&
-        github.event.pull_request.base.ref == 'master' &&
-        contains(github.event.pull_request.labels.*.name, 'A4-needs-backport')
+        github.event.pull_request.base.ref == 'master'
       )
     steps:
       - uses: actions/checkout@v4
 
+      - name: Check for backport labels
+        id: check_labels
+        run: |
+          LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name')
+
+          if echo "$LABELS" | grep -q '^A4-backport-stable'; then
+            echo "found=true" >> $GITHUB_OUTPUT
+            readarray -t labels_array <<< "$LABELS"
+            echo "LABELS=${labels_array[@]}" >> $GITHUB_OUTPUT
+          else
+            echo "found=false" >> $GITHUB_OUTPUT
+          fi
+
+
+  backport:
+    name: Backport pull request
+    runs-on: ubuntu-latest
+    needs: [ check-labels ]
+    if: ${{ needs.check-labels.outputs.found  == 'true' }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Get branches to backport to
+        id: branches
+        run: |
+          . ./.github/scripts/common/lib.sh
+
+          LABELS="${{ needs.check-labels.outputs.LABELS }}"
+          BACKPORT_BRANCHES=$(parse_branch_names_from_backport_labels "$LABELS")
+          echo "BACKPORT_BRANCHES=${BACKPORT_BRANCHES}" >> $GITHUB_OUTPUT
+
       - name: Generate token
         id: generate_token
         uses: actions/create-github-app-token@v1
         with:
-          app_id: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_ID }}
-          private_key: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_PRIVATE_KEY }}
+          app-id: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_ID }}
+          private-key: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_PRIVATE_KEY }}
 
       - name: Create backport pull requests
         uses: korthout/backport-action@v3
         id: backport
         with:
-          target_branches: stable2407 stable2409 stable2412 stable2503
+          target_branches: ${{ steps.branches.outputs.BACKPORT_BRANCHES }}
           merge_commits: skip
           github_token: ${{ steps.generate_token.outputs.token }}
           pull_description: |
@@ -59,6 +93,7 @@ jobs:
               "conflict_resolution": "draft_commit_conflicts"
             }
           copy_assignees: true
+          label_pattern: ^A4-backport-stable
 
       - name: Label Backports
         if: ${{ steps.backport.outputs.created_pull_numbers != '' }}
@@ -86,11 +121,11 @@ jobs:
             const reviewer = '${{ github.event.pull_request.user.login }}';
 
             for (const pullNumber of pullNumbers) {
-              await github.pulls.requestReviewers({
+              await github.rest.pulls.requestReviewers({
                 owner: context.repo.owner,
                 repo: context.repo.repo,
                 pull_number: parseInt(pullNumber),
-                reviewers: [ reviewer ]
+                reviewers: [reviewer]
               });
               console.log(`Requested review from ${reviewer} for PR #${pullNumber}`);
             }
diff --git a/docs/BACKPORT.md b/docs/BACKPORT.md
index 0b4a97e6f66..50de5a9b34a 100644
--- a/docs/BACKPORT.md
+++ b/docs/BACKPORT.md
@@ -6,7 +6,11 @@ Backports should only be used to fix bugs or security issues - never to introduc
 ## Steps
 
 1. Fix a bug through a PR that targets `master`.
-2. Add label `A4-needs-backport` to the PR.
+2. Add label related to the branch to wich to backport changes to the PR.
+    - `A4-backport-stable2407`
+    - `A4-backport-stable2409`
+    - `A4-backport-stable2412`
+    - `A4-backport-stable2503`
 3. Merge the PR into `master`.
 4. Wait for the bot to open the backport PR.
 5. Ensure the change is audited or does not need audit.
-- 
GitLab