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