2f4cb354f7
Introduced changes: - pre-commit config and rules - Add pre-commit to pep8 gate, Flake8 is covered in the pre-commit hooks. - Applying fixes for pre-commit compliance in all code. Also commit hash will be used instead of version tags in pre-commit to prevend arbitrary code from running in developer's machines. pre-commit will be used to: - trailing whitespace; - Replaces or checks mixed line ending (mixed-line-ending); - Forbid files which have a UTF-8 byte-order marker (check-byte-order-marker); - Checks that non-binary executables have a proper shebang (check-executables-have-shebangs); - Check for files that contain merge conflict strings (check-merge-conflict); - Check for debugger imports and py37+ breakpoint() calls in python source (debug-statements); - Attempts to load all yaml files to verify syntax (check-yaml); - Run flake8 checks (flake8) (local) For further details about tests please refer to: https://github.com/pre-commit/pre-commit-hooks Change-Id: Icd2a9bded756b2bcba1fb7da6abe48503fd9b2d6 Signed-off-by: Moisés Guimarães de Medeiros <moguimar@redhat.com>
211 lines
6.5 KiB
Python
Executable File
211 lines
6.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# 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.
|
|
"""Run unit tests for projects that use a library.
|
|
"""
|
|
|
|
import glob
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
from oslo_config import cfg
|
|
from oslotest.tools import config as tconfig
|
|
from pbr import packaging
|
|
import pkg_resources
|
|
|
|
|
|
def find_all_projects(repo_root):
|
|
"""Scan the checked out repositories for all available projects."""
|
|
pattern = os.path.join(repo_root, 'openstack/*')
|
|
candidates = glob.glob(pattern)
|
|
prefix_len = len(repo_root)
|
|
return [
|
|
c[prefix_len:].lstrip('/')
|
|
for c in candidates
|
|
if os.path.isdir(c)
|
|
]
|
|
|
|
|
|
def find_consuming_projects(lib_name, repo_root, projects):
|
|
"""Filter the list of projects
|
|
|
|
Filter the list of projects to only include entries that use the library.
|
|
"""
|
|
for p in projects:
|
|
consumer = False
|
|
for base in packaging.get_requirements_files():
|
|
req_file = os.path.join(repo_root, p, base)
|
|
for req in packaging.parse_requirements([req_file]):
|
|
try:
|
|
parsed_req = pkg_resources.Requirement.parse(req)
|
|
req_name = parsed_req.project_name
|
|
except ValueError:
|
|
continue
|
|
if req_name == lib_name:
|
|
consumer = True
|
|
yield p
|
|
break
|
|
if consumer:
|
|
break
|
|
|
|
|
|
def main():
|
|
conf = tconfig.get_config_parser()
|
|
conf.register_cli_opt(
|
|
cfg.StrOpt(
|
|
'library-under-test',
|
|
short='l',
|
|
default='',
|
|
help=('the name of the library being tested; '
|
|
'defaults to current dir'),
|
|
)
|
|
)
|
|
conf.register_cli_opt(
|
|
cfg.BoolOpt(
|
|
'update',
|
|
short='u',
|
|
default=False,
|
|
help='update consumers before running tests',
|
|
)
|
|
)
|
|
conf.register_cli_opt(
|
|
cfg.BoolOpt(
|
|
'verbose',
|
|
short='v',
|
|
default=False,
|
|
help='print verbose output',
|
|
)
|
|
)
|
|
conf.register_cli_opt(
|
|
cfg.StrOpt(
|
|
'ref',
|
|
short='r',
|
|
default='HEAD',
|
|
help='the commit reference to test; defaults to HEAD',
|
|
)
|
|
)
|
|
conf.register_cli_opt(
|
|
cfg.MultiStrOpt(
|
|
'env',
|
|
short='e',
|
|
default=['py27', 'pep8'],
|
|
help=('the name of the tox environment to test; '
|
|
'defaults to py27 and pep8'),
|
|
)
|
|
)
|
|
conf.register_cli_opt(
|
|
cfg.MultiStrOpt(
|
|
'consumer',
|
|
positional=True,
|
|
default=[],
|
|
help='the name of a project to test with; may be repeated',
|
|
)
|
|
)
|
|
tconfig.parse_arguments(conf)
|
|
|
|
repo_root = os.path.expanduser(conf.repo_root)
|
|
|
|
# Figure out which library is being tested
|
|
lib_name = conf.library_under_test
|
|
if not lib_name:
|
|
if conf.verbose:
|
|
print('finding library name')
|
|
lib_name = subprocess.check_output(
|
|
['python', 'setup.py', '--name']
|
|
).strip()
|
|
lib_dir = os.getcwd()
|
|
else:
|
|
lib_dir = os.path.join(repo_root, 'openstack', lib_name)
|
|
print('testing %s in %s' % (lib_name, lib_dir))
|
|
|
|
projects = set(conf.consumer)
|
|
if not projects:
|
|
# TODO(dhellmann): Need to update this to look at gerrit, so
|
|
# we can check out the projects we want to test with.
|
|
if conf.verbose:
|
|
print('defaulting to all projects under %s/openstack' % repo_root)
|
|
projects = find_all_projects(repo_root)
|
|
|
|
# Filter out projects that do not require the library under test
|
|
before = len(projects)
|
|
projects = list(find_consuming_projects(lib_name, repo_root, projects))
|
|
after = len(projects)
|
|
if (after < before) and conf.verbose:
|
|
print('ignoring %s projects that do not use %s'
|
|
% (before - after, lib_name))
|
|
|
|
projects = list(sorted(projects))
|
|
if not projects:
|
|
print('ERROR: found no projects using %s' % lib_name)
|
|
return 1
|
|
if conf.verbose:
|
|
print('preparing to test %s projects' % after)
|
|
|
|
# Make sure the lib being tested is set to the reference intended.
|
|
if conf.ref != 'HEAD':
|
|
if conf.verbose:
|
|
print('ensuring %s is updated to %s' % (lib_name, conf.ref))
|
|
subprocess.check_call(
|
|
['git', 'checkout', conf.ref],
|
|
cwd=lib_dir,
|
|
)
|
|
|
|
git_quiet = ['-q'] if not conf.verbose else []
|
|
|
|
failures = []
|
|
for p in projects:
|
|
if conf.verbose:
|
|
print()
|
|
proj_dir = os.path.join(repo_root, p)
|
|
if conf.update:
|
|
if conf.verbose:
|
|
print('updating %s with "git pull"' % p)
|
|
subprocess.Popen(
|
|
['git', 'pull'] + git_quiet,
|
|
cwd=proj_dir,
|
|
).communicate()
|
|
p_log_name = p.split('/')[-1].replace('.', '-')
|
|
for e in conf.env:
|
|
log_name = 'cross-test-%s-%s.log' % (p_log_name, e)
|
|
with open(log_name, 'w') as log_file:
|
|
print('testing %s in %s, logging to %s' % (e, p, log_name),
|
|
end=' ')
|
|
sys.stdout.flush()
|
|
command = ['oslo_run_cross_tests', proj_dir, e]
|
|
log_file.write('running: %s\n' % ' '.join(command))
|
|
log_file.flush() # since Popen is going to use the fd directly
|
|
cmd = subprocess.Popen(
|
|
command,
|
|
cwd=lib_dir,
|
|
stdout=log_file,
|
|
stderr=log_file
|
|
)
|
|
cmd.communicate()
|
|
log_file.write('\nexit code: %s\n' % cmd.returncode)
|
|
if cmd.returncode:
|
|
print('FAIL')
|
|
failures.append((p, e, cmd.returncode))
|
|
else:
|
|
print('PASS')
|
|
|
|
if failures:
|
|
print('\nFAILED %d jobs' % len(failures))
|
|
return 1
|
|
print('\nPASSED all jobs')
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|