From 4da99464f57638d0ab324b7a091953a21d90bcea Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 17 Feb 2021 10:51:31 +0000 Subject: [PATCH] Recognize standalone galaxy roles Identify when run with a standalone galaxy roles and attempt to make them available. This change may produce a specific error when 'author' field inside galaxy_info is missing or not matching namespace requirements. Fixes: #1329 --- src/ansiblelint/_prerun.py | 35 +++++++++++++++++++++++++++++++++++ src/ansiblelint/constants.py | 1 + src/ansiblelint/loaders.py | 10 ++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/ansiblelint/loaders.py diff --git a/src/ansiblelint/_prerun.py b/src/ansiblelint/_prerun.py index 30e5722bff5..ec54cb3f14f 100644 --- a/src/ansiblelint/_prerun.py +++ b/src/ansiblelint/_prerun.py @@ -1,4 +1,5 @@ import os +import pathlib import re import subprocess import sys @@ -13,7 +14,18 @@ ANSIBLE_MISSING_RC, ANSIBLE_MOCKED_MODULE, INVALID_CONFIG_RC, + INVALID_PREREQUISITES_RC, ) +from ansiblelint.loaders import yaml_from_file + +MISSING_GALAXY_NAMESPACE = """\ +A valid role namespace is required, edit meta/main.yml and include a valid namespace inside the author field: + +galaxy_info: + author: your_galaxy_namespace + +See: https://galaxy.ansible.com/docs/contributing/namespaces.html#galaxy-namespace-limitations +""" def check_ansible_presence() -> None: @@ -87,10 +99,33 @@ def prepare_environment() -> None: if run.returncode != 0: sys.exit(run.returncode) + _install_galaxy_role() _perform_mockings() _prepare_ansible_paths() +def _install_galaxy_role() -> None: + """Detect standalone galaxy role and installs it.""" + if not os.path.exists("meta/main.yml"): + return + yaml = yaml_from_file("meta/main.yml") + if 'galaxy_info' not in 'yaml': + return + role_name = yaml['galaxy_info'].get('role_name', None) + role_author = yaml['galaxy_info'].get('author', None) + if not role_name: + role_name = os.path.dirname(".") + role_name = re.sub(r'^{0}'.format(re.escape('ansible-role-')), '', role_name) + if not role_author or not re.match(r"[\w\d_]{2,}", role_name): + print(MISSING_GALAXY_NAMESPACE, file=sys.stderr) + sys.exit(INVALID_PREREQUISITES_RC) + p = pathlib.Path(".cache/roles") + p.mkdir(parents=True, exist_ok=True) + link_path = p / f"{role_author}.{role_name}" + if not link_path.exists: + link_path.symlink_to(pathlib.Path("../..", target_is_directory=True)) + + def _prepare_ansible_paths() -> None: """Configure Ansible environment variables.""" library_paths: List[str] = [] diff --git a/src/ansiblelint/constants.py b/src/ansiblelint/constants.py index df020ea45eb..cfb8289b5a0 100644 --- a/src/ansiblelint/constants.py +++ b/src/ansiblelint/constants.py @@ -15,6 +15,7 @@ INVALID_CONFIG_RC = 2 ANSIBLE_FAILURE_RC = 3 ANSIBLE_MISSING_RC = 4 +INVALID_PREREQUISITES_RC = 10 # Minimal version of Ansible we support for runtime ANSIBLE_MIN_VERSION = "2.9" diff --git a/src/ansiblelint/loaders.py b/src/ansiblelint/loaders.py new file mode 100644 index 00000000000..262b4266def --- /dev/null +++ b/src/ansiblelint/loaders.py @@ -0,0 +1,10 @@ +"""Utilities for loading various files.""" +from typing import Any + +import yaml + + +def yaml_from_file(filepath: str) -> Any: + """Return a loaded YAML file.""" + with open(filepath) as content: + return yaml.load(content, Loader=yaml.FullLoader)