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