##// END OF EJS Templates
Add script to test pull request
Thomas Kluyver -
Show More
@@ -0,0 +1,141 b''
1 """
2 This is a script for testing pull requests for IPython. It merges the pull
3 request with current master, installs and tests on all available versions of
4 Python, and posts the results to Gist if any tests fail.
5
6 Usage:
7 python test_pr.py 1657
8 """
9 from __future__ import print_function
10
11 import errno
12 from glob import glob
13 import json
14 import os
15 import shutil
16 from subprocess import call, check_call, check_output, PIPE, STDOUT, CalledProcessError
17 try:
18 from urllib.request import urlopen
19 except ImportError:
20 from urllib2 import urlopen
21
22 basedir = os.path.join(os.path.expanduser("~"), ".ipy_pr_tests")
23 repodir = os.path.join(basedir, "ipython")
24 ipy_repository = 'git://github.com/ipython/ipython.git'
25
26 supported_pythons = ['python2.6', 'python2.7', 'python3.1', 'python3.2']
27 unavailable_pythons = []
28
29 def available_python_versions():
30 """Get the executable names of available versions of Python on the system.
31 """
32 del unavailable_pythons[:]
33 for py in supported_pythons:
34 try:
35 check_call([py, '-c', 'import nose'], stdout=PIPE)
36 yield py
37 except (OSError, CalledProcessError):
38 unavailable_pythons.append(py)
39
40 venvs = []
41
42 def setup():
43 """Prepare the repository and virtualenvs."""
44 global venvs
45
46 try:
47 os.mkdir(basedir)
48 except OSError as e:
49 if e.errno != errno.EEXIST:
50 raise
51 os.chdir(basedir)
52
53 # Delete virtualenvs and recreate
54 for venv in glob('venv-*'):
55 shutil.rmtree(venv)
56 for py in available_python_versions():
57 check_call(['virtualenv', '-p', py, '--system-site-packages', 'venv-%s' % py])
58 venvs.append((py, 'venv-%s' % py))
59
60 # Check out and update the repository
61 if not os.path.exists('ipython'):
62 check_call(['git', 'clone', ipy_repository])
63 os.chdir(repodir)
64 check_call(['git', 'checkout', 'master'])
65 check_call(['git', 'pull', ipy_repository, 'master'])
66 os.chdir(basedir)
67
68 def get_pull_request(num, project="ipython/ipython"):
69 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
70 response = urlopen(url).read().decode('utf-8')
71 return json.loads(response)
72
73 def merge_branch(repo, branch, owner):
74 merged_branch = "%s-%s" % (owner, branch)
75 os.chdir(repodir)
76 # Delete the branch first
77 call(['git', 'branch', '-D', merged_branch])
78 check_call(['git', 'checkout', '-b', merged_branch])
79 check_call(['git', 'pull', repo, branch])
80 os.chdir(basedir)
81
82 def run_tests(venv):
83 py = os.path.join(basedir, venv, 'bin', 'python')
84 print(py)
85 os.chdir(repodir)
86 check_call([py, 'setup.py', 'install'])
87 os.chdir(basedir)
88
89 iptest = os.path.join(basedir, venv, 'bin', 'iptest')
90 if not os.path.exists(iptest):
91 iptest = os.path.join(basedir, venv, 'bin', 'iptest3')
92
93 print("\nRunning tests, this typically takes a few minutes...")
94 try:
95 return True, check_output([iptest], stderr=STDOUT).decode('utf-8')
96 except CalledProcessError as e:
97 return False, e.output
98
99 def post_gist(content, description='IPython test log', filename="results.log"):
100 """Post some text to a Gist, and return the URL."""
101 post_data = json.dumps({
102 "description": description,
103 "public": True,
104 "files": {
105 filename: {
106 "content": content
107 }
108 }
109 }).encode('utf-8')
110
111 response = urlopen("https://api.github.com/gists", post_data)
112 response_data = json.loads(response.read().decode('utf-8'))
113 return response_data['html_url']
114
115 if __name__ == '__main__':
116 import sys
117 num = sys.argv[1]
118 setup()
119 pr = get_pull_request(num)
120 merge_branch(repo=pr['head']['repo']['clone_url'],
121 branch=pr['head']['ref'],
122 owner=pr['head']['repo']['owner']['login'])
123
124 results = []
125 for py, venv in venvs:
126 passed, log = run_tests(venv)
127 if passed:
128 results.append((py, True, None))
129 else:
130 gist_url = post_gist(log)
131 results.append((py, False, gist_url))
132
133 print("\n")
134 print("**Test results for commit %s merged into master**" % pr['head']['sha'][:7])
135 for py, passed, gist_url in results:
136 if passed:
137 print(py, ":", "OK")
138 else:
139 print(py, ":", "Failed")
140 print(" Test log:", gist_url)
141 print("Not available for testing:", ", ".join(unavailable_pythons))
General Comments 0
You need to be logged in to leave comments. Login now