Add ProjectTestingInterface to horizon.

Horizon is the last project that doesn't have support for the common
Project Testing Interface. This gets horizon up to speed with the
other bits, but shouldn't break any of the existing interfaces.

Change-Id: I464c3b10d9708a0b7b5ffd42c88cd3cf515ef6a7
This commit is contained in:
Monty Taylor 2012-05-26 14:58:14 -04:00 committed by Gabriel Hurley
parent 4f7ee81b58
commit c93e7c06fd
14 changed files with 283 additions and 176 deletions

4
.gitignore vendored
View File

@ -5,6 +5,7 @@
.coverage*
.noseids
coverage.xml
nosetests.xml
pep8.txt
pylint.txt
reports
@ -13,5 +14,8 @@ openstack_dashboard/local/local_settings.py
docs/build/
docs/source/sourcecode
.venv
.tox
build
dist
AUTHORS
ChangeLog

57
AUTHORS
View File

@ -1,57 +0,0 @@
Alessio Ababilov <aababilov@griddynamics.com>
Andrews Medina <andrewsmedina@gmail.com>
Andy Chong <andycjw@gmail.com>
Anthony Young <sleepsonthefloor@gmail.com>
Arvind Somya <asomya@cisco.com>
Bernhard M. Wiedemann <bwiedemann@suse.de>
Carlo Truijllo <truijllo@crs4.it>
Chuck Short <chuck.short@canonical.com>
Cole Robinson <crobinso@redhat.com>
Dean Troyer <dtroyer@gmail.com>
Devin Carlen <devin.carlen@gmail.com>
Doug Doan <dougdoan@gmail.com>
Duncan McGreggor <duncan@dreamhost.com>
Emma Steimann <emmasteimann@gmail.com>
Erwan Gallen <dev@zinux.com>
Ewan Mellor <ewan.mellor@citrix.com>
Gabriel Hurley <gabriel@strikeawe.com>
Ghe Rivero <ghe@debian.org>
Greg Althaus <galthaus@austin.rr.com>
Hengqing Hu <hudayou@hotmail.com>
Ionuț Arțăriși <iartarisi@suse.cz>
Ivan Kolodyazhny <e0ne@e0ne.info>
J. Daniel Schmidt <jdsn@suse.de>
Jake Dahn <jake@ansolabs.com>
Jake Zukowski <jake@ponyloaf.com>
James E. Blair <jeblair@hp.com>
Jay Pipes <jaypipes@gmail.com>
Jeffrey Wilcox <jeffjapan@gmail.com>
Jesse Andrews <anotherjesse@gmail.com>
Jim Yeh <lemonlatte@gmail.com>
John Postlethwait <john.postlethwait@nebula.com>
Joseph Heck <heckj@mac.com>
Joshua McKenty <joshua@pistoncloud.com>
Julien Danjou <julien.danjou@enovance.com>
Ke Wu <ke.wu@ibeca.me>
Ken Pepple <ken.pepple@gmail.com>
Mark Gius <launchpad@markgius.com>
Michael Szilagyi <mszilagyi@gmail.com>
Mike Perez <thingee@gmail.com>
Mike Scherbakov <mihgen@gmail.com>
Monty Taylor <mordred@inaugust.com>
Neil Johnston <onewheeldrive.net@gmail.com>
Paul McMillan <paul.mcmillan@nebula.com>
Sam Morrison <sorrison@gmail.com>
Stephane Angot <sa@hydre.org>
termie <github@anarkystic.com>
Thierry Carrez <thierry@openstack.org>
Tihomir Trifonov <t.trifonov@gmail.com>
Todd Willey <todd@ansolabs.com>
Tom Fifield <fifieldt@unimelb.edu.au>
Tomasz 'Zen' Napierala <tomasz@napierala.org>
Tres Henry <tres@treshenry.net>
Vishvananda Ishaya <vishvananda@gmail.com>
Yuriy Taraday <yorik.sar@gmail.com>
ZhongYue Luo <lzyeval@gmail.com>
Ziad Sawalha <github@highbridgellc.com>
ZHANG Hua <zhuadl@cn.ibm.com>

View File

@ -4,6 +4,7 @@ recursive-include openstack_dashboard *.html *.js *.css *.less *.csv *.template
recursive-include tools *.py *.sh
include AUTHORS
include ChangeLog
include LICENSE
include Makefile
include manage.py

View File

View File

View File

@ -0,0 +1,200 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Utilities with minimum-depends for use in setup.py
"""
import os
import re
import subprocess
from setuptools.command import sdist
def parse_mailmap(mailmap='.mailmap'):
mapping = {}
if os.path.exists(mailmap):
fp = open(mailmap, 'r')
for l in fp:
l = l.strip()
if not l.startswith('#') and ' ' in l:
canonical_email, alias = [x for x in l.split(' ')
if x.startswith('<')]
mapping[alias] = canonical_email
return mapping
def canonicalize_emails(changelog, mapping):
"""Takes in a string and an email alias mapping and replaces all
instances of the aliases in the string with their real email.
"""
for alias, email in mapping.iteritems():
changelog = changelog.replace(alias, email)
return changelog
# Get requirements from the first file that exists
def get_reqs_from_files(requirements_files):
reqs_in = []
for requirements_file in requirements_files:
if os.path.exists(requirements_file):
return open(requirements_file, 'r').read().split('\n')
return []
def parse_requirements(requirements_files=['requirements.txt',
'tools/pip-requires']):
requirements = []
for line in get_reqs_from_files(requirements_files):
# For the requirements list, we need to inject only the portion
# after egg= so that distutils knows the package it's looking for
# such as:
# -e git://github.com/openstack/nova/master#egg=nova
if re.match(r'\s*-e\s+', line):
requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1',
line))
# such as:
# http://github.com/openstack/nova/zipball/master#egg=nova
elif re.match(r'\s*https?:', line):
requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1',
line))
# -f lines are for index locations, and don't get used here
elif re.match(r'\s*-f\s+', line):
pass
else:
requirements.append(line)
return requirements
def parse_dependency_links(requirements_files=['requirements.txt',
'tools/pip-requires']):
dependency_links = []
# dependency_links inject alternate locations to find packages listed
# in requirements
for line in get_reqs_from_files(requirements_files):
# skip comments and blank lines
if re.match(r'(\s*#)|(\s*$)', line):
continue
# lines with -e or -f need the whole line, minus the flag
if re.match(r'\s*-[ef]\s+', line):
dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line))
# lines that are only urls can go in unmolested
elif re.match(r'\s*https?:', line):
dependency_links.append(line)
return dependency_links
def write_requirements():
venv = os.environ.get('VIRTUAL_ENV', None)
if venv is not None:
with open("requirements.txt", "w") as req_file:
output = subprocess.Popen(["pip", "-E", venv, "freeze", "-l"],
stdout=subprocess.PIPE)
requirements = output.communicate()[0].strip()
req_file.write(requirements)
def _run_shell_command(cmd):
output = subprocess.Popen(["/bin/sh", "-c", cmd],
stdout=subprocess.PIPE)
return output.communicate()[0].strip()
def write_vcsversion(location):
"""Produce a vcsversion dict that mimics the old one produced by bzr.
"""
if os.path.isdir('.git'):
branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "'
branch_nick = _run_shell_command(branch_nick_cmd)
revid_cmd = "git rev-parse HEAD"
revid = _run_shell_command(revid_cmd).split()[0]
revno_cmd = "git log --oneline | wc -l"
revno = _run_shell_command(revno_cmd)
with open(location, 'w') as version_file:
version_file.write("""
# This file is automatically generated by setup.py, So don't edit it. :)
version_info = {
'branch_nick': '%s',
'revision_id': '%s',
'revno': %s
}
""" % (branch_nick, revid, revno))
def write_git_changelog():
"""Write a changelog based on the git changelog."""
if os.path.isdir('.git'):
git_log_cmd = 'git log --stat'
changelog = _run_shell_command(git_log_cmd)
mailmap = parse_mailmap()
with open("ChangeLog", "w") as changelog_file:
changelog_file.write(canonicalize_emails(changelog, mailmap))
def generate_authors():
"""Create AUTHORS file using git commits."""
jenkins_email = 'jenkins@review.openstack.org'
old_authors = 'AUTHORS.in'
new_authors = 'AUTHORS'
if os.path.isdir('.git'):
# don't include jenkins email address in AUTHORS file
git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
"grep -v " + jenkins_email)
changelog = _run_shell_command(git_log_cmd)
mailmap = parse_mailmap()
with open(new_authors, 'w') as new_authors_fh:
new_authors_fh.write(canonicalize_emails(changelog, mailmap))
if os.path.exists(old_authors):
with open(old_authors, "r") as old_authors_fh:
new_authors_fh.write('\n' + old_authors_fh.read())
def get_cmdclass():
"""Return dict of commands to run from setup.py."""
cmdclass = dict()
class LocalSDist(sdist.sdist):
"""Builds the ChangeLog and Authors files from VC first."""
def run(self):
write_git_changelog()
generate_authors()
# sdist.sdist is an old style class, can't use super()
sdist.sdist.run(self)
cmdclass['sdist'] = LocalSDist
# If Sphinx is installed on the box running setup.py,
# enable setup.py to build the documentation, otherwise,
# just ignore it
try:
from sphinx.setup_command import BuildDoc
class LocalBuildDoc(BuildDoc):
def run(self):
for builder in ['html', 'man']:
self.builder = builder
self.finalize_options()
BuildDoc.run(self)
cmdclass['build_sphinx'] = LocalBuildDoc
except ImportError:
pass
return cmdclass

View File

@ -1,64 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC
# Copyright 2012 Nebula Inc
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import commands
import unittest
def parse_mailmap(mailmap='.mailmap'):
mapping = {}
if os.path.exists(mailmap):
fp = open(mailmap, 'r')
for l in fp:
l = l.strip()
if not l.startswith('#') and ' ' in l:
canonical_email, alias = l.split(' ')
mapping[alias] = canonical_email
return mapping
def str_dict_replace(s, mapping):
for s1, s2 in mapping.iteritems():
s = s.replace(s1, s2)
return s
class AuthorsTestCase(unittest.TestCase):
def test_authors_up_to_date(self):
path_bits = (os.path.dirname(__file__), '..', '..')
root = os.path.normpath(os.path.join(*path_bits))
contributors = set()
missing = set()
authors_file = open(os.path.join(root, 'AUTHORS'), 'r').read()
if os.path.exists(os.path.join(root, '.git')):
mailmap = parse_mailmap(os.path.join(root, '.mailmap'))
for email in commands.getoutput('git log --format=%ae').split():
if not email:
continue
if "jenkins" in email and "openstack.org" in email:
continue
email = '<' + email + '>'
contributors.add(str_dict_replace(email, mailmap))
for contributor in contributors:
if not contributor in authors_file:
missing.add(contributor)
self.assertTrue(len(missing) == 0,
'%r not listed in AUTHORS file.' % missing)

7
openstack-common.conf Normal file
View File

@ -0,0 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
modules=setup
# The base module to hold the copy of openstack.common
base=horizon

View File

@ -6,7 +6,7 @@ set -o errexit
# Increment me any time the environment should be rebuilt.
# This includes dependncy changes, directory renames, etc.
# Simple integer secuence: 1, 2, 3...
environment_version=17
environment_version=18
#--------------------------------------------------------#
function usage {

4
setup.cfg Normal file
View File

@ -0,0 +1,4 @@
[nosetests]
verbosity=2
detailed-errors=1

View File

@ -21,60 +21,22 @@
import os
import re
from setuptools import setup, find_packages
import setuptools
from horizon import version
from horizon.openstack.common import setup
requires = setup.parse_requirements()
depend_links = setup.parse_dependency_links()
tests_require = setup.parse_requirements(['tools/test-requires'])
ROOT = os.path.dirname(__file__)
PIP_REQUIRES = os.path.join(ROOT, "tools", "pip-requires")
TEST_REQUIRES = os.path.join(ROOT, "tools", "test-requires")
def parse_requirements(*filenames):
"""
We generate our install_requires from the pip-requires and test-requires
files so that we don't have to maintain the dependency definitions in
two places.
"""
requirements = []
for f in filenames:
for line in open(f, 'r').read().split('\n'):
# Comment lines. Skip.
if re.match(r'(\s*#)|(\s*$)', line):
continue
# Editable matches. Put the egg name into our reqs list.
if re.match(r'\s*-e\s+', line):
pkg = re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', line)
requirements.append("%s" % pkg)
# File-based installs not supported/needed. Skip.
elif re.match(r'\s*-f\s+', line):
pass
else:
requirements.append(line)
return requirements
def parse_dependency_links(*filenames):
"""
We generate our dependency_links from the pip-requires and test-requires
files for the dependencies pulled from github (prepended with -e).
"""
dependency_links = []
for f in filenames:
for line in open(f, 'r').read().split('\n'):
if re.match(r'\s*-[ef]\s+', line):
line = re.sub(r'\s*-[ef]\s+', '', line)
line = re.sub(r'\s*git\+https', 'http', line)
line = re.sub(r'\.git#', '/tarball/master#', line)
dependency_links.append(line)
return dependency_links
def read(fname):
return open(os.path.join(ROOT, fname)).read()
setup(name="horizon",
setuptools.setup(name="horizon",
version=version.canonical_version_string(),
url='https://github.com/openstack/horizon/',
license='Apache 2.0',
@ -82,12 +44,13 @@ setup(name="horizon",
long_description=read('README.rst'),
author='OpenStack',
author_email='horizon@lists.launchpad.net',
packages=find_packages(),
packages=setuptools.find_packages(),
cmdclass=setup.get_cmdclass(),
include_package_data=True,
install_requires=requires,
tests_require=tests_require,
dependency_links=depend_links,
zip_safe=False,
install_requires=parse_requirements(PIP_REQUIRES),
tests_require=parse_requirements(TEST_REQUIRES),
dependency_links=parse_dependency_links(PIP_REQUIRES, TEST_REQUIRES),
classifiers=['Development Status :: 4 - Beta',
'Framework :: Django',
'Intended Audience :: Developers',

View File

@ -4,6 +4,6 @@ python-cloudfiles
python-dateutil
# Horizon Non-pip Requirements
-e git+https://github.com/openstack/python-novaclient.git#egg=python-novaclient
-e git+https://github.com/openstack/python-keystoneclient.git#egg=python-keystoneclient
-e git+https://github.com/openstack/python-glanceclient.git#egg=python-glanceclient
https://github.com/openstack/python-novaclient/zipball/master#egg=python-novaclient
https://github.com/openstack/python-keystoneclient/zipball/master#egg=python-keystoneclient
https://github.com/openstack/python-glanceclient/zipball/master#egg=python-glanceclient

View File

@ -1,14 +1,17 @@
distribute>=0.6.24
# Testing Requirements
coverage
django-nose
mox
netaddr
nose
nose-exclude
nosexcover
openstack.nose_plugin
pep8
pylint
distribute>=0.6.24
selenium
netaddr
# Docs Requirements
sphinx

46
tox.ini Normal file
View File

@ -0,0 +1,46 @@
[tox]
envlist = py26,py27,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
NOSE_WITH_OPENSTACK=1
NOSE_OPENSTACK_COLOR=1
NOSE_OPENSTACK_RED=0.05
NOSE_OPENSTACK_YELLOW=0.025
NOSE_OPENSTACK_SHOW_ELAPSED=1
deps = -r{toxinidir}/tools/pip-requires
-r{toxinidir}/tools/test-requires
commands = /bin/bash run_tests.sh -N
[testenv:pep8]
deps = pep8
commands = /bin/bash run_tests.sh -N --pep8
[testenv:venv]
commands = {posargs}
[testenv:cover]
commands = /bin/bash run_tests.sh -N --coverage
[tox:jenkins]
downloadcache = ~/cache/pip
[testenv:jenkins26]
setenv = NOSE_WITH_XUNIT=1
basepython = python2.6
[testenv:jenkins27]
setenv = NOSE_WITH_XUNIT=1
basepython = python2.7
[testenv:jenkinspep8]
setenv = NOSE_WITH_XUNIT=1
commands = /bin/bash run_tests.sh -N --pep8
[testenv:jenkinscover]
setenv = NOSE_WITH_XUNIT=1
commands = /bin/bash run_tests.sh -N --coverage
[testenv:jenkinsvenv]
setenv = NOSE_WITH_XUNIT=1
commands = {posargs}