##// END OF EJS Templates
merge pull request by number...
Matthias BUSSONNIER -
Show More
@@ -0,0 +1,117 b''
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 Usage:
5 python test_pr.py 1657
6 """
7 from __future__ import print_function
8
9 import re
10 import requests
11 from subprocess import call, check_call, check_output, CalledProcessError
12 import sys
13
14 import gh_api
15
16 ipy_repository = 'git://github.com/ipython/ipython.git'
17 gh_project="ipython/ipython"
18 not_merged={}
19
20 def get_branch(repo, branch, owner, mergeable):
21 merged_branch = "%s-%s" % (owner, branch)
22 # Delete the branch first
23 try :
24 check_call(['git', 'pull','--no-edit',repo, branch])
25 except CalledProcessError :
26 check_call(['git', 'merge', '--abort'])
27 return False
28 return True
29
30 def merge_pr(num,httpv2=False):
31 # Get Github authorisation first, so that the user is prompted straight away
32 # if their login is needed.
33
34 pr = gh_api.get_pull_request(gh_project, num, httpv2)
35 if(httpv2):
36 repo = pr['head']['repository']['url']
37 owner=pr['head']['user']['name'],
38 else :
39 repo=pr['head']['repo']['clone_url']
40 owner=pr['head']['repo']['owner']['login'],
41
42 branch=pr['head']['ref']
43 mergeable = get_branch(repo=repo,
44 branch=branch,
45 owner=owner,
46 mergeable=pr['mergeable'],
47 )
48 if not mergeable :
49 cmd = "git pull "+repo+" "+branch
50 not_merged[str(num)]=cmd
51 print("==============================================================================")
52 print("Something went wrong merging this branch, you can try it manually by runngin :")
53 print(cmd)
54 print("==============================================================================")
55
56
57
58 if __name__ == '__main__':
59 import argparse
60 parser = argparse.ArgumentParser(
61 description="""
62 Merge (one|many) github pull request by their number.\
63
64 If pull request can't be merge as is, cancel merge,
65 and continue to the next if any.
66 """
67 )
68 parser.add_argument('-v2','--githubapiv2', action='store_const', const=True)
69
70 grp = parser.add_mutually_exclusive_group()
71 grp.add_argument(
72 '-l',
73 '--list',
74 action='store_const',
75 const=True,
76 help='list PR, their number and their mergeability')
77 grp.add_argument('-a',
78 '--merge-all',
79 action='store_const',
80 const=True ,
81 help='try to merge as many PR as possible, one by one')
82 grp.add_argument('-m',
83 '--merge',
84 type=int,
85 help="The pull request numbers",
86 nargs='*',
87 metavar='pr-number')
88 not_merged = {};
89 args = parser.parse_args()
90 ghv2 = args.githubapiv2
91 if(args.list):
92 pr_list = gh_api.get_pulls_list(gh_project, ghv2)
93 for pr in pr_list :
94 mergeable = gh_api.get_pull_request(gh_project, pr['number'],httpv2=ghv2)['mergeable']
95
96 ismgb = u"√" if mergeable else " "
97 print(u"* #{number} [{ismgb}]: {title}".format(
98 number=pr['number'],
99 title=pr['title'],
100 ismgb=ismgb))
101
102 if(args.merge_all):
103 pr_list = gh_api.get_pulls_list(gh_project)
104 for pr in pr_list :
105 merge_pr(pr['number'])
106
107
108 elif args.merge:
109 for num in args.merge :
110 merge_pr(num,httpv2=ghv2)
111
112 if not_merged :
113 print('*************************************************************************************')
114 print('the following branch have not been merged automatically, considere doing it by hand :')
115 for num,cmd in not_merged.items() :
116 print( "PR {num}: {cmd}".format(num=num,cmd=cmd))
117 print('*************************************************************************************')
@@ -1,80 +1,96 b''
1 """Functions for Github authorisation."""
1 """Functions for Github authorisation."""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 try:
4 try:
5 input = raw_input
5 input = raw_input
6 except NameError:
6 except NameError:
7 pass
7 pass
8
8
9 import requests
9 import requests
10 import getpass
10 import getpass
11 import json
11 import json
12
12
13 # Keyring stores passwords by a 'username', but we're not storing a username and
13 # Keyring stores passwords by a 'username', but we're not storing a username and
14 # password
14 # password
15 fake_username = 'ipython_tools'
15 fake_username = 'ipython_tools'
16
16
17 token = None
17 token = None
18 def get_auth_token():
18 def get_auth_token():
19 global token
19 global token
20
20
21 if token is not None:
21 if token is not None:
22 return token
22 return token
23
23
24 import keyring
24 import keyring
25 token = keyring.get_password('github', fake_username)
25 token = keyring.get_password('github', fake_username)
26 if token is not None:
26 if token is not None:
27 return token
27 return token
28
28
29 print("Please enter your github username and password. These are not "
29 print("Please enter your github username and password. These are not "
30 "stored, only used to get an oAuth token. You can revoke this at "
30 "stored, only used to get an oAuth token. You can revoke this at "
31 "any time on Github.")
31 "any time on Github.")
32 user = input("Username: ")
32 user = input("Username: ")
33 pw = getpass.getpass("Password: ")
33 pw = getpass.getpass("Password: ")
34
34
35 auth_request = {
35 auth_request = {
36 "scopes": [
36 "scopes": [
37 "public_repo",
37 "public_repo",
38 "gist"
38 "gist"
39 ],
39 ],
40 "note": "IPython tools",
40 "note": "IPython tools",
41 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
41 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
42 }
42 }
43 response = requests.post('https://api.github.com/authorizations',
43 response = requests.post('https://api.github.com/authorizations',
44 auth=(user, pw), data=json.dumps(auth_request))
44 auth=(user, pw), data=json.dumps(auth_request))
45 response.raise_for_status()
45 response.raise_for_status()
46 token = json.loads(response.text)['token']
46 token = json.loads(response.text)['token']
47 keyring.set_password('github', fake_username, token)
47 keyring.set_password('github', fake_username, token)
48 return token
48 return token
49
49
50 def make_auth_header():
50 def make_auth_header():
51 return {'Authorization': 'token ' + get_auth_token()}
51 return {'Authorization': 'token ' + get_auth_token()}
52
52
53 def post_issue_comment(project, num, body):
53 def post_issue_comment(project, num, body):
54 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
54 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
55 payload = json.dumps({'body': body})
55 payload = json.dumps({'body': body})
56 r = requests.post(url, data=payload, headers=make_auth_header())
56 r = requests.post(url, data=payload, headers=make_auth_header())
57
57
58 def post_gist(content, description='', filename='file', auth=False):
58 def post_gist(content, description='', filename='file', auth=False):
59 """Post some text to a Gist, and return the URL."""
59 """Post some text to a Gist, and return the URL."""
60 post_data = json.dumps({
60 post_data = json.dumps({
61 "description": description,
61 "description": description,
62 "public": True,
62 "public": True,
63 "files": {
63 "files": {
64 filename: {
64 filename: {
65 "content": content
65 "content": content
66 }
66 }
67 }
67 }
68 }).encode('utf-8')
68 }).encode('utf-8')
69
69
70 headers = make_auth_header() if auth else {}
70 headers = make_auth_header() if auth else {}
71 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
71 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
72 response.raise_for_status()
72 response.raise_for_status()
73 response_data = json.loads(response.text)
73 response_data = json.loads(response.text)
74 return response_data['html_url']
74 return response_data['html_url']
75
75
76 def get_pull_request(project, num):
76 def get_pull_request(project, num, httpv2=False):
77 if httpv2 :
78 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
79 else:
77 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
80 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
78 response = requests.get(url)
81 response = requests.get(url)
79 response.raise_for_status()
82 response.raise_for_status()
83 if httpv2 :
84 return json.loads(response.text)['pull']
85 return json.loads(response.text)
86
87 def get_pulls_list(project,httpv2=False):
88 if not httpv2 :
89 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
90 else :
91 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
92 response = requests.get(url)
93 response.raise_for_status()
94 if httpv2 :
95 return json.loads(response.text)['pulls']
80 return json.loads(response.text)
96 return json.loads(response.text)
General Comments 0
You need to be logged in to leave comments. Login now