From 3e200b082fb6078e959cf1491187a3a257a98243 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Fri, 21 May 2021 10:02:58 +0100 Subject: [PATCH] Switch cache to user cache folder - instead of in-repo .cache folder make use of ~/.cache - use a hash based on project_dir path to avoid cross-contamination between various projects Fixes: #1566 --- src/ansiblelint/__main__.py | 11 ++++++++++- src/ansiblelint/config.py | 1 + src/ansiblelint/prerun.py | 38 +++++++++++++++++++++---------------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/ansiblelint/__main__.py b/src/ansiblelint/__main__.py index 931782bec9..0cfdfd1a7d 100755 --- a/src/ansiblelint/__main__.py +++ b/src/ansiblelint/__main__.py @@ -21,6 +21,7 @@ """Command line implementation.""" import errno +import hashlib import logging import os import pathlib @@ -107,6 +108,14 @@ def initialize_options(arguments: Optional[List[str]] = None) -> None: options.warn_list = [normalize_tag(tag) for tag in options.warn_list] options.configured = True + # 6 chars of entropy should be enough + cache_key = hashlib.sha256( + os.path.abspath(options.project_dir).encode() + ).hexdigest()[:6] + options.cache_dir = "%s/ansible-lint/%s" % ( + os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache")), + cache_key, + ) def report_outcome( @@ -258,7 +267,7 @@ def main(argv: Optional[List[str]] = None) -> int: @contextmanager def _previous_revision() -> Iterator[None]: """Create or update a temporary workdir containing the previous revision.""" - worktree_dir = ".cache/old-rev" + worktree_dir = f"{options.cache_dir}/old-rev" revision = subprocess.run( ["git", "rev-parse", "HEAD^1"], check=True, diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 04dfd0d4f0..be8a8648c1 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -59,6 +59,7 @@ options = Namespace( + cache_dir=".cache", colored=True, configured=False, cwd=".", diff --git a/src/ansiblelint/prerun.py b/src/ansiblelint/prerun.py index 731b561db0..d47be907a3 100644 --- a/src/ansiblelint/prerun.py +++ b/src/ansiblelint/prerun.py @@ -111,7 +111,7 @@ def install_requirements(requirement: str) -> None: "role", "install", "--roles-path", - f"{options.project_dir}/.cache/roles", + f"{options.cache_dir}/roles", "-vr", f"{requirement}", ] @@ -136,7 +136,7 @@ def install_requirements(requirement: str) -> None: "collection", "install", "-p", - f"{options.project_dir}/.cache/collections", + f"{options.cache_dir}/collections", "-vr", f"{requirement}", ] @@ -245,13 +245,16 @@ def _install_galaxy_role() -> None: fqrn = f"{role_namespace}{role_name}" else: fqrn = pathlib.Path(".").absolute().name - p = pathlib.Path(f"{options.project_dir}/.cache/roles") + p = pathlib.Path(f"{options.cache_dir}/roles") p.mkdir(parents=True, exist_ok=True) link_path = p / fqrn # despite documentation stating that is_file() reports true for symlinks, # it appears that is_dir() reports true instead, so we rely on exits(). - if not link_path.exists(): - link_path.symlink_to(pathlib.Path("../..", target_is_directory=True)) + target = pathlib.Path(options.project_dir).absolute() + if not link_path.exists() or os.readlink(link_path) != target: + if link_path.exists(): + link_path.unlink() + link_path.symlink_to(target, target_is_directory=True) _logger.info( "Using %s symlink to current repository in order to enable Ansible to find the role using its expected full name.", link_path, @@ -265,10 +268,10 @@ def _prepare_ansible_paths() -> None: for path_list, path in ( (library_paths, "plugins/modules"), - (library_paths, f"{options.project_dir}/.cache/modules"), - (collection_list, f"{options.project_dir}/.cache/collections"), + (library_paths, f"{options.cache_dir}/modules"), + (collection_list, f"{options.cache_dir}/collections"), (roles_path, "roles"), - (roles_path, f"{options.project_dir}/.cache/roles"), + (roles_path, f"{options.cache_dir}/roles"), ): if path not in path_list and os.path.exists(path): path_list.append(path) @@ -283,14 +286,14 @@ def _make_module_stub(module_name: str) -> None: if re.match(r"^(\w+|\w+\.\w+\.[\.\w]+)$", module_name): parts = module_name.split(".") if len(parts) < 3: - path = f"{options.project_dir}/.cache/modules" - module_file = f"{options.project_dir}/.cache/modules/{module_name}.py" + path = f"{options.cache_dir}/modules" + module_file = f"{options.cache_dir}/modules/{module_name}.py" namespace = None collection = None else: namespace = parts[0] collection = parts[1] - path = f"{ options.project_dir }/.cache/collections/ansible_collections/{ namespace }/{ collection }/plugins/modules/{ '/'.join(parts[2:-1]) }" + path = f"{ options.cache_dir }/collections/ansible_collections/{ namespace }/{ collection }/plugins/modules/{ '/'.join(parts[2:-1]) }" module_file = f"{path}/{parts[-1]}.py" os.makedirs(path, exist_ok=True) _write_module_stub( @@ -336,9 +339,9 @@ def _perform_mockings() -> None: for role_name in options.mock_roles: if re.match(r"\w+\.\w+\.\w+$", role_name): namespace, collection, role_dir = role_name.split(".") - path = f".cache/collections/ansible_collections/{ namespace }/{ collection }/roles/{ role_dir }/" + path = f"{options.cache_dir}/collections/ansible_collections/{ namespace }/{ collection }/roles/{ role_dir }/" else: - path = f".cache/roles/{role_name}" + path = f"{options.cache_dir}/roles/{role_name}" os.makedirs(path, exist_ok=True) if options.mock_modules: @@ -357,9 +360,12 @@ def _perform_mockings() -> None: if not namespace or not collection: return p = pathlib.Path( - f"{options.project_dir}/.cache/collections/ansible_collections/{ namespace }" + f"{options.cache_dir}/collections/ansible_collections/{ namespace }" ) p.mkdir(parents=True, exist_ok=True) link_path = p / collection - if not link_path.exists(): - link_path.symlink_to(pathlib.Path("../../../..", target_is_directory=True)) + target = pathlib.Path(options.project_dir).absolute() + if not link_path.exists() or os.readlink(link_path) != target: + if link_path.exists(): + link_path.unlink() + link_path.symlink_to(target, target_is_directory=True)