diff --git a/MANIFEST.in b/MANIFEST.in index 22c8ce86785..e75acb70cde 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -29,10 +29,10 @@ recursive-include packaging * recursive-include test/ansible_test *.py Makefile recursive-include test/integration * recursive-include test/lib/ansible_test/config *.yml *.template -recursive-include test/lib/ansible_test/_data *.cfg *.ini *.ps1 *.txt *.yml coveragerc +recursive-include test/lib/ansible_test/_data *.cfg *.in *.ini *.ps1 *.txt *.yml coveragerc recursive-include test/lib/ansible_test/_util *.cfg *.json *.ps1 *.psd1 *.py *.sh *.txt *.yml recursive-include test/lib/ansible_test/_util/controller/sanity/validate-modules validate-modules -recursive-include test/sanity *.json *.py *.txt +recursive-include test/sanity *.in *.json *.py *.txt recursive-include test/support *.py *.ps1 *.psm1 *.cs exclude test/sanity/code-smell/botmeta.* exclude test/sanity/code-smell/release-names.* @@ -46,4 +46,5 @@ include changelogs/changelog.yaml recursive-include hacking/build_library *.py include hacking/build-ansible.py include hacking/test-module.py +include hacking/update-sanity-requirements.py include bin/* diff --git a/changelogs/fragments/ansible-test-sanity-requirements-update.yaml b/changelogs/fragments/ansible-test-sanity-requirements-update.yaml new file mode 100644 index 00000000000..4499b85e2ac --- /dev/null +++ b/changelogs/fragments/ansible-test-sanity-requirements-update.yaml @@ -0,0 +1,4 @@ +minor_changes: + - ansible-test - Declare public dependencies of ansible-core and use to limit unguarded imports in plugins. + - ansible-test - Requirements for the plugin import test are now frozen. + - ansible-test - Update sanity test requirements. diff --git a/docs/docsite/rst/dev_guide/testing/sanity/import.rst b/docs/docsite/rst/dev_guide/testing/sanity/import.rst index 045afee718c..49994092c71 100644 --- a/docs/docsite/rst/dev_guide/testing/sanity/import.rst +++ b/docs/docsite/rst/dev_guide/testing/sanity/import.rst @@ -60,9 +60,15 @@ Ansible allows the following unchecked imports from these specific directories: * ansible-core: * For ``lib/ansible/modules/`` and ``lib/ansible/module_utils/``, unchecked imports are only allowed from the Python standard library; - * For ``lib/ansible/plugins/``, unchecked imports are only allowed from the Python standard library, from dependencies of ansible-core, and from ansible-core itself; + * For ``lib/ansible/plugins/``, unchecked imports are only allowed from the Python standard library, from public dependencies of ansible-core, and from ansible-core itself; * collections: * For ``plugins/modules/`` and ``plugins/module_utils/``, unchecked imports are only allowed from the Python standard library; - * For other directories in ``plugins/`` (see `the community collection requirements `_ for a list), unchecked imports are only allowed from the Python standard library, from dependencies of ansible-core, and from ansible-core itself. + * For other directories in ``plugins/`` (see `the community collection requirements `_ for a list), unchecked imports are only allowed from the Python standard library, from public dependencies of ansible-core, and from ansible-core itself. + +Public dependencies of ansible-core are: + + * Jinja2 + * PyYAML + * MarkupSafe (as a dependency of Jinja2) diff --git a/hacking/update-sanity-requirements.py b/hacking/update-sanity-requirements.py new file mode 100755 index 00000000000..747f058880a --- /dev/null +++ b/hacking/update-sanity-requirements.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# PYTHON_ARGCOMPLETE_OK +"""Generate frozen sanity test requirements from source requirements files.""" + +from __future__ import annotations + +import argparse +import dataclasses +import pathlib +import subprocess +import tempfile +import typing as t +import venv + +try: + import argcomplete +except ImportError: + argcomplete = None + + +FILE = pathlib.Path(__file__).resolve() +ROOT = FILE.parent.parent +SELF = FILE.relative_to(ROOT) + + +@dataclasses.dataclass(frozen=True) +class SanityTest: + name: str + requirements_path: pathlib.Path + source_path: pathlib.Path + + def freeze_requirements(self) -> None: + with tempfile.TemporaryDirectory() as venv_dir: + venv.create(venv_dir, with_pip=True) + + python = pathlib.Path(venv_dir, 'bin', 'python') + pip = [python, '-m', 'pip', '--disable-pip-version-check'] + env = dict() + + pip_freeze = subprocess.run(pip + ['freeze'], env=env, check=True, capture_output=True, text=True) + + if pip_freeze.stdout: + raise Exception(f'Initial virtual environment is not empty:\n{pip_freeze.stdout}') + + subprocess.run(pip + ['install', 'wheel'], env=env, check=True) # make bdist_wheel available during pip install + subprocess.run(pip + ['install', '-r', self.source_path], env=env, check=True) + + pip_freeze = subprocess.run(pip + ['freeze'], env=env, check=True, capture_output=True, text=True) + + requirements = f'# edit "{self.source_path.name}" and generate with: {SELF} --test {self.name}\n{pip_freeze.stdout}' + + with open(self.requirements_path, 'w') as requirement_file: + requirement_file.write(requirements) + + @staticmethod + def create(path: pathlib.Path) -> SanityTest: + return SanityTest( + name=path.stem.replace('sanity.', '').replace('.requirements', ''), + requirements_path=path, + source_path=path.with_suffix('.in'), + ) + + +def main() -> None: + tests = find_tests() + + parser = argparse.ArgumentParser() + parser.add_argument( + '--test', + metavar='TEST', + dest='test_names', + action='append', + choices=[test.name for test in tests], + help='test requirements to update' + ) + + if argcomplete: + argcomplete.autocomplete(parser) + + args = parser.parse_args() + test_names: set[str] = set(args.test_names or []) + + tests = [test for test in tests if test.name in test_names] if test_names else tests + + for test in tests: + print(f'===[ {test.name} ]===') + test.freeze_requirements() + + +def find_tests() -> t.List[SanityTest]: + globs = ( + 'test/lib/ansible_test/_data/requirements/sanity.*.txt', + 'test/sanity/code-smell/*.requirements.txt', + ) + + tests: t.List[SanityTest] = [] + + for glob in globs: + tests.extend(get_tests(pathlib.Path(glob))) + + return sorted(tests, key=lambda test: test.name) + + +def get_tests(glob: pathlib.Path) -> t.List[SanityTest]: + path = pathlib.Path(ROOT, glob.parent) + pattern = glob.name + + return [SanityTest.create(item) for item in path.glob(pattern)] + + +if __name__ == '__main__': + main() diff --git a/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.in b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.in new file mode 100644 index 00000000000..80c769fbb37 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.in @@ -0,0 +1,3 @@ +jinja2 # ansible-core requirement +packaging # ansible-core requirement +pyyaml # ansible-core requirement diff --git a/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt index 660620dc7b8..105069f2e3a 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.ansible-doc.txt @@ -1,7 +1,6 @@ -jinja2 == 3.0.1 # ansible-core requirement -pyyaml == 5.4.1 # ansible-core requirement -packaging == 21.0 # ansible-doc requirement - -# dependencies -MarkupSafe == 2.0.1 -pyparsing == 2.4.7 +# edit "sanity.ansible-doc.in" and generate with: hacking/update-sanity-requirements.py --test ansible-doc +Jinja2==3.0.3 +MarkupSafe==2.0.1 +packaging==21.2 +pyparsing==2.4.7 +PyYAML==6.0 diff --git a/test/lib/ansible_test/_data/requirements/sanity.changelog.in b/test/lib/ansible_test/_data/requirements/sanity.changelog.in new file mode 100644 index 00000000000..3fcfbe08415 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.changelog.in @@ -0,0 +1,2 @@ +antsibull-changelog +docutils < 0.18 # match version required by sphinx in the docs-build sanity test diff --git a/test/lib/ansible_test/_data/requirements/sanity.changelog.txt b/test/lib/ansible_test/_data/requirements/sanity.changelog.txt index cb9f02f873a..b378b5f375d 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.changelog.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.changelog.txt @@ -1,9 +1,8 @@ -antsibull-changelog == 0.9.0 - -# dependencies -pyyaml == 5.4.1 -docutils == 0.17.1 -packaging == 21.0 -pyparsing == 2.4.7 -rstcheck == 3.3.1 -semantic-version == 2.8.5 +# edit "sanity.changelog.in" and generate with: hacking/update-sanity-requirements.py --test changelog +antsibull-changelog==0.12.0 +docutils==0.17.1 +packaging==21.2 +pyparsing==2.4.7 +PyYAML==6.0 +rstcheck==3.3.1 +semantic-version==2.8.5 diff --git a/test/lib/ansible_test/_data/requirements/sanity.import.in b/test/lib/ansible_test/_data/requirements/sanity.import.in new file mode 100644 index 00000000000..dea704eb04a --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.import.in @@ -0,0 +1 @@ +pyyaml # needed for yaml_to_json.py diff --git a/test/lib/ansible_test/_data/requirements/sanity.import.plugin.in b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.in new file mode 100644 index 00000000000..cec0eed3cd5 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.in @@ -0,0 +1,2 @@ +jinja2 # ansible-core requirement +pyyaml # ansible-core requirement diff --git a/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt new file mode 100644 index 00000000000..bf7050ddea7 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.import.plugin.txt @@ -0,0 +1,4 @@ +# edit "sanity.import.plugin.in" and generate with: hacking/update-sanity-requirements.py --test import.plugin +Jinja2==3.0.3 +MarkupSafe==2.0.1 +PyYAML==6.0 diff --git a/test/lib/ansible_test/_data/requirements/sanity.import.txt b/test/lib/ansible_test/_data/requirements/sanity.import.txt index d77a09d7f5c..e9645ea2dad 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.import.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.import.txt @@ -1 +1,2 @@ -pyyaml == 5.4.1 # needed for yaml_to_json.py +# edit "sanity.import.in" and generate with: hacking/update-sanity-requirements.py --test import +PyYAML==6.0 diff --git a/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.in b/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.in new file mode 100644 index 00000000000..c3726e8bfee --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.in @@ -0,0 +1 @@ +pyyaml diff --git a/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.txt b/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.txt index cc530e42c25..ba3a50284fd 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.integration-aliases.txt @@ -1 +1,2 @@ -pyyaml == 5.4.1 +# edit "sanity.integration-aliases.in" and generate with: hacking/update-sanity-requirements.py --test integration-aliases +PyYAML==6.0 diff --git a/test/lib/ansible_test/_data/requirements/sanity.pep8.in b/test/lib/ansible_test/_data/requirements/sanity.pep8.in new file mode 100644 index 00000000000..282a93fbd7c --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.pep8.in @@ -0,0 +1 @@ +pycodestyle diff --git a/test/lib/ansible_test/_data/requirements/sanity.pep8.txt b/test/lib/ansible_test/_data/requirements/sanity.pep8.txt index 86f73fba11e..cc0f1afd4f4 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.pep8.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.pep8.txt @@ -1 +1,2 @@ -pycodestyle == 2.6.0 +# edit "sanity.pep8.in" and generate with: hacking/update-sanity-requirements.py --test pep8 +pycodestyle==2.8.0 diff --git a/test/lib/ansible_test/_data/requirements/sanity.pylint.in b/test/lib/ansible_test/_data/requirements/sanity.pylint.in new file mode 100644 index 00000000000..2344fb4d359 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.pylint.in @@ -0,0 +1,2 @@ +pylint == 2.9.3 # currently vetted version +pyyaml # needed for collection_detail.py diff --git a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt index 7332d162381..85fdedb8465 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.pylint.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.pylint.txt @@ -1,10 +1,9 @@ -pylint == 2.9.3 -pyyaml == 5.4.1 # needed for collection_detail.py - -# dependencies -astroid == 2.6.6 -isort == 5.9.3 -lazy-object-proxy == 1.6.0 -mccabe == 0.6.1 -toml == 0.10.2 -wrapt == 1.12.1 +# edit "sanity.pylint.in" and generate with: hacking/update-sanity-requirements.py --test pylint +astroid==2.6.6 +isort==5.10.1 +lazy-object-proxy==1.6.0 +mccabe==0.6.1 +pylint==2.9.3 +PyYAML==6.0 +toml==0.10.2 +wrapt==1.12.1 diff --git a/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.in b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.in new file mode 100644 index 00000000000..edd96991dde --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.in @@ -0,0 +1,2 @@ +pyyaml +voluptuous diff --git a/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt index 1281a04528b..3324a389551 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.runtime-metadata.txt @@ -1,2 +1,3 @@ -pyyaml == 5.4.1 -voluptuous == 0.12.1 +# edit "sanity.runtime-metadata.in" and generate with: hacking/update-sanity-requirements.py --test runtime-metadata +PyYAML==6.0 +voluptuous==0.12.2 diff --git a/test/lib/ansible_test/_data/requirements/sanity.validate-modules.in b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.in new file mode 100644 index 00000000000..efe940041c5 --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.in @@ -0,0 +1,3 @@ +jinja2 # ansible-core requirement +pyyaml # needed for collection_detail.py +voluptuous diff --git a/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt index 4b1d5f05d0a..32f14fea853 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.validate-modules.txt @@ -1,6 +1,5 @@ -jinja2 == 3.0.1 # ansible-core requirement -pyyaml == 5.4.1 # needed for collection_detail.py -voluptuous == 0.12.1 - -# dependencies -MarkupSafe == 2.0.1 +# edit "sanity.validate-modules.in" and generate with: hacking/update-sanity-requirements.py --test validate-modules +Jinja2==3.0.3 +MarkupSafe==2.0.1 +PyYAML==6.0 +voluptuous==0.12.2 diff --git a/test/lib/ansible_test/_data/requirements/sanity.yamllint.in b/test/lib/ansible_test/_data/requirements/sanity.yamllint.in new file mode 100644 index 00000000000..b2c729ca4de --- /dev/null +++ b/test/lib/ansible_test/_data/requirements/sanity.yamllint.in @@ -0,0 +1 @@ +yamllint diff --git a/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt b/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt index 67384863940..78806991551 100644 --- a/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt +++ b/test/lib/ansible_test/_data/requirements/sanity.yamllint.txt @@ -1,5 +1,4 @@ -yamllint == 1.26.0 - -# dependencies -pathspec == 0.9.0 -pyyaml == 5.4.1 +# edit "sanity.yamllint.in" and generate with: hacking/update-sanity-requirements.py --test yamllint +pathspec==0.9.0 +PyYAML==6.0 +yamllint==1.26.3 diff --git a/test/lib/ansible_test/_internal/commands/sanity/__init__.py b/test/lib/ansible_test/_internal/commands/sanity/__init__.py index 542e078ad18..b380509e6b1 100644 --- a/test/lib/ansible_test/_internal/commands/sanity/__init__.py +++ b/test/lib/ansible_test/_internal/commands/sanity/__init__.py @@ -242,7 +242,7 @@ def command_sanity(args): # type: (SanityConfig) -> None elif isinstance(test, SanitySingleVersion): # single version sanity tests use the controller python test_profile = host_state.controller_profile - virtualenv_python = create_sanity_virtualenv(args, test_profile.python, test.name, context=test.name) + virtualenv_python = create_sanity_virtualenv(args, test_profile.python, test.name) if virtualenv_python: virtualenv_yaml = check_sanity_virtualenv_yaml(virtualenv_python) @@ -1077,10 +1077,8 @@ def create_sanity_virtualenv( args, # type: SanityConfig python, # type: PythonConfig name, # type: str - ansible=False, # type: bool coverage=False, # type: bool minimize=False, # type: bool - context=None, # type: t.Optional[str] ): # type: (...) -> t.Optional[VirtualPythonConfig] """Return an existing sanity virtual environment matching the requested parameters or create a new one.""" commands = collect_requirements( # create_sanity_virtualenv() @@ -1088,13 +1086,11 @@ def create_sanity_virtualenv( controller=True, virtualenv=False, command=None, - # used by import tests - ansible=ansible, - cryptography=ansible, + ansible=False, + cryptography=False, coverage=coverage, minimize=minimize, - # used by non-import tests - sanity=context, + sanity=name, ) if commands: diff --git a/test/lib/ansible_test/_internal/commands/sanity/import.py b/test/lib/ansible_test/_internal/commands/sanity/import.py index 19a67b4b290..4ab638ab441 100644 --- a/test/lib/ansible_test/_internal/commands/sanity/import.py +++ b/test/lib/ansible_test/_internal/commands/sanity/import.py @@ -87,8 +87,17 @@ class ImportTest(SanityMultipleVersion): """Sanity test for proper import exception handling.""" def filter_targets(self, targets): # type: (t.List[TestTarget]) -> t.List[TestTarget] """Return the given list of test targets, filtered to include only those relevant for the test.""" + if data_context().content.is_ansible: + # all of ansible-core must pass the import test, not just plugins/modules + # modules/module_utils will be tested using the module context + # everything else will be tested using the plugin context + paths = ['lib/ansible'] + else: + # only plugins/modules must pass the import test for collections + paths = list(data_context().content.plugin_paths.values()) + return [target for target in targets if os.path.splitext(target.path)[1] == '.py' and - any(is_subdir(target.path, path) for path in data_context().content.plugin_paths.values())] + any(is_subdir(target.path, path) for path in paths)] @property def needs_pypi(self): # type: () -> bool @@ -112,9 +121,9 @@ class ImportTest(SanityMultipleVersion): messages = [] - for import_type, test, controller in ( - ('module', _get_module_test(True), False), - ('plugin', _get_module_test(False), True), + for import_type, test in ( + ('module', _get_module_test(True)), + ('plugin', _get_module_test(False)), ): if import_type == 'plugin' and python.version in REMOTE_ONLY_PYTHON_VERSIONS: continue @@ -124,7 +133,7 @@ class ImportTest(SanityMultipleVersion): if not data and not args.prime_venvs: continue - virtualenv_python = create_sanity_virtualenv(args, python, f'{self.name}.{import_type}', ansible=controller, coverage=args.coverage, minimize=True) + virtualenv_python = create_sanity_virtualenv(args, python, f'{self.name}.{import_type}', coverage=args.coverage, minimize=True) if not virtualenv_python: display.warning(f'Skipping sanity test "{self.name}" on Python {python.version} due to missing virtual environment support.') @@ -143,7 +152,7 @@ class ImportTest(SanityMultipleVersion): ) if data_context().content.collection: - external_python = create_sanity_virtualenv(args, args.controller_python, self.name, context=self.name) + external_python = create_sanity_virtualenv(args, args.controller_python, self.name) env.update( SANITY_COLLECTION_FULL_NAME=data_context().content.collection.full_name, diff --git a/test/sanity/code-smell/botmeta.requirements.in b/test/sanity/code-smell/botmeta.requirements.in new file mode 100644 index 00000000000..edd96991dde --- /dev/null +++ b/test/sanity/code-smell/botmeta.requirements.in @@ -0,0 +1,2 @@ +pyyaml +voluptuous diff --git a/test/sanity/code-smell/botmeta.requirements.txt b/test/sanity/code-smell/botmeta.requirements.txt index 1281a04528b..69f4b4bd0d4 100644 --- a/test/sanity/code-smell/botmeta.requirements.txt +++ b/test/sanity/code-smell/botmeta.requirements.txt @@ -1,2 +1,3 @@ -pyyaml == 5.4.1 -voluptuous == 0.12.1 +# edit "botmeta.requirements.in" and generate with: hacking/update-sanity-requirements.py --test botmeta +PyYAML==6.0 +voluptuous==0.12.2 diff --git a/test/sanity/code-smell/deprecated-config.requirements.in b/test/sanity/code-smell/deprecated-config.requirements.in new file mode 100644 index 00000000000..859c4ee7e63 --- /dev/null +++ b/test/sanity/code-smell/deprecated-config.requirements.in @@ -0,0 +1,2 @@ +jinja2 # ansible-core requirement +pyyaml diff --git a/test/sanity/code-smell/deprecated-config.requirements.txt b/test/sanity/code-smell/deprecated-config.requirements.txt index a3a33e6c205..ca5e9115aff 100644 --- a/test/sanity/code-smell/deprecated-config.requirements.txt +++ b/test/sanity/code-smell/deprecated-config.requirements.txt @@ -1,5 +1,4 @@ -jinja2 == 3.0.1 # ansible-core requirement -pyyaml == 5.4.1 - -# dependencies -MarkupSafe == 2.0.1 +# edit "deprecated-config.requirements.in" and generate with: hacking/update-sanity-requirements.py --test deprecated-config +Jinja2==3.0.3 +MarkupSafe==2.0.1 +PyYAML==6.0 diff --git a/test/sanity/code-smell/docs-build.requirements.in b/test/sanity/code-smell/docs-build.requirements.in new file mode 100644 index 00000000000..87210630cc8 --- /dev/null +++ b/test/sanity/code-smell/docs-build.requirements.in @@ -0,0 +1,8 @@ +jinja2 +pyyaml +resolvelib < 0.6.0 +sphinx == 4.2.0 +sphinx-notfound-page +sphinx-ansible-theme +straight.plugin +antsibull diff --git a/test/sanity/code-smell/docs-build.requirements.txt b/test/sanity/code-smell/docs-build.requirements.txt index 36fc363a0e4..1462ae9aa5f 100644 --- a/test/sanity/code-smell/docs-build.requirements.txt +++ b/test/sanity/code-smell/docs-build.requirements.txt @@ -1,50 +1,50 @@ -jinja2 == 3.0.1 -pyyaml == 5.4.1 -resolvelib == 0.5.4 -sphinx == 2.1.2 -sphinx-notfound-page == 0.7.1 -sphinx-ansible-theme == 0.8.0 -straight.plugin == 1.5.0 -antsibull == 0.26.0 - -# dependencies -MarkupSafe == 2.0.1 -aiofiles == 0.7.0 -aiohttp == 3.7.4.post0 -alabaster == 0.7.12 -ansible-pygments == 0.1.0 -antsibull-changelog == 0.9.0 -async-timeout == 3.0.1 -asyncio-pool == 0.5.2 -attrs == 21.2.0 -babel == 2.9.1 -certifi == 2021.5.30 -chardet == 4.0.0 -charset-normalizer == 2.0.5 -docutils == 0.17.1 -idna == 2.5 -imagesize == 1.2.0 -multidict == 5.1.0 -packaging == 21.0 -perky == 0.5.5 -pydantic == 1.8.2 -pygments == 2.10.0 -pyparsing == 2.4.7 -pytz == 2021.1 -requests == 2.26.0 -rstcheck == 3.3.1 -semantic-version == 2.8.5 -sh == 1.14.2 -six == 1.16.0 -snowballstemmer == 2.1.0 -sphinx-rtd-theme == 1.0.0 -sphinxcontrib-applehelp == 1.0.2 -sphinxcontrib-devhelp == 1.0.2 -sphinxcontrib-htmlhelp == 2.0.0 -sphinxcontrib-jsmath == 1.0.1 -sphinxcontrib-qthelp == 1.0.3 -sphinxcontrib-serializinghtml == 1.1.5 -twiggy == 0.5.1 -typing-extensions == 3.10.0.2 -urllib3 == 1.26.6 -yarl == 1.6.3 +# edit "docs-build.requirements.in" and generate with: hacking/update-sanity-requirements.py --test docs-build +aiofiles==0.7.0 +aiohttp==3.8.0 +aiosignal==1.2.0 +alabaster==0.7.12 +ansible-pygments==0.1.0 +antsibull==0.39.2 +antsibull-changelog==0.12.0 +async-timeout==4.0.1 +asyncio-pool==0.5.2 +attrs==21.2.0 +Babel==2.9.1 +certifi==2021.10.8 +charset-normalizer==2.0.7 +docutils==0.17.1 +frozenlist==1.2.0 +idna==3.3 +imagesize==1.3.0 +Jinja2==3.0.3 +MarkupSafe==2.0.1 +multidict==5.2.0 +packaging==21.2 +perky==0.5.5 +pydantic==1.8.2 +Pygments==2.10.0 +pyparsing==2.4.7 +pytz==2021.3 +PyYAML==6.0 +requests==2.26.0 +resolvelib==0.5.4 +rstcheck==3.3.1 +semantic-version==2.8.5 +sh==1.14.2 +six==1.16.0 +snowballstemmer==2.1.0 +Sphinx==4.2.0 +sphinx-ansible-theme==0.8.0 +sphinx-notfound-page==0.8 +sphinx-rtd-theme==1.0.0 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.5 +straight.plugin==1.5.0 +Twiggy==0.5.1 +typing-extensions==3.10.0.2 +urllib3==1.26.7 +yarl==1.7.2 diff --git a/test/sanity/code-smell/package-data.requirements.in b/test/sanity/code-smell/package-data.requirements.in new file mode 100644 index 00000000000..68c2248e581 --- /dev/null +++ b/test/sanity/code-smell/package-data.requirements.in @@ -0,0 +1,7 @@ +docutils < 0.18 # match version required by sphinx in the docs-build sanity test +jinja2 +pyyaml # ansible-core requirement +resolvelib < 0.6.0 +rstcheck +straight.plugin +antsibull-changelog diff --git a/test/sanity/code-smell/package-data.requirements.txt b/test/sanity/code-smell/package-data.requirements.txt index 8055b3c5c70..8b08b9a34e1 100644 --- a/test/sanity/code-smell/package-data.requirements.txt +++ b/test/sanity/code-smell/package-data.requirements.txt @@ -1,13 +1,12 @@ -docutils == 0.17.1 -jinja2 == 3.0.1 -packaging == 21.0 -pyyaml == 5.4.1 # ansible-core requirement -resolvelib == 0.5.4 # ansible-core requirement -rstcheck == 3.3.1 -straight.plugin == 1.5.0 -antsibull-changelog == 0.9.0 - -# dependencies -MarkupSafe == 2.0.1 -pyparsing == 2.4.7 -semantic-version == 2.8.5 +# edit "package-data.requirements.in" and generate with: hacking/update-sanity-requirements.py --test package-data +antsibull-changelog==0.12.0 +docutils==0.17.1 +Jinja2==3.0.3 +MarkupSafe==2.0.1 +packaging==21.2 +pyparsing==2.4.7 +PyYAML==6.0 +resolvelib==0.5.4 +rstcheck==3.3.1 +semantic-version==2.8.5 +straight.plugin==1.5.0 diff --git a/test/sanity/code-smell/release-names.requirements.in b/test/sanity/code-smell/release-names.requirements.in new file mode 100644 index 00000000000..c3726e8bfee --- /dev/null +++ b/test/sanity/code-smell/release-names.requirements.in @@ -0,0 +1 @@ +pyyaml diff --git a/test/sanity/code-smell/release-names.requirements.txt b/test/sanity/code-smell/release-names.requirements.txt index cc530e42c25..41ee6fa8d77 100644 --- a/test/sanity/code-smell/release-names.requirements.txt +++ b/test/sanity/code-smell/release-names.requirements.txt @@ -1 +1,2 @@ -pyyaml == 5.4.1 +# edit "release-names.requirements.in" and generate with: hacking/update-sanity-requirements.py --test release-names +PyYAML==6.0 diff --git a/test/sanity/code-smell/rstcheck.requirements.in b/test/sanity/code-smell/rstcheck.requirements.in new file mode 100644 index 00000000000..f997391281c --- /dev/null +++ b/test/sanity/code-smell/rstcheck.requirements.in @@ -0,0 +1,3 @@ +sphinx == 4.2.0 # required for full rstcheck functionality, installed first to get the correct docutils version +rstcheck +jinja2 # ansible-core requirement diff --git a/test/sanity/code-smell/rstcheck.requirements.txt b/test/sanity/code-smell/rstcheck.requirements.txt index 071bc5a1f1d..f89165398ae 100644 --- a/test/sanity/code-smell/rstcheck.requirements.txt +++ b/test/sanity/code-smell/rstcheck.requirements.txt @@ -1,27 +1,25 @@ -rstcheck == 3.3.1 -sphinx == 2.1.2 # required for full functionality - -# dependencies -Jinja2 == 3.0.1 -MarkupSafe == 2.0.1 -Pygments == 2.10.0 -alabaster == 0.7.12 -babel == 2.9.1 -certifi == 2021.5.30 -charset-normalizer == 2.0.5 -docutils == 0.17.1 -idna == 2.5 -imagesize == 1.2.0 -packaging == 21.0 -pyparsing == 2.4.7 -pytz == 2021.1 -requests == 2.26.0 -rstcheck == 3.3.1 -snowballstemmer == 2.1.0 -sphinxcontrib-applehelp == 1.0.2 -sphinxcontrib-devhelp == 1.0.2 -sphinxcontrib-htmlhelp == 2.0.0 -sphinxcontrib-jsmath == 1.0.1 -sphinxcontrib-qthelp == 1.0.3 -sphinxcontrib-serializinghtml == 1.1.5 -urllib3 == 1.26.6 +# edit "rstcheck.requirements.in" and generate with: hacking/update-sanity-requirements.py --test rstcheck +alabaster==0.7.12 +Babel==2.9.1 +certifi==2021.10.8 +charset-normalizer==2.0.7 +docutils==0.17.1 +idna==3.3 +imagesize==1.3.0 +Jinja2==3.0.3 +MarkupSafe==2.0.1 +packaging==21.2 +Pygments==2.10.0 +pyparsing==2.4.7 +pytz==2021.3 +requests==2.26.0 +rstcheck==3.3.1 +snowballstemmer==2.1.0 +Sphinx==4.2.0 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.5 +urllib3==1.26.7 diff --git a/test/sanity/code-smell/test-constraints.py b/test/sanity/code-smell/test-constraints.py index 8383235e150..fd6f17d578e 100644 --- a/test/sanity/code-smell/test-constraints.py +++ b/test/sanity/code-smell/test-constraints.py @@ -1,6 +1,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import os import re import sys @@ -21,6 +22,11 @@ def main(): non_sanity_requirements = set() for path, requirements in requirements.items(): + filename = os.path.basename(path) + + is_sanity = filename.startswith('sanity.') or filename.endswith('.requirements.txt') + is_constraints = path == constraints_path + for lineno, line, requirement in requirements: if not requirement: print('%s:%d:%d: cannot parse requirement: %s' % (path, lineno, 1, line)) @@ -28,14 +34,10 @@ def main(): name = requirement.group('name').lower() raw_constraints = requirement.group('constraints') - raw_markers = requirement.group('markers') constraints = raw_constraints.strip() - markers = raw_markers.strip() comment = requirement.group('comment') - is_sanity = path.startswith('test/lib/ansible_test/_data/requirements/sanity.') or path.startswith('test/sanity/code-smell/') is_pinned = re.search('^ *== *[0-9.]+(\\.post[0-9]+)?$', constraints) - is_constraints = path == constraints_path if is_sanity: sanity = frozen_sanity.setdefault(name, []) @@ -43,20 +45,19 @@ def main(): elif not is_constraints: non_sanity_requirements.add(name) + if is_sanity: + if not is_pinned: + # sanity test requirements must be pinned + print('%s:%d:%d: sanity test requirement (%s%s) must be frozen (use `==`)' % (path, lineno, 1, name, raw_constraints)) + + continue + if constraints and not is_constraints: allow_constraints = 'sanity_ok' in comment - if is_sanity and is_pinned and not markers: - allow_constraints = True # sanity tests can use frozen requirements without markers - if not allow_constraints: - if is_sanity: - # sanity test requirements which need constraints should be frozen to maintain consistent test results - # use of anything other than frozen constraints will make evaluation of conflicts extremely difficult - print('%s:%d:%d: sanity test constraint (%s%s) must be frozen (use `==`)' % (path, lineno, 1, name, raw_constraints)) - else: - # keeping constraints for tests other than sanity tests in one file helps avoid conflicts - print('%s:%d:%d: put the constraint (%s%s) in `%s`' % (path, lineno, 1, name, raw_constraints, constraints_path)) + # keeping constraints for tests other than sanity tests in one file helps avoid conflicts + print('%s:%d:%d: put the constraint (%s%s) in `%s`' % (path, lineno, 1, name, raw_constraints, constraints_path)) for name, requirements in frozen_sanity.items(): if len(set(req[3].group('constraints').strip() for req in requirements)) != 1: diff --git a/test/sanity/code-smell/update-bundled.requirements.in b/test/sanity/code-smell/update-bundled.requirements.in new file mode 100644 index 00000000000..748809f75c4 --- /dev/null +++ b/test/sanity/code-smell/update-bundled.requirements.in @@ -0,0 +1 @@ +packaging diff --git a/test/sanity/code-smell/update-bundled.requirements.txt b/test/sanity/code-smell/update-bundled.requirements.txt index 101e3fdb556..93330e34836 100644 --- a/test/sanity/code-smell/update-bundled.requirements.txt +++ b/test/sanity/code-smell/update-bundled.requirements.txt @@ -1,4 +1,3 @@ -packaging == 21.0 - -# dependencies -pyparsing == 2.4.7 +# edit "update-bundled.requirements.in" and generate with: hacking/update-sanity-requirements.py --test update-bundled +packaging==21.2 +pyparsing==2.4.7 diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index d056a698ce0..d692cc2c70f 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -4,6 +4,39 @@ docs/docsite/rst/locales/ja/LC_MESSAGES/dev_guide.po no-smart-quotes # Translat examples/scripts/ConfigureRemotingForAnsible.ps1 pslint:PSCustomUseLiteralPath examples/scripts/upgrade_to_ps3.ps1 pslint:PSCustomUseLiteralPath examples/scripts/upgrade_to_ps3.ps1 pslint:PSUseApprovedVerbs +lib/ansible/cli/galaxy.py import-3.8 # unguarded indirect resolvelib import +lib/ansible/galaxy/collection/__init__.py import-3.8 # unguarded resolvelib import +lib/ansible/galaxy/collection/concrete_artifact_manager.py import-3.8 # unguarded resolvelib import +lib/ansible/galaxy/collection/galaxy_api_proxy.py import-3.8 # unguarded resolvelib imports +lib/ansible/galaxy/dependency_resolution/__init__.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/dataclasses.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/errors.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/providers.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/reporters.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/resolvers.py import-3.8 # circular imports +lib/ansible/galaxy/dependency_resolution/versioning.py import-3.8 # circular imports +lib/ansible/cli/galaxy.py import-3.9 # unguarded indirect resolvelib import +lib/ansible/galaxy/collection/__init__.py import-3.9 # unguarded resolvelib import +lib/ansible/galaxy/collection/concrete_artifact_manager.py import-3.9 # unguarded resolvelib import +lib/ansible/galaxy/collection/galaxy_api_proxy.py import-3.9 # unguarded resolvelib imports +lib/ansible/galaxy/dependency_resolution/__init__.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/dataclasses.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/errors.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/providers.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/reporters.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/resolvers.py import-3.9 # circular imports +lib/ansible/galaxy/dependency_resolution/versioning.py import-3.9 # circular imports +lib/ansible/cli/galaxy.py import-3.10 # unguarded indirect resolvelib import +lib/ansible/galaxy/collection/__init__.py import-3.10 # unguarded resolvelib import +lib/ansible/galaxy/collection/concrete_artifact_manager.py import-3.10 # unguarded resolvelib import +lib/ansible/galaxy/collection/galaxy_api_proxy.py import-3.10 # unguarded resolvelib imports +lib/ansible/galaxy/dependency_resolution/__init__.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/dataclasses.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/errors.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/providers.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/reporters.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/resolvers.py import-3.10 # circular imports +lib/ansible/galaxy/dependency_resolution/versioning.py import-3.10 # circular imports lib/ansible/cli/scripts/ansible_connection_cli_stub.py shebang lib/ansible/config/base.yml no-unwanted-files lib/ansible/executor/playbook_executor.py pylint:disallowed-name