maskit commented on a change in pull request #2526: [dev] provide a python 
merge script for merging pull requests
URL: https://github.com/apache/incubator-pulsar/pull/2526#discussion_r216286359
 
 

 ##########
 File path: dev/pulsar-merge-pr.py
 ##########
 @@ -0,0 +1,699 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+# Utility for creating well-formed pull request merges and pushing them to 
Apache. This script is a modified version
+# of the one created by the BookKeeper project 
(https://github.com/apache/bookkeeper/blob/master/dev/bk-merge-pr.py).
+#
+# Usage: ./pulsar-merge-pr.py (see config env vars below)
+#
+# This utility assumes you already have local a pulsar git folder and that you
+# have added remotes corresponding to the github apache pulsar repo.
+
+import json
+import os
+import re
+import subprocess
+import sys
+import urllib2
+
+PROJECT_NAME = "incubator-pulsar"
+
+CAPITALIZED_PROJECT_NAME = "pulsar".upper()
+GITHUB_ISSUES_NAME = "issue".upper()
+
+# Location of the local git repository
+REPO_HOME = os.environ.get("%s_HOME" % CAPITALIZED_PROJECT_NAME, os.getcwd())
+# Remote name which points to the GitHub site
+PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache")
+# Remote name which points to Apache git
+PUSH_REMOTE_NAME = os.environ.get("PUSH_REMOTE_NAME", "apache")
+# Reference branch name
+DEV_BRANCH_NAME = os.environ.get("DEV_BRANCH_NAME", "master")
+# Github API page size
+GITHUB_PAGE_SIZE = os.environ.get("GH_PAGE_SIZE", "100")
+# OAuth key used for issuing requests against the GitHub API. If this is not 
defined, then requests
+# will be unauthenticated. You should only need to configure this if you find 
yourself regularly
+# exceeding your IP's unauthenticated request rate limit. You can create an 
OAuth key at
+# https://github.com/settings/tokens. This script only requires the 
"public_repo" scope.
+GITHUB_OAUTH_KEY = os.environ.get("GITHUB_OAUTH_KEY")
+
+GITHUB_USER = os.environ.get("GITHUB_USER", "apache")
+GITHUB_BASE = "https://github.com/%s/%s/pull"; % (GITHUB_USER, PROJECT_NAME)
+GITHUB_API_URL  = "https://api.github.com";
+GITHUB_API_BASE = "%s/repos/%s/%s" % (GITHUB_API_URL, GITHUB_USER, 
PROJECT_NAME)
+# Prefix added to temporary branches
+TEMP_BRANCH_PREFIX = "PR_TOOL"
+RELEASE_BRANCH_PREFIX = "branch-"
+
+DEFAULT_FIX_VERSION = os.environ.get("DEFAULT_FIX_VERSION", "0.9.1.0")
+
+def get_json(url, preview_api = False):
+    try:
+        request = urllib2.Request(url)
+        if GITHUB_OAUTH_KEY:
+            request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY)
+        if preview_api:
+            request.add_header('Accept', 
'application/vnd.github.black-cat-preview+json')
+        return json.load(urllib2.urlopen(request))
+    except urllib2.HTTPError as e:
+        if "X-RateLimit-Remaining" in e.headers and 
e.headers["X-RateLimit-Remaining"] == '0':
+            print "Exceeded the GitHub API rate limit; see the instructions in 
" + \
+                  "pulsar-merge-pr.py to configure an OAuth token for making 
authenticated " + \
+                  "GitHub requests."
+        else:
+            print "Unable to fetch URL, exiting: %s" % url
+        sys.exit(-1)
+
+def post_json(url, data):
+    try:
+        request = urllib2.Request(url, data, { 'Content-Type': 
'application/json' })
+        if GITHUB_OAUTH_KEY:
+            request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY)
+        return json.load(urllib2.urlopen(request))
+    except urllib2.HTTPError as e:
+        if "X-RateLimit-Remaining" in e.headers and 
e.headers["X-RateLimit-Remaining"] == '0':
+            print "Exceeded the GitHub API rate limit; see the instructions in 
" + \
+                  "pulsar-merge-pr.py to configure an OAuth token for making 
authenticated " + \
+                  "GitHub requests."
+        else:
+            print "Unable to fetch URL, exiting: %s - %s" % (url, e)
+        sys.exit(-1)
+
+def put_json(url, data):
+    try:
+        request = urllib2.Request(url, data, { 'Content-Type': 
'application/json' })
+        request.get_method = lambda: 'PUT'
+        if GITHUB_OAUTH_KEY:
+            request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY)
+        return json.load(urllib2.urlopen(request))
+    except urllib2.HTTPError as e:
+        if "X-RateLimit-Remaining" in e.headers and 
e.headers["X-RateLimit-Remaining"] == '0':
+            print "Exceeded the GitHub API rate limit; see the instructions in 
" + \
+                  "pulsar-merge-pr.py to configure an OAuth token for making 
authenticated " + \
+                  "GitHub requests."
+        else:
+            print "Unable to fetch URL, exiting: %s - %s" % (url, e)
+            print e
+        sys.exit(-1)
+
+
+def fail(msg):
+    print msg
+    clean_up()
+    sys.exit(-1)
+
+
+def run_cmd(cmd):
+    print cmd
+    if isinstance(cmd, list):
+        return subprocess.check_output(cmd)
+    else:
+        return subprocess.check_output(cmd.split(" "))
+
+
+def continue_maybe(prompt):
+    result = raw_input("\n%s (y/n): " % prompt)
+    if result.lower() != "y":
+        fail("Okay, exiting")
+
+def clean_up():
+    if original_head != get_current_branch():
+        print "Restoring head pointer to %s" % original_head
+        run_cmd("git checkout %s" % original_head)
+
+    branches = run_cmd("git branch").replace(" ", "").split("\n")
+
+    for branch in filter(lambda x: x.startswith(TEMP_BRANCH_PREFIX), branches):
+        print "Deleting local branch %s" % branch
+        run_cmd("git branch -D %s" % branch)
+
+def get_current_branch():
+    return run_cmd("git rev-parse --abbrev-ref HEAD").replace("\n", "")
+
+def get_milestones():
+    return 
get_json("https://api.github.com/repos/%s/%s/milestones?state=open&sort=due_on&direction=asc";
 % (GITHUB_USER, PROJECT_NAME))
+
+def get_all_labels():
+    result = get_json("https://api.github.com/repos/%s/%s/labels?per_page=%s"; 
% (GITHUB_USER, PROJECT_NAME, GITHUB_PAGE_SIZE))
+    return map(lambda x: x['name'], result)
+
+# merge the requested PR and return the merge hash
+def merge_pr(pr_num, target_ref, title, body, default_pr_reviewers, 
pr_repo_desc):
+    pr_branch_name = "%s_MERGE_PR_%s" % (TEMP_BRANCH_PREFIX, pr_num)
+    run_cmd("git fetch %s pull/%s/head:%s" % (PR_REMOTE_NAME, pr_num, 
pr_branch_name))
+
+    commit_authors = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name,
+                             '--pretty=format:%an <%ae>']).split("\n")
+    distinct_authors = sorted(set(commit_authors),
+                              key=lambda x: commit_authors.count(x), 
reverse=True)
+    primary_author = raw_input(
+        "Enter primary author in the format of \"name <email>\" [%s]: " %
+        distinct_authors[0])
+    if primary_author == "":
+        primary_author = distinct_authors[0]
+
+    reviewers = raw_input("Enter reviewers [%s]: " % 
default_pr_reviewers).strip()
+    if reviewers == '':
+        reviewers = default_pr_reviewers
+
+    commits = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name,
+                      '--pretty=format:%h [%an] %s']).split("\n")
+    
+    if len(commits) > 1:
+        result = raw_input("List pull request commits in squashed commit 
message? (y/n): ")
+        if result.lower() == "y":
+          should_list_commits = True
+        else:
+          should_list_commits = False
+    else:
+        should_list_commits = False
+
+    merge_message_flags = []
+
+    if body is not None:
+        # We remove @ symbols from the body to avoid triggering e-mails
+        # to people every time someone creates a public fork of the project.
+        merge_message_flags += [body.replace("@", "")]
+
+    authors = "\n".join(["Author: %s" % a for a in distinct_authors])
+
+    merge_message_flags += [authors, "\n"]
+
+    if (reviewers != ""):
+        merge_message_flags += ["Reviewers: %s" % reviewers, "\n"]
+
+    # The string "Closes #%s" string is required for GitHub to correctly close 
the PR
+    close_line = "This closes #%s from %s" % (pr_num, pr_repo_desc)
+    # Find the github issues to close
+    github_issues = re.findall("#[0-9]{3,6}", title)
+
+    if len(github_issues) != 0:
 
 Review comment:
   This block is expecting that all referred issues in a PR title will be 
resolved when the PR is closed.
   
   I'm OK with this rule, however, it need to be discussed and clearly stated.
   
   

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to