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