##// END OF EJS Templates
some fixs
Matthias BUSSONNIER -
Show More
@@ -1,96 +1,104 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, httpv2=False):
76 def get_pull_request(project, num, github_api=3):
77 if httpv2 :
77 """get pull request info by number
78
79 github_api : version of github api to use
80 """
81 if github_api==2 :
78 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
82 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
79 else:
83 elif github_api == 3:
80 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
84 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
81 response = requests.get(url)
85 response = requests.get(url)
82 response.raise_for_status()
86 response.raise_for_status()
83 if httpv2 :
87 if httpv2 :
84 return json.loads(response.text)['pull']
88 return json.loads(response.text)['pull']
85 return json.loads(response.text)
89 return json.loads(response.text)
86
90
87 def get_pulls_list(project,httpv2=False):
91 def get_pulls_list(project, github_api=3):
88 if not httpv2 :
92 """get pull request list
93
94 github_api : version of github api to use
95 """
96 if github_api == 3 :
89 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
97 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
90 else :
98 else :
91 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
99 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
92 response = requests.get(url)
100 response = requests.get(url)
93 response.raise_for_status()
101 response.raise_for_status()
94 if httpv2 :
102 if httpv2 :
95 return json.loads(response.text)['pulls']
103 return json.loads(response.text)['pulls']
96 return json.loads(response.text)
104 return json.loads(response.text)
@@ -1,117 +1,136 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """
3 """
4 Usage:
4 Usage:
5 python test_pr.py 1657
5 python git-mpr.py -m 1657
6 """
6 """
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import re
9 import re
10 import requests
10 import requests
11 import argparse
11 from subprocess import call, check_call, check_output, CalledProcessError
12 from subprocess import call, check_call, check_output, CalledProcessError
12 import sys
13 import sys
13
14
14 import gh_api
15 import gh_api
15
16
16 ipy_repository = 'git://github.com/ipython/ipython.git'
17 ipy_repository = 'git://github.com/ipython/ipython.git'
17 gh_project="ipython/ipython"
18 gh_project="ipython/ipython"
18 not_merged={}
19 not_merged={}
19
20
20 def get_branch(repo, branch, owner, mergeable):
21
22 def merge_branch(repo, branch, owner, mergeable):
23 """try to merge the givent branch into the current one
24
25 If something does not goes smoothly, merge is aborted
26
27 Returns True if merge sucessfull, False otherwise
28 """
21 merged_branch = "%s-%s" % (owner, branch)
29 merged_branch = "%s-%s" % (owner, branch)
22 # Delete the branch first
30 # Delete the branch first
23 try :
31 try :
24 check_call(['git', 'pull','--no-edit',repo, branch])
32 check_call(['git', 'pull','--no-edit',repo, branch])
25 except CalledProcessError :
33 except CalledProcessError :
26 check_call(['git', 'merge', '--abort'])
34 check_call(['git', 'merge', '--abort'])
27 return False
35 return False
28 return True
36 return True
29
37
30 def merge_pr(num,httpv2=False):
38
39 def merge_pr(num,github_api=3):
40 """ try to merge the branch of PR `num` into current branch
41
42 github_api : use github api v2 (to bypass https and issues with proxy) to find the
43 remote branch that should be merged by it's number
44 """
31 # Get Github authorisation first, so that the user is prompted straight away
45 # Get Github authorisation first, so that the user is prompted straight away
32 # if their login is needed.
46 # if their login is needed.
33
47
34 pr = gh_api.get_pull_request(gh_project, num, httpv2)
48 pr = gh_api.get_pull_request(gh_project, num, github_api)
35 if(httpv2):
49 if github_api == 2:
36 repo = pr['head']['repository']['url']
50 repo = pr['head']['repository']['url']
37 owner=pr['head']['user']['name'],
51 owner = pr['head']['user']['name']
38 else :
52 elif github_api == 2 :
39 repo=pr['head']['repo']['clone_url']
53 repo = pr['head']['repo']['clone_url']
40 owner=pr['head']['repo']['owner']['login'],
54 owner = pr['head']['repo']['owner']['login']
41
55
42 branch=pr['head']['ref']
56 branch = pr['head']['ref']
43 mergeable = get_branch(repo=repo,
57 mergeable = merge_branch(repo=repo,
44 branch=branch,
58 branch=branch,
45 owner=owner,
59 owner=owner,
46 mergeable=pr['mergeable'],
60 mergeable=pr['mergeable'],
47 )
61 )
48 if not mergeable :
62 if not mergeable :
49 cmd = "git pull "+repo+" "+branch
63 cmd = "git pull "+repo+" "+branch
50 not_merged[str(num)]=cmd
64 not_merged[str(num)] = cmd
51 print("==============================================================================")
65 print("==============================================================================")
52 print("Something went wrong merging this branch, you can try it manually by runngin :")
66 print("Something went wrong merging this branch, you can try it manually by runngin :")
53 print(cmd)
67 print(cmd)
54 print("==============================================================================")
68 print("==============================================================================")
55
69
56
70
57
71 def main(*args):
58 if __name__ == '__main__':
59 import argparse
60 parser = argparse.ArgumentParser(
72 parser = argparse.ArgumentParser(
61 description="""
73 description="""
62 Merge (one|many) github pull request by their number.\
74 Merge (one|many) github pull request by their number.\
63
75
64 If pull request can't be merge as is, cancel merge,
76 If pull request can't be merge as is, cancel merge,
65 and continue to the next if any.
77 and continue to the next if any.
66 """
78 """
67 )
79 )
68 parser.add_argument('-v2','--githubapiv2', action='store_const', const=True)
80 parser.add_argument('-v2','--githubapiv2', action='store_const', const=2)
69
81
70 grp = parser.add_mutually_exclusive_group()
82 grp = parser.add_mutually_exclusive_group()
71 grp.add_argument(
83 grp.add_argument(
72 '-l',
84 '-l',
73 '--list',
85 '--list',
74 action='store_const',
86 action='store_const',
75 const=True,
87 const=True,
76 help='list PR, their number and their mergeability')
88 help='list PR, their number and their mergeability')
77 grp.add_argument('-a',
89 grp.add_argument('-a',
78 '--merge-all',
90 '--merge-all',
79 action='store_const',
91 action='store_const',
80 const=True ,
92 const=True ,
81 help='try to merge as many PR as possible, one by one')
93 help='try to merge as many PR as possible, one by one')
82 grp.add_argument('-m',
94 grp.add_argument('-m',
83 '--merge',
95 '--merge',
84 type=int,
96 type=int,
85 help="The pull request numbers",
97 help="The pull request numbers",
86 nargs='*',
98 nargs='*',
87 metavar='pr-number')
99 metavar='pr-number')
88 not_merged = {};
100 not_merged = {};
89 args = parser.parse_args()
101 args = parser.parse_args()
90 ghv2 = args.githubapiv2
102 if args.githubapiv2 == 2 :
103 github_api = 2
104 else :
105 github_api = 3
106
91 if(args.list):
107 if(args.list):
92 pr_list = gh_api.get_pulls_list(gh_project, ghv2)
108 pr_list = gh_api.get_pulls_list(gh_project, github_api)
93 for pr in pr_list :
109 for pr in pr_list :
94 mergeable = gh_api.get_pull_request(gh_project, pr['number'],httpv2=ghv2)['mergeable']
110 mergeable = gh_api.get_pull_request(gh_project, pr['number'],github_api=github_api)['mergeable']
95
111
96 ismgb = u"√" if mergeable else " "
112 ismgb = u"√" if mergeable else " "
97 print(u"* #{number} [{ismgb}]: {title}".format(
113 print(u"* #{number} [{ismgb}]: {title}".format(
98 number=pr['number'],
114 number=pr['number'],
99 title=pr['title'],
115 title=pr['title'],
100 ismgb=ismgb))
116 ismgb=ismgb))
101
117
102 if(args.merge_all):
118 if(args.merge_all):
103 pr_list = gh_api.get_pulls_list(gh_project)
119 pr_list = gh_api.get_pulls_list(gh_project)
104 for pr in pr_list :
120 for pr in pr_list :
105 merge_pr(pr['number'])
121 merge_pr(pr['number'])
106
122
107
123
108 elif args.merge:
124 elif args.merge:
109 for num in args.merge :
125 for num in args.merge :
110 merge_pr(num,httpv2=ghv2)
126 merge_pr(num, github_api=github_api)
111
127
112 if not_merged :
128 if not_merged :
113 print('*************************************************************************************')
129 print('*************************************************************************************')
114 print('the following branch have not been merged automatically, considere doing it by hand :')
130 print('the following branch have not been merged automatically, considere doing it by hand :')
115 for num,cmd in not_merged.items() :
131 for num,cmd in not_merged.items() :
116 print( "PR {num}: {cmd}".format(num=num,cmd=cmd))
132 print( "PR {num}: {cmd}".format(num=num,cmd=cmd))
117 print('*************************************************************************************')
133 print('*************************************************************************************')
134
135 if __name__ == '__main__':
136 main()
General Comments 0
You need to be logged in to leave comments. Login now