##// END OF EJS Templates
Give test_pr proper env call to python interpreter....
Fernando Perez -
Show More
@@ -1,243 +1,244 b''
1 #!/usr/bin/env python
1 2 """
2 3 This is a script for testing pull requests for IPython. It merges the pull
3 4 request with current master, installs and tests on all available versions of
4 5 Python, and posts the results to Gist if any tests fail.
5 6
6 7 Usage:
7 8 python test_pr.py 1657
8 9 """
9 10 from __future__ import print_function
10 11
11 12 import errno
12 13 from glob import glob
13 14 import io
14 15 import json
15 16 import os
16 17 import pickle
17 18 import re
18 19 import requests
19 20 import shutil
20 21 import time
21 22 from subprocess import call, check_call, check_output, PIPE, STDOUT, CalledProcessError
22 23 import sys
23 24
24 25 import gh_api
25 26
26 27 basedir = os.path.join(os.path.expanduser("~"), ".ipy_pr_tests")
27 28 repodir = os.path.join(basedir, "ipython")
28 29 ipy_repository = 'git://github.com/ipython/ipython.git'
29 30 gh_project="ipython/ipython"
30 31
31 32 supported_pythons = ['python2.6', 'python2.7', 'python3.1', 'python3.2']
32 33 unavailable_pythons = []
33 34
34 35 def available_python_versions():
35 36 """Get the executable names of available versions of Python on the system.
36 37 """
37 38 del unavailable_pythons[:]
38 39 for py in supported_pythons:
39 40 try:
40 41 check_call([py, '-c', 'import nose'], stdout=PIPE)
41 42 yield py
42 43 except (OSError, CalledProcessError):
43 44 unavailable_pythons.append(py)
44 45
45 46 venvs = []
46 47
47 48 def setup():
48 49 """Prepare the repository and virtualenvs."""
49 50 global venvs
50 51
51 52 try:
52 53 os.mkdir(basedir)
53 54 except OSError as e:
54 55 if e.errno != errno.EEXIST:
55 56 raise
56 57 os.chdir(basedir)
57 58
58 59 # Delete virtualenvs and recreate
59 60 for venv in glob('venv-*'):
60 61 shutil.rmtree(venv)
61 62 for py in available_python_versions():
62 63 check_call(['virtualenv', '-p', py, '--system-site-packages', 'venv-%s' % py])
63 64 venvs.append((py, 'venv-%s' % py))
64 65
65 66 # Check out and update the repository
66 67 if not os.path.exists('ipython'):
67 68 check_call(['git', 'clone', ipy_repository])
68 69 os.chdir(repodir)
69 70 check_call(['git', 'checkout', 'master'])
70 71 check_call(['git', 'pull', ipy_repository, 'master'])
71 72 os.chdir(basedir)
72 73
73 74 missing_libs_re = re.compile(r"Tools and libraries NOT available at test time:\n"
74 75 r"\s*(.*?)\n")
75 76 def get_missing_libraries(log):
76 77 m = missing_libs_re.search(log)
77 78 if m:
78 79 return m.group(1)
79 80
80 81 def get_branch(repo, branch, owner, mergeable):
81 82 os.chdir(repodir)
82 83 if mergeable:
83 84 merged_branch = "%s-%s" % (owner, branch)
84 85 # Delete the branch first
85 86 call(['git', 'branch', '-D', merged_branch])
86 87 check_call(['git', 'checkout', '-b', merged_branch])
87 88 check_call(['git', 'pull', '--no-commit', repo, branch])
88 89 check_call(['git', 'commit', '-m', "merge %s/%s" % (repo, branch)])
89 90 else:
90 91 # Fetch the branch without merging it.
91 92 check_call(['git', 'fetch', repo, branch])
92 93 check_call(['git', 'checkout', 'FETCH_HEAD'])
93 94 os.chdir(basedir)
94 95
95 96 def run_tests(venv):
96 97 py = os.path.join(basedir, venv, 'bin', 'python')
97 98 print(py)
98 99 os.chdir(repodir)
99 100 # cleanup build-dir
100 101 if os.path.exists('build'):
101 102 shutil.rmtree('build')
102 103 check_call([py, 'setup.py', 'install'])
103 104 os.chdir(basedir)
104 105
105 106 iptest = os.path.join(basedir, venv, 'bin', 'iptest')
106 107 if not os.path.exists(iptest):
107 108 iptest = os.path.join(basedir, venv, 'bin', 'iptest3')
108 109
109 110 print("\nRunning tests, this typically takes a few minutes...")
110 111 try:
111 112 return True, check_output([iptest], stderr=STDOUT).decode('utf-8')
112 113 except CalledProcessError as e:
113 114 return False, e.output.decode('utf-8')
114 115
115 116 def markdown_format(pr, results_urls):
116 117 def format_result(py, passed, gist_url, missing_libraries):
117 118 s = "* %s: " % py
118 119 if passed:
119 120 s += "OK"
120 121 else:
121 122 s += "Failed, log at %s" % gist_url
122 123 if missing_libraries:
123 124 s += " (libraries not available: " + missing_libraries + ")"
124 125 return s
125 126
126 127 if pr['mergeable']:
127 128 com = pr['head']['sha'][:7] + " merged into master"
128 129 else:
129 130 com = pr['head']['sha'][:7] + " (can't merge cleanly)"
130 131 lines = ["**Test results for commit %s**" % com,
131 132 "Platform: " + sys.platform,
132 133 ""] + \
133 134 [format_result(*r) for r in results_urls] + \
134 135 ["",
135 136 "Not available for testing: " + ", ".join(unavailable_pythons)]
136 137 return "\n".join(lines)
137 138
138 139 def post_results_comment(pr, results, num):
139 140 body = markdown_format(pr, results)
140 141 gh_api.post_issue_comment(gh_project, num, body)
141 142
142 143 def print_results(pr, results_urls):
143 144 print("\n")
144 145 if pr['mergeable']:
145 146 print("**Test results for commit %s merged into master**" % pr['head']['sha'][:7])
146 147 else:
147 148 print("**Test results for commit %s (can't merge cleanly)**" % pr['head']['sha'][:7])
148 149 print("Platform:", sys.platform)
149 150 for py, passed, gist_url, missing_libraries in results_urls:
150 151 if passed:
151 152 print(py, ":", "OK")
152 153 else:
153 154 print(py, ":", "Failed")
154 155 print(" Test log:", gist_url)
155 156 if missing_libraries:
156 157 print(" Libraries not available:", missing_libraries)
157 158 print("Not available for testing:", ", ".join(unavailable_pythons))
158 159
159 160 def dump_results(num, results, pr):
160 161 with open(os.path.join(basedir, 'lastresults.pkl'), 'wb') as f:
161 162 pickle.dump((num, results, pr), f)
162 163
163 164 def load_results():
164 165 with open(os.path.join(basedir, 'lastresults.pkl'), 'rb') as f:
165 166 return pickle.load(f)
166 167
167 168 def save_logs(results, pr):
168 169 results_paths = []
169 170 for py, passed, log, missing_libraries in results:
170 171 if passed:
171 172 results_paths.append((py, passed, None, missing_libraries))
172 173 else:
173 174
174 175 result_locn = os.path.abspath(os.path.join('venv-%s' % py,
175 176 pr['head']['sha'][:7]+".log"))
176 177 with io.open(result_locn, 'w', encoding='utf-8') as f:
177 178 f.write(log)
178 179
179 180 results_paths.append((py, False, result_locn, missing_libraries))
180 181
181 182 return results_paths
182 183
183 184 def post_logs(results):
184 185 results_urls = []
185 186 for py, passed, log, missing_libraries in results:
186 187 if passed:
187 188 results_urls.append((py, passed, None, missing_libraries))
188 189 else:
189 190 result_locn = gh_api.post_gist(log, description='IPython test log',
190 191 filename="results.log", auth=True)
191 192 results_urls.append((py, False, result_locn, missing_libraries))
192 193
193 194 return results_urls
194 195
195 196 def test_pr(num, post_results=True):
196 197 # Get Github authorisation first, so that the user is prompted straight away
197 198 # if their login is needed.
198 199 if post_results:
199 200 gh_api.get_auth_token()
200 201
201 202 setup()
202 203 pr = gh_api.get_pull_request(gh_project, num)
203 204 get_branch(repo=pr['head']['repo']['clone_url'],
204 205 branch=pr['head']['ref'],
205 206 owner=pr['head']['repo']['owner']['login'],
206 207 mergeable=pr['mergeable'],
207 208 )
208 209
209 210 results = []
210 211 for py, venv in venvs:
211 212 tic = time.time()
212 213 passed, log = run_tests(venv)
213 214 elapsed = int(time.time() - tic)
214 215 print("Ran tests with %s in %is" % (py, elapsed))
215 216 missing_libraries = get_missing_libraries(log)
216 217 if passed:
217 218 results.append((py, True, None, missing_libraries))
218 219 else:
219 220 results.append((py, False, log, missing_libraries))
220 221
221 222 dump_results(num, results, pr)
222 223
223 224 results_paths = save_logs(results, pr)
224 225 print_results(pr, results_paths)
225 226
226 227 if post_results:
227 228 results_urls = post_logs(results)
228 229 post_results_comment(pr, results_urls, num)
229 230 print("(Posted to Github)")
230 231 else:
231 232 post_script = os.path.join(os.path.dirname(sys.argv[0]), "post_pr_test.py")
232 233 print("To post the results to Github, run", post_script)
233 234
234 235
235 236 if __name__ == '__main__':
236 237 import argparse
237 238 parser = argparse.ArgumentParser(description="Test an IPython pull request")
238 239 parser.add_argument('-p', '--publish', action='store_true',
239 240 help="Publish the results to Github")
240 241 parser.add_argument('number', type=int, help="The pull request number")
241 242
242 243 args = parser.parse_args()
243 244 test_pr(args.number, post_results=args.publish)
General Comments 0
You need to be logged in to leave comments. Login now