requirements/playbooks/files/project-requirements-change.py
Brian Haley ea345875cd Improve terminology in the requirements tree
There is no real reason we should be using some of the
terms we do, they're outdated, and we're behind other
open-source projects in this respect. Let's switch to
using more inclusive terms in all possible places.

Change-Id: I90ac679c1745a31474c6f24ec7953624ad056d79
2024-05-14 10:08:34 +02:00

167 lines
5.4 KiB
Python
Executable File

#! /usr/bin/env python3
# Copyright (C) 2011 OpenStack, LLC.
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2013 OpenStack Foundation
#
# 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 argparse
import contextlib
import os
import re
import shlex
import shutil
import subprocess
import sys
import tempfile
from openstack_requirements import check # noqa
from openstack_requirements import project # noqa
from openstack_requirements import requirement # noqa
PYTHON_3_BRANCH = re.compile(r'^stable\/[u-z].*')
def run_command(cmd):
print(cmd)
cmd_list = shlex.split(str(cmd))
kwargs = {}
if sys.version_info >= (3, ):
kwargs = {
'encoding': 'utf-8',
'errors': 'surrogateescape',
}
p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, **kwargs)
(out, err) = p.communicate()
if p.returncode != 0:
raise SystemError(err)
return (out.strip(), err.strip())
_DEFAULT_REQS_DIR = os.path.expanduser(
'~/src/opendev.org/openstack/requirements')
def grab_args():
"""Grab and return arguments"""
parser = argparse.ArgumentParser(
description="Check if project requirements have changed"
)
parser.add_argument('--local', action='store_true',
help='check local changes (not yet in git)')
parser.add_argument('src_dir', help='directory to process')
parser.add_argument('branch', nargs='?', default='master',
help='target branch for diffs')
parser.add_argument('--zc', help='what zuul cloner to call')
parser.add_argument('--reqs', help='use a specified requirements tree',
default=None)
return parser.parse_args()
@contextlib.contextmanager
def tempdir():
try:
reqroot = tempfile.mkdtemp()
yield reqroot
finally:
shutil.rmtree(reqroot)
def main():
args = grab_args()
branch = args.branch
reqdir = args.reqs
print(sys.version_info)
if reqdir is None:
if args.local:
print('selecting default requirements directory for local mode')
reqdir = os.path.dirname(
os.path.dirname(
os.path.dirname(
os.path.abspath(sys.argv[0]))))
else:
print('selecting default requirements directory for normal mode')
reqdir = _DEFAULT_REQS_DIR
print('Branch: {}'.format(branch))
print('Source: {}'.format(args.src_dir))
print('Requirements: {}'.format(reqdir))
os.chdir(args.src_dir)
sha, _ = run_command('git log -n 1 --format=%H')
print('Patch under test: {}'.format(sha))
# build a list of requirements from the global list in the
# openstack/requirements project so we can match them to the changes
with tempdir():
with open(reqdir + '/global-requirements.txt', 'rt') as f:
global_reqs = check.get_global_reqs(f.read())
denylist = requirement.parse(
open(reqdir + '/denylist.txt', 'rt').read())
backports_file = reqdir + '/backports.txt'
if os.path.exists(backports_file):
backports = requirement.parse(open(backports_file, 'rt').read())
else:
backports = {}
cwd = os.getcwd()
# build a list of requirements in the proposed change,
# and check them for style violations while doing so
head_proj = project.read(cwd)
head_reqs = check.RequirementsList(sha, head_proj)
# Don't apply strict parsing rules to stable branches.
# Reasoning is:
# - devstack etc protect us from functional issues
# - we're backporting to stable, so guarding against
# aesthetics and DRY concerns is not our business anymore
# - if in future we have other not-functional linty style
# things to add, we don't want them to affect stable
# either.
head_strict = not branch.startswith('stable/')
head_reqs.process(strict=head_strict)
# Starting with Ussuri and later, we only need to be strict about
# Python 3 requirements.
python_3_branch = head_strict or PYTHON_3_BRANCH.match(branch)
failed = check.validate(
head_reqs,
denylist,
global_reqs,
list(backports.keys()),
allow_3_only=python_3_branch,
)
failed = (
check.validate_lower_constraints(
head_reqs,
head_proj['lower-constraints.txt'],
denylist,
)
or failed
)
# report the results
if failed or head_reqs.failed:
print("*** Incompatible requirement found!")
print("*** See https://docs.openstack.org/requirements/latest/")
sys.exit(1)
print("Updated requirements match openstack/requirements.")
if __name__ == '__main__':
main()