|
|
"""
|
|
|
This is a script for testing pull requests for IPython. It merges the pull
|
|
|
request with current master, installs and tests on all available versions of
|
|
|
Python, and posts the results to Gist if any tests fail.
|
|
|
|
|
|
Usage:
|
|
|
python test_pr.py 1657
|
|
|
"""
|
|
|
from __future__ import print_function
|
|
|
|
|
|
import errno
|
|
|
from glob import glob
|
|
|
import json
|
|
|
import os
|
|
|
import re
|
|
|
import shutil
|
|
|
from subprocess import call, check_call, check_output, PIPE, STDOUT, CalledProcessError
|
|
|
try:
|
|
|
from urllib.request import urlopen
|
|
|
except ImportError:
|
|
|
from urllib2 import urlopen
|
|
|
|
|
|
basedir = os.path.join(os.path.expanduser("~"), ".ipy_pr_tests")
|
|
|
repodir = os.path.join(basedir, "ipython")
|
|
|
ipy_repository = 'git://github.com/ipython/ipython.git'
|
|
|
|
|
|
supported_pythons = ['python2.6', 'python2.7', 'python3.1', 'python3.2']
|
|
|
unavailable_pythons = []
|
|
|
|
|
|
def available_python_versions():
|
|
|
"""Get the executable names of available versions of Python on the system.
|
|
|
"""
|
|
|
del unavailable_pythons[:]
|
|
|
for py in supported_pythons:
|
|
|
try:
|
|
|
check_call([py, '-c', 'import nose'], stdout=PIPE)
|
|
|
yield py
|
|
|
except (OSError, CalledProcessError):
|
|
|
unavailable_pythons.append(py)
|
|
|
|
|
|
venvs = []
|
|
|
|
|
|
def setup():
|
|
|
"""Prepare the repository and virtualenvs."""
|
|
|
global venvs
|
|
|
|
|
|
try:
|
|
|
os.mkdir(basedir)
|
|
|
except OSError as e:
|
|
|
if e.errno != errno.EEXIST:
|
|
|
raise
|
|
|
os.chdir(basedir)
|
|
|
|
|
|
# Delete virtualenvs and recreate
|
|
|
for venv in glob('venv-*'):
|
|
|
shutil.rmtree(venv)
|
|
|
for py in available_python_versions():
|
|
|
check_call(['virtualenv', '-p', py, '--system-site-packages', 'venv-%s' % py])
|
|
|
venvs.append((py, 'venv-%s' % py))
|
|
|
|
|
|
# Check out and update the repository
|
|
|
if not os.path.exists('ipython'):
|
|
|
check_call(['git', 'clone', ipy_repository])
|
|
|
os.chdir(repodir)
|
|
|
check_call(['git', 'checkout', 'master'])
|
|
|
check_call(['git', 'pull', ipy_repository, 'master'])
|
|
|
os.chdir(basedir)
|
|
|
|
|
|
def get_pull_request(num, project="ipython/ipython"):
|
|
|
url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
|
|
|
response = urlopen(url).read().decode('utf-8')
|
|
|
return json.loads(response)
|
|
|
|
|
|
missing_libs_re = re.compile(r"Tools and libraries NOT available at test time:\n"
|
|
|
r"\s*(.*?)\n")
|
|
|
def get_missing_libraries(log):
|
|
|
m = missing_libs_re.search(log)
|
|
|
if m:
|
|
|
return m.group(1)
|
|
|
|
|
|
def merge_branch(repo, branch, owner):
|
|
|
merged_branch = "%s-%s" % (owner, branch)
|
|
|
os.chdir(repodir)
|
|
|
# Delete the branch first
|
|
|
call(['git', 'branch', '-D', merged_branch])
|
|
|
check_call(['git', 'checkout', '-b', merged_branch])
|
|
|
check_call(['git', 'pull', repo, branch])
|
|
|
os.chdir(basedir)
|
|
|
|
|
|
def run_tests(venv):
|
|
|
py = os.path.join(basedir, venv, 'bin', 'python')
|
|
|
print(py)
|
|
|
os.chdir(repodir)
|
|
|
check_call([py, 'setup.py', 'install'])
|
|
|
os.chdir(basedir)
|
|
|
|
|
|
iptest = os.path.join(basedir, venv, 'bin', 'iptest')
|
|
|
if not os.path.exists(iptest):
|
|
|
iptest = os.path.join(basedir, venv, 'bin', 'iptest3')
|
|
|
|
|
|
print("\nRunning tests, this typically takes a few minutes...")
|
|
|
try:
|
|
|
return True, check_output([iptest], stderr=STDOUT).decode('utf-8')
|
|
|
except CalledProcessError as e:
|
|
|
return False, e.output
|
|
|
|
|
|
def post_gist(content, description='IPython test log', filename="results.log"):
|
|
|
"""Post some text to a Gist, and return the URL."""
|
|
|
post_data = json.dumps({
|
|
|
"description": description,
|
|
|
"public": True,
|
|
|
"files": {
|
|
|
filename: {
|
|
|
"content": content
|
|
|
}
|
|
|
}
|
|
|
}).encode('utf-8')
|
|
|
|
|
|
response = urlopen("https://api.github.com/gists", post_data)
|
|
|
response_data = json.loads(response.read().decode('utf-8'))
|
|
|
return response_data['html_url']
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
import sys
|
|
|
num = sys.argv[1]
|
|
|
setup()
|
|
|
pr = get_pull_request(num)
|
|
|
merge_branch(repo=pr['head']['repo']['clone_url'],
|
|
|
branch=pr['head']['ref'],
|
|
|
owner=pr['head']['repo']['owner']['login'])
|
|
|
|
|
|
results = []
|
|
|
for py, venv in venvs:
|
|
|
passed, log = run_tests(venv)
|
|
|
missing_libraries = get_missing_libraries(log)
|
|
|
if passed:
|
|
|
results.append((py, True, None, missing_libraries))
|
|
|
else:
|
|
|
gist_url = post_gist(log)
|
|
|
results.append((py, False, gist_url, missing_libraries))
|
|
|
|
|
|
print("\n")
|
|
|
print("**Test results for commit %s merged into master**" % pr['head']['sha'][:7])
|
|
|
print("Platform:", sys.platform)
|
|
|
for py, passed, gist_url, missing_libraries in results:
|
|
|
if passed:
|
|
|
print(py, ":", "OK")
|
|
|
else:
|
|
|
print(py, ":", "Failed")
|
|
|
print(" Test log:", gist_url)
|
|
|
if missing_libraries:
|
|
|
print(" Libraries not available:", missing_libraries)
|
|
|
print("Not available for testing:", ", ".join(unavailable_pythons))
|
|
|
|