From 7fb906f6d4f3ab4e8b331ea6370ce6b52261323d Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Thu, 23 Jul 2015 17:18:50 +0000 Subject: [PATCH] Refactor validation script Move some of the validation code into a real python package so we can more easily reuse it. Make the validation script reprint all errors, for cases where it has checked a lot of files and the original messages might have been mixed in with other output. Fix the launchpad project name for tooz so the validation script will pass. Change-Id: I17534d460dd9ed19d10e48a0ef5d28dac3ab0fe7 --- deliverables/liberty/tooz.yaml | 2 +- openstack_releases/__init__.py | 0 openstack_releases/cmds/__init__.py | 0 .../cmds}/validate.py | 66 +++++++------------ openstack_releases/gitutils.py | 53 +++++++++++++++ setup.cfg | 7 ++ tox.ini | 2 +- 7 files changed, 87 insertions(+), 43 deletions(-) create mode 100644 openstack_releases/__init__.py create mode 100644 openstack_releases/cmds/__init__.py rename {tools => openstack_releases/cmds}/validate.py (67%) create mode 100644 openstack_releases/gitutils.py diff --git a/deliverables/liberty/tooz.yaml b/deliverables/liberty/tooz.yaml index 47ebb5de2a..7d09be504e 100644 --- a/deliverables/liberty/tooz.yaml +++ b/deliverables/liberty/tooz.yaml @@ -1,5 +1,5 @@ --- -launchpad: tooz +launchpad: python-tooz releases: - version: 1.19.0 projects: diff --git a/openstack_releases/__init__.py b/openstack_releases/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_releases/cmds/__init__.py b/openstack_releases/cmds/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/validate.py b/openstack_releases/cmds/validate.py similarity index 67% rename from tools/validate.py rename to openstack_releases/cmds/validate.py index 73608580f3..f232bfd168 100755 --- a/tools/validate.py +++ b/openstack_releases/cmds/validate.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -# # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -23,8 +21,6 @@ from __future__ import print_function import argparse import glob import re -import subprocess -import sys import requests import yaml @@ -33,23 +29,11 @@ import yaml from requests.packages import urllib3 urllib3.disable_warnings() +from openstack_releases import gitutils + + DEFAULT_RELEASE = 'liberty' -CGIT_TEMPLATE = 'http://git.openstack.org/cgit/%s/commit/?id=%s' - - -def find_modified_deliverable_files(): - "Return a list of files modified by the most recent commit." - results = subprocess.check_output( - ['git', 'show', '--name-only', '--pretty=format:'] - ) - filenames = [ - l.strip() - for l in results.splitlines() - if l.startswith('deliverables/') - ] - return filenames - def is_a_hash(val): "Return bool indicating if val looks like a valid hash." @@ -66,13 +50,13 @@ def main(): ) args = parser.parse_args() - filenames = args.input or find_modified_deliverable_files() + filenames = args.input or gitutils.find_modified_deliverable_files() if not filenames: print('no modified deliverable files, validating all releases from %s' % DEFAULT_RELEASE) filenames = glob.glob('deliverables/' + DEFAULT_RELEASE + '/*.yaml') - num_errors = 0 + errors = [] for filename in filenames: print('\nChecking %s' % filename) @@ -83,14 +67,14 @@ def main(): try: lp_name = deliverable_info['launchpad'] except KeyError: - num_errors += 1 + errors.append('No launchpad project given in %s' % filename) print('no launchpad project name given') else: print('launchpad project %s ' % lp_name, end='') lp_resp = requests.get('https://api.launchpad.net/1.0/' + lp_name) if (lp_resp.status_code // 100) == 4: print('MISSING') - num_errors += 1 + errors.append('Launchpad project %s does not exist' % lp_name) else: print('found') @@ -103,24 +87,24 @@ def main(): if not is_a_hash(project['hash']): print('NOT A SHA HASH') - num_errors += 1 - else: - url = CGIT_TEMPLATE % (project['repo'], - project['hash']) - response = requests.get(url) - missing_commit = ( - (response.status_code // 100 != 2) - or 'Bad object id' in response.text + errors.append( + ('%(repo)s version %(version)s release from ' + '%(hash)r, which is not a hash') % project ) - print('MISSING' if missing_commit else 'found') - if missing_commit: - num_errors += 1 + else: + exists = gitutils.commit_has_merged( + project['repo'], project['hash'], + ) + if not exists: + print('MISSING') + errors.append('No commit %(hash)r in %(repo)r' + % project) + else: + print('found') - if num_errors: - print('\n%s errors found' % num_errors) + if errors: + print('\n%s errors found' % len(errors)) + for e in errors: + print(e) - return 1 if num_errors else 0 - - -if __name__ == '__main__': - sys.exit(main()) + return 1 if errors else 0 diff --git a/openstack_releases/gitutils.py b/openstack_releases/gitutils.py new file mode 100644 index 0000000000..dba66c9c51 --- /dev/null +++ b/openstack_releases/gitutils.py @@ -0,0 +1,53 @@ +# 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. + +import subprocess + +import requests + +# Disable warnings about insecure connections. +from requests.packages import urllib3 +urllib3.disable_warnings() + +CGIT_TEMPLATE = 'http://git.openstack.org/cgit/%s/commit/?id=%s' + + +def find_modified_deliverable_files(): + "Return a list of files modified by the most recent commit." + results = subprocess.check_output( + ['git', 'show', '--name-only', '--pretty=format:'] + ) + filenames = [ + l.strip() + for l in results.splitlines() + if l.startswith('deliverables/') + ] + return filenames + + +def commit_has_merged(repo, hash): + """Return boolean specifying whether the hash exists in the repository. + + Uses a cgit query instead of looking locally to avoid cloning a + repository or having Depends-On settings in a commit message allow + someone to fool the check. + + """ + url = CGIT_TEMPLATE % (repo, hash) + response = requests.get(url) + missing_commit = ( + (response.status_code // 100 != 2) + or 'Bad object id' in response.text + ) + return not missing_commit diff --git a/setup.cfg b/setup.cfg index fde8138dd6..4ad190eb93 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,3 +16,10 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 + +[files] +packages = openstack_releases + +[entry_points] +console_scripts = + validate-request = openstack_releases.cmds.validate:main diff --git a/tox.ini b/tox.ini index af21882798..98ac5b6544 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ setenv = deps = -r{toxinidir}/requirements.txt [testenv:validate] -commands = {toxinidir}/tools/validate.py {posargs} +commands = validate-request {posargs} [testenv:pep8] deps = flake8