Skip to content
86 changes: 86 additions & 0 deletions .github/scripts/auto-add-milestone.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/bash

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

get_milestone_number() {
local milestone_title="$1"
local number=$(gh api "repos/$REPOSITORY/milestones" --jq ".[] | select(.title==\"$milestone_title\") | .number")

if [ -z "$number" ]; then
echo "Creating milestone: $milestone_title"
number=$(gh api "repos/$REPOSITORY/milestones" -f title="$milestone_title" --jq '.number')
fi

echo "$number"
}

create_backport_issue() {
local pr_number="$1"
local pr_title="$2"
local target_milestone="$3"

local milestone_number=$(get_milestone_number "$target_milestone")
local message="Backport #$pr_number to release branch for version $target_milestone"
local note="Add the \`approved\` label to start the automatic backport process."
local body=$(jq -n --arg source_pr "$pr_number" --arg target_version "$target_milestone" --arg message "$message" --arg note "$note" '$ARGS.named')

gh api "repos/$REPOSITORY/issues" \
-f title="[$target_milestone] $pr_title" \
-f body="$body" \
-f milestone="$milestone_number" \
-f labels[]="backport" \
--jq '.number'
}

if [ "$#" -ne 2 ]; then
echo "Usage: $0 <pr_number> <repository>"
echo "Example: $0 12345 apache/druid"
exit 1
fi

PR_NUMBER="$1"
REPOSITORY="$2"

VERSION=$(xmllint --xpath "/*[local-name()='project']/*[local-name()='version']/text()" pom.xml | sed 's/-SNAPSHOT//')

if [ -z "$VERSION" ]; then
echo "Error: Could not extract version from pom.xml"
exit 1
fi

echo "Extracted version: $VERSION"

EXISTING_MILESTONE=$(gh api "repos/$REPOSITORY/issues/$PR_NUMBER" --jq '.milestone.title // empty')

if [ -n "$EXISTING_MILESTONE" ] && [ "$EXISTING_MILESTONE" != "$VERSION" ]; then
echo "PR #$PR_NUMBER has milestone $EXISTING_MILESTONE, but should be $VERSION"

PR_TITLE=$(gh api "repos/$REPOSITORY/issues/$PR_NUMBER" --jq '.title')
BACKPORT_ISSUE=$(create_backport_issue "$PR_NUMBER" "$PR_TITLE" "$EXISTING_MILESTONE")

echo "Created backport issue #$BACKPORT_ISSUE for milestone $EXISTING_MILESTONE"
elif [ -n "$EXISTING_MILESTONE" ]; then
echo "PR #$PR_NUMBER already has correct milestone: $EXISTING_MILESTONE"
exit 0
fi

MILESTONE_NUMBER=$(get_milestone_number "$VERSION")

echo "Adding PR #$PR_NUMBER to milestone $VERSION"
gh api "repos/$REPOSITORY/issues/$PR_NUMBER" -f milestone="$MILESTONE_NUMBER" -X PATCH
echo "Successfully added PR #$PR_NUMBER to milestone $VERSION"
43 changes: 43 additions & 0 deletions .github/scripts/backport-pr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
echo "Usage: $0 <commit_hash> <target_branch> <pr_title> [body]"
echo "Example: $0 abc123def456 37.0.0 'Fix bug in query engine' '{\"backport_issue\": \"12345\"}'"
exit 1
fi

COMMIT_HASH="$1"
TARGET_BRANCH="$2"
PR_TITLE="$3"
BODY="${4:-Automatic backport to $TARGET_BRANCH}"

BACKPORT_BRANCH="backport-$COMMIT_HASH-to-$TARGET_BRANCH"

git fetch origin "$TARGET_BRANCH"
git checkout -b "$BACKPORT_BRANCH" "origin/$TARGET_BRANCH"
git cherry-pick -x "$COMMIT_HASH"
git push origin "$BACKPORT_BRANCH"

gh pr create \
--base "$TARGET_BRANCH" \
--head "$BACKPORT_BRANCH" \
--title "$PR_TITLE" \
--body "$BODY" \
--label "backport"
71 changes: 71 additions & 0 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

name: "Backport PR"

on:
issues:
types: [labeled]

env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}

jobs:
backport:
if: github.repository == 'apache/druid' && github.event.label.name == 'approved' && contains(github.event.issue.labels.*.name, 'backport')
permissions:
contents: write
pull-requests: write
issues: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Parse issue and create backport PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_PR: ${{ fromJson(github.event.issue.body).source_pr }}
TARGET_VERSION: ${{ fromJson(github.event.issue.body).target_version }}
run: |
MERGE_COMMIT=$(gh api "repos/${{ github.repository }}/pulls/$SOURCE_PR" --jq '.merge_commit_sha')

if [ -z "$MERGE_COMMIT" ]; then
echo "Error: Could not find merge commit for PR #$SOURCE_PR"
exit 1
fi

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

MESSAGE="Backport for #$ISSUE_NUMBER"
BODY=$(jq -n --arg backport_issue "$ISSUE_NUMBER" --arg message "$MESSAGE" '$ARGS.named')

.github/scripts/backport-pr.sh "$MERGE_COMMIT" "$TARGET_VERSION" "[Backport] $ISSUE_TITLE" "$BODY"

- name: Comment on failure
if: failure()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api "repos/${{ github.repository }}/issues/$ISSUE_NUMBER/comments" \
-f body="❌ Automatic backport failed. See workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
47 changes: 47 additions & 0 deletions .github/workflows/pr-merged.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

name: "PR Merged"

on:
pull_request:
types: [closed]

jobs:
add-milestone:
if: github.repository == 'apache/druid' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'master'
permissions:
contents: read
pull-requests: write
issues: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install xmllint
run: |
sudo apt-get update
sudo apt-get install -y libxml2-utils

- name: Add milestone to PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
.github/scripts/auto-add-milestone.sh "${{ github.event.pull_request.number }}" "${{ github.repository }}"
Loading