Skip to content

Commit 239558b

Browse files
authored
Automatic cherry-pick script (#339)
* Automatic cherry-pick script * switch from alamb to apache * autopep8 * flake8 * add rat * tweaks * Add some docs to the README
1 parent 7ec3158 commit 239558b

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

dev/release/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,31 @@ following commands
234234
(cd parquet && cargo publish)
235235
(cd parquet_derive && cargo publish)
236236
```
237+
238+
# Backporting
239+
240+
As of now, the plan for backporting to `active_release` is to do so semi-manually.
241+
242+
Step 1: Pick the commit to cherry-pick
243+
244+
Step 2: Create cherry-pick PR to active_release
245+
246+
Step 3a: If CI passes, merge cherry-pick PR
247+
248+
Step 3b: If CI doesn't pass or some other changes are needed, the PR should be reviewed / approved as normal prior to merge
249+
250+
251+
252+
For example, to backport `b2de5446cc1e45a0559fb39039d0545df1ac0d26` to active_release use the folliwing
253+
254+
```shell
255+
git clone git@github.com:apache/arrow-rs.git /tmp/arrow-rs
256+
257+
ARROW_GITHUB_API_TOKEN=$ARROW_GITHUB_API_TOKEN CHECKOUT_ROOT=/tmp/arrow-rs CHERRY_PICK_SHA=b2de5446cc1e45a0559fb39039d0545df1ac0d26 python3 dev/release/cherry-pick-pr.py
258+
```
259+
260+
## Rationale for creating PRs:
261+
1. PRs are a natural place to run the CI tests to make sure there are no logical conflicts
262+
2. PRs offer a place for the original author / committers to comment and say it should/should not be backported.
263+
3. PRs offer a way to make cleanups / fixups and approve (if needed) for non cherry pick PRs
264+
4. There is an additional control / review when the candidate release is created

dev/release/cherry-pick-pr.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#!/usr/bin/python3
2+
##############################################################################
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
##############################################################################
20+
21+
# This script is designed to create a cherry pick PR to a target branch
22+
#
23+
# Usage: python3 cherry_pick_pr.py
24+
#
25+
# To test locally:
26+
#
27+
# git clone git@github.com:apache/arrow-rs.git /tmp/arrow-rs
28+
#
29+
# pip3 install PyGithub
30+
# ARROW_GITHUB_API_TOKEN=<..>
31+
# CHECKOUT_ROOT=<path>
32+
# CHERRY_PICK_SHA=<sha> python3 cherry-pick-pr.py
33+
#
34+
import os
35+
import sys
36+
import six
37+
import subprocess
38+
39+
from pathlib import Path
40+
41+
TARGET_BRANCH = 'active_release'
42+
TARGET_REPO = 'apache/arrow-rs'
43+
44+
p = Path(__file__)
45+
46+
# Use github workspace if specified
47+
repo_root = os.environ.get("CHECKOUT_ROOT")
48+
if repo_root is None:
49+
print("arrow-rs checkout must be supplied in CHECKOUT_ROOT environment")
50+
sys.exit(1)
51+
52+
print("Using checkout in {}".format(repo_root))
53+
54+
token = os.environ.get('ARROW_GITHUB_API_TOKEN', None)
55+
if token is None:
56+
print("GITHUB token must be supplied in ARROW_GITHUB_API_TOKEN environmet")
57+
sys.exit(1)
58+
59+
new_sha = os.environ.get('CHERRY_PICK_SHA', None)
60+
if new_sha is None:
61+
print("SHA to cherry pick must be supplied in CHERRY_PICK_SHA environment")
62+
sys.exit(1)
63+
64+
65+
# from merge_pr.py from arrow repo
66+
def run_cmd(cmd):
67+
if isinstance(cmd, six.string_types):
68+
cmd = cmd.split(' ')
69+
try:
70+
output = subprocess.check_output(cmd)
71+
except subprocess.CalledProcessError as e:
72+
# this avoids hiding the stdout / stderr of failed processes
73+
print('Command failed: %s' % cmd)
74+
print('With output:')
75+
print('--------------')
76+
print(e.output)
77+
print('--------------')
78+
raise e
79+
80+
if isinstance(output, six.binary_type):
81+
output = output.decode('utf-8')
82+
83+
return output
84+
85+
86+
os.chdir(repo_root)
87+
new_sha_short = run_cmd("git rev-parse --short {}".format(new_sha)).strip()
88+
new_branch = 'cherry_pick_{}'.format(new_sha_short)
89+
90+
91+
def make_cherry_pick():
92+
if os.environ.get('GITHUB_SHA', None) is not None:
93+
print("Running on github runner, setting email/username")
94+
run_cmd(['git', 'config', 'user.email', 'dev@arrow.apache.com'])
95+
run_cmd(['git', 'config', 'user.name', 'Arrow-RS Automation'])
96+
97+
#
98+
# Create a new branch from active_release
99+
# and cherry pick to there.
100+
#
101+
102+
print("Creating cherry pick from {} to {}".format(
103+
new_sha_short, new_branch
104+
))
105+
106+
# The following tortured dance is required due to how the github
107+
# actions/checkout works (it doesn't pull other branches and pulls
108+
# only one commit back)
109+
110+
# pull 10 commits back so we can get the proper cherry pick
111+
# (probably only need 2 but 10 must be better, right?)
112+
run_cmd(['git', 'fetch', '--depth', '10', 'origin', 'master'])
113+
run_cmd(['git', 'fetch', 'origin', 'active_release'])
114+
run_cmd(['git', 'checkout', '-b', new_branch])
115+
run_cmd(['git', 'reset', '--hard', 'origin/active_release'])
116+
run_cmd(['git', 'cherry-pick', new_sha])
117+
run_cmd(['git', 'push', '-u', 'origin', new_branch])
118+
119+
120+
def make_cherry_pick_pr():
121+
from github import Github
122+
g = Github(token)
123+
repo = g.get_repo(TARGET_REPO)
124+
125+
# Default titles
126+
new_title = 'Cherry pick {} to active_release'.format(new_sha)
127+
new_commit_message = 'Automatic cherry-pick of {}\n'.format(new_sha)
128+
129+
# try and get info from github api
130+
commit = repo.get_commit(new_sha)
131+
for orig_pull in commit.get_pulls():
132+
new_commit_message += '* Originally appeared in {}: {}\n'.format(
133+
orig_pull.html_url, orig_pull.title)
134+
new_title = 'Cherry pick {} to active_release'.format(orig_pull.title)
135+
136+
pr = repo.create_pull(title=new_title,
137+
body=new_commit_message,
138+
base='refs/heads/active_release',
139+
head='refs/heads/{}'.format(new_branch),
140+
maintainer_can_modify=True
141+
)
142+
143+
print('Created PR {}'.format(pr.html_url))
144+
145+
146+
make_cherry_pick()
147+
make_cherry_pick_pr()

0 commit comments

Comments
 (0)