Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e9cc7cb authored by Corey Bryant's avatar Corey Bryant
Browse files

Change uplift process to branch-specific pull requests

parent 7f64b282
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ name: PR Merged Actions
# Don't add any steps that act on external code.
on:
  pull_request_target:
    branches: [main]
    branches: [main, beta, release]
    types: [closed]

permissions:
@@ -32,16 +32,24 @@ jobs:
          PR_NUMBER: ${{  github.event.pull_request.number  }}
          GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
        run: |
          # The furthest open milestone in the future should be current main
          gh api repos/$GITHUB_REPOSITORY/milestones --jq '
                map(select(.state == "open" and .due_on != null))
          # There should be exactly 3 milestones open at all times
          TARGET_BRANCH="${{ github.base_ref }}"
          case "$TARGET_BRANCH" in
            main) milestone_index=0 ;;
            beta) milestone_index=1 ;;
            release) milestone_index=2 ;;
          esac
          echo "$milestone_index"
          gh api repos/$GITHUB_REPOSITORY/milestones --jq "
                map(select(.state == \"open\" and .due_on != null))
                | sort_by(.due_on) | reverse
                | .[0] | { number, title }
                | .[${milestone_index}] | { number, title }
                | to_entries
                | map(.key + "=" + (.value|tostring)) | join("\n")' | tee -a $GITHUB_OUTPUT
                | map(.key + \"=\" + (.value|tostring)) | join(\"\n\")" | tee -a $GITHUB_OUTPUT

      - name: Thank you
        if: |
          github.base_ref == 'main' &&
          github.event.pull_request.author_association != 'OWNER' &&
          github.event.pull_request.author_association != 'MEMBER' &&
          github.event.pull_request.author_association != 'COLLABORATOR' &&
+48 −0
Original line number Diff line number Diff line
---
name: PR Opened Actions

# Warning, this job is running on pull_request_target and therefore has access to issue content.
# Don't add any steps that act on external code.
on:
  pull_request_target:
    branches: [beta, release]
    types: [opened]

permissions:
  pull-requests: write

jobs:
  pull-request-opened:
    runs-on: ubuntu-latest
    environment: botmobile
    steps:
      - name: App token generate
        uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e  # v2.0.6
        if: ${{ vars.BOT_CLIENT_ID }}
        id: app-token
        with:
          app-id: ${{ vars.BOT_CLIENT_ID }}
          private-key: ${{ secrets.BOT_PRIVATE_KEY }}

      - name: Uplift Approval Request
        env:
          PR_NUMBER: ${{  github.event.pull_request.number  }}
          GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
          MILESTONE: ${{ steps.milestone.outputs.title }}
          MESSAGE: |
            Thank you for your uplift request! Please add a comment with the following approval request template filled out.

            **[Approval Request]**
            Original Issue/Pull request:
            Regression caused by (issue #):
            User impact if declined:
            Testing completed (on daily, etc.):
            Risk to taking this patch (and alternatives if risky):
        run: |
          if gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json comments \
            --jq '.comments[].body' | grep -q '\[Approval Request\]'; then
            echo "Approval Request already exists. Skipping comment."
            exit 0
          fi

          gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body "$MESSAGE"
+6 −6
Original line number Diff line number Diff line
@@ -191,7 +191,8 @@ The app-k9mail/src/main/res/raw/changelog_master.xml should not include any beta

## Branch Uplifts

If the urgency of a fix requires it to be included in the Beta or Release channel before the next merge, the uplift process is followed. If possible, uplifts should be avoided and patches should “ride the train” instead, following the merge day cycle.
If the urgency of a fix requires it to be included in the Beta or Release channel before the next merge, the uplift process is followed.
If possible, uplifts should be avoided and patches should “ride the train” instead, following the merge day cycle.

### Uplift Criteria

@@ -210,15 +211,14 @@ Release uplifts should additionally:

### Uplift Process

1. The requestor adds the "task: uplift to beta" or "task: uplift to release" label to a merged pull request.
2. The requestor makes a comment in the associated issue with the Approval Request Comment template filled out.
3. The release driver reviews all uplift requests and, retaining the label for approved uplifts and removing the label for rejected uplifts.
4. The release driver runs the Uplift Merges action for the specified target branch, which will remove the label, adjust the milestone, cherry-pick the commits, and push to the target branch.
1. The requestor creates a pull request against the target uplift branch.
2. The requestor adds a comment to the pull request with the Approval Request template filled out.
3. The release driver reviews the uplift request, merging if approved, or closing with a comment if rejected.

Template for uplift requests:

```sh
[Approval Request Comment]
[Approval Request]
Original Issue/Pull request:
Regression caused by (issue #):
User impact if declined:

scripts/ci/uplift-merges.sh

deleted100755 → 0
+0 −144
Original line number Diff line number Diff line
#!/bin/bash

function fail() {
  echo "Error: $*"
  exit 1
}

function drydo() {
  if [ "$dry_run" = true ]; then
    echo "$@"
  else
    eval "$@"
  fi
}

function usage() {
  echo "Usage: $0 [--release | --beta] [--no-dry-run] [--push]"
  echo
  echo "  --release      Required: merge into release branch (mutually exclusive with --beta)"
  echo "  --beta         Required: merge into beta branch (mutually exclusive with --release)"
  echo "  --no-dry-run   Optional: actually perform the merge"
  echo "  --push         Optional: push changes after completion"
  exit 1
}

# Check if tools are installed
command -v gh &> /dev/null || fail "gh (GitHub CLI) is not installed"
command -v jq &> /dev/null || fail "jq is not installed"
command -v git &> /dev/null || fail "git is not installed"

# Default values
dry_run=true
repo=${GITHUB_REPOSITORY:-thunderbird/thunderbird-android}
label="task: uplift to beta"
branch=""
push=false

milestones=$(gh api repos/${repo}/milestones --jq 'map(select(.state == "open" and .due_on != null)) | sort_by(.due_on)' | jq -c)

# Parse command-line arguments
for arg in "$@"; do
  case $arg in
    --no-dry-run)
      dry_run=false
      shift
      ;;
    --release)
      label="task: uplift to release"
      branch="release"
      expected_milestone=$(echo $milestones | jq -r '.[1].title')
      target_milestone=$(echo $milestones | jq -r '.[0].title')
      shift
      ;;
    --beta)
      label="task: uplift to beta"
      branch="beta"
      expected_milestone=$(echo $milestones | jq -r '.[2].title')
      target_milestone=$(echo $milestones | jq -r '.[1].title')
      shift
      ;;
    --push)
      push=true
      shift
      ;;
    *)
      usage
      ;;
  esac
done

if [[ -z "$branch" ]]; then
  usage
fi

# Check if on the correct branch
current_branch=$(git branch --show-current)
if [ "$current_branch" != "$branch" ]; then
    fail "You are not on the $branch branch. Please switch to the $branch branch."
    true
fi

# Check correct number of milestones
milestone_count=$(echo "$milestones" | jq 'length')
if [ "$milestone_count" != 3 ]; then
    fail "Expected 3 open milestones with due date on https://github.com/${repo}/milestones but found $milestone_count"
fi

# Status Info
if [ "$dry_run" = true ]
then
  echo "Dry run in progress, to disable pass --no-dry-run"
fi

echo "Label: \"$label\""
echo ""

# Fetch the uplift commits from the GitHub repository
json_data=$(gh pr list --repo "$repo" --label "$label" --state merged --limit 99 --json "mergedAt,mergeCommit,number,url,title,milestone" | jq -c .)

# Sort by mergedAt
sorted_commits=$(echo "$json_data" | jq -c '. | sort_by(.mergedAt) | .[]')

# Check if there are no commits to cherry-pick
if [ -z "$sorted_commits" ]; then
  echo "No commits to cherry-pick."
  exit 0
fi

# Generate git cherry-pick commands
while IFS= read -r commit
do
    oid=$(echo "$commit" | jq -r '.mergeCommit.oid')
    pr_number=$(echo "$commit" | jq -r '.number')
    pr_url=$(echo "$commit" | jq -r '.url')
    pr_title=$(echo "$commit" | jq -r '.title')
    pr_milestone=$(echo "$commit" | jq -r '.milestone.title')

    echo "Updating branches"
    git checkout main && git pull --quiet
    git checkout $current_branch && git pull --quiet

    echo "Cherry-picking $oid from $pr_url ($pr_title)"
    if [ "$pr_milestone" != "$expected_milestone" ]; then
        fail "PR https://github.com/$repo/pull/$pr_number is on milestone $pr_milestone but expected $expected_milestone"
    fi

    if [ "$dry_run" = true ]; then
        dry_run_branch="${branch}-dry-run"
        if git show-ref --verify --quiet refs/heads/${dry_run_branch}; then
            git branch -D "${dry_run_branch}"
        fi
        git checkout -b "${dry_run_branch}"
        echo "Using $dry_run_branch for cherry-picks"
    fi
    if ! git cherry-pick -m 1 "$oid"; then
        fail "Failed to cherry-pick $oid"
    fi
    if [ "$push" = true ]; then
      drydo git push || fail "Failed to push $oid"
    fi

    drydo gh pr edit "$pr_number" --repo "$repo" --remove-label '"$label"' --milestone '"$target_milestone"' || fail "Failed to remove label from $pr_number"
    echo ""
done <<< "$sorted_commits"