Skip to content

Commit

Permalink
Avoid creating .cache directory in cwd (#1419)
Browse files Browse the repository at this point in the history
* Avoid creating .cache directory in cwd

Instead of always using cwd for the .cache folder, we now try to use
the project_dir for that, which defaults to the location of
.ansiblelint config file.

If there is no config file found and git fails to report a project
root, it will use ~/.cache

* Update docs/usage.rst

Co-authored-by: Amin Vakil <[email protected]>

Co-authored-by: Amin Vakil <[email protected]>
  • Loading branch information
ssbarnea and Amin Vakil authored Mar 3, 2021
1 parent 696e982 commit 2bb568c
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 13 deletions.
13 changes: 13 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ associated tags.
:cwd: ..
:returncode: 0

Temporary files
---------------

As part of the execution, the linter will likely need to create a cache of
installed or mocked roles, collections and modules. This is done inside
``{project_dir}/.cache`` folder. The project directory is either given as a
command line argument, determined by location of the configuration
file, git project top-level directiory or user home directory as fallback.
In order to speed-up reruns, the linter does not clean this folder by itself.

If you are using git, you will likely want to add this folder to your
``.gitignore`` file.

Progressive mode
----------------

Expand Down
19 changes: 10 additions & 9 deletions src/ansiblelint/_prerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def prepare_environment() -> None:
"role",
"install",
"--roles-path",
".cache/roles",
f"{options.project_dir}/.cache/roles",
"-vr",
"requirements.yml",
]
Expand All @@ -124,7 +124,7 @@ def prepare_environment() -> None:
"collection",
"install",
"-p",
".cache/collections",
f"{options.project_dir}/.cache/collections",
"-vr",
"requirements.yml",
]
Expand Down Expand Up @@ -175,7 +175,7 @@ def _install_galaxy_role() -> None:
file=sys.stderr,
)
sys.exit(INVALID_PREREQUISITES_RC)
p = pathlib.Path(".cache/roles")
p = pathlib.Path(f"{options.project_dir}/.cache/roles")
p.mkdir(parents=True, exist_ok=True)
link_path = p / f"{role_author}.{role_name}"
# despite documentation stating that is_file() reports true for symlinks,
Expand All @@ -195,10 +195,10 @@ def _prepare_ansible_paths() -> None:

for path_list, path in (
(library_paths, "plugins/modules"),
(library_paths, ".cache/modules"),
(collection_list, ".cache/collections"),
(library_paths, f"{options.project_dir}/.cache/modules"),
(collection_list, f"{options.project_dir}/.cache/collections"),
(roles_path, "roles"),
(roles_path, ".cache/roles"),
(roles_path, f"{options.project_dir}/.cache/roles"),
):
if path not in path_list and os.path.exists(path):
path_list.append(path)
Expand All @@ -212,7 +212,7 @@ def _make_module_stub(module_name: str) -> None:
# a.b.c is treated a collection
if re.match(r"\w+\.\w+\.\w+", module_name):
namespace, collection, module_file = module_name.split(".")
path = f".cache/collections/ansible_collections/{ namespace }/{ collection }/plugins/modules"
path = f"{ options.project_dir }/.cache/collections/ansible_collections/{ namespace }/{ collection }/plugins/modules"
os.makedirs(path, exist_ok=True)
_write_module_stub(
filename=f"{path}/{module_file}.py",
Expand All @@ -227,9 +227,10 @@ def _make_module_stub(module_name: str) -> None:
)
sys.exit(INVALID_CONFIG_RC)
else:
os.makedirs(".cache/modules", exist_ok=True)
os.makedirs(f"{options.project_dir}/.cache/modules", exist_ok=True)
_write_module_stub(
filename=f".cache/modules/{module_name}.py", name=module_name
filename=f"{options.project_dir}/.cache/modules/{module_name}.py",
name=module_name,
)


Expand Down
22 changes: 20 additions & 2 deletions src/ansiblelint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
DEFAULT_RULESDIR,
INVALID_CONFIG_RC,
)
from ansiblelint.file_utils import expand_path_vars
from ansiblelint.file_utils import expand_path_vars, guess_project_dir, normpath

_logger = logging.getLogger(__name__)
_PATH_VARS = [
Expand Down Expand Up @@ -82,6 +82,8 @@ def load_config(config_file: str) -> Dict[Any, Any]:
except yaml.YAMLError as e:
_logger.error(e)
sys.exit(INVALID_CONFIG_RC)

config['config_file'] = config_path
# TODO(ssbarnea): implement schema validation for config file
if isinstance(config, list):
_logger.error(
Expand All @@ -92,6 +94,7 @@ def load_config(config_file: str) -> Dict[Any, Any]:

config_dir = os.path.dirname(config_path)
expand_to_normalized_paths(config, config_dir)

return config


Expand Down Expand Up @@ -164,6 +167,13 @@ def get_cli_parser() -> argparse.ArgumentParser:
" of violations compared with previous git commit. This "
"feature works only in git repositories.",
)
parser.add_argument(
'--project-dir',
dest='project_dir',
default=None,
help="Location of project/repository, autodetected based on location "
" of configuration file.",
)
parser.add_argument(
'-r',
action=AbspathArgAction,
Expand Down Expand Up @@ -289,7 +299,7 @@ def merge_config(file_config, cli_config: Namespace) -> Namespace:
'mock_roles': [],
}

scalar_map = {"loop_var_prefix": None}
scalar_map = {"loop_var_prefix": None, "project_dir": None}

if not file_config:
# use defaults if we don't have a config file and the commandline
Expand Down Expand Up @@ -337,6 +347,14 @@ def get_config(arguments: List[str]) -> Namespace:

options.rulesdirs = get_rules_dirs(options.rulesdir, options.use_default_rules)

if not options.project_dir:
project_dir = os.path.dirname(
os.path.abspath(
options.config_file or f"{guess_project_dir()}/.ansiblelint"
)
)
options.project_dir = normpath(project_dir)

return config


Expand Down
1 change: 1 addition & 0 deletions src/ansiblelint/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
mock_roles=[],
loop_var_prefix=None,
offline=False,
project_dir=None,
extra_vars=None,
skip_action_validation=True,
)
Expand Down
15 changes: 15 additions & 0 deletions src/ansiblelint/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@ def get_yaml_files(options: Namespace) -> Dict[str, Any]:
return OrderedDict.fromkeys(sorted(out))


def guess_project_dir() -> str:
"""Return detected project dir or user home directory."""
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True,
check=False,
)

if result.returncode != 0:
return str(Path.home())
return result.stdout[0]


def expand_dirs_in_lintables(lintables: Set[Lintable]) -> None:
"""Return all recognized lintables within given directory."""
all_files = get_yaml_files(options)
Expand Down
4 changes: 2 additions & 2 deletions test/TestCliRolePaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_run_role_name_with_prefix(self):
result = run_ansible_lint(role_path, cwd=cwd)
assert len(result.stdout) == 0
assert (
"Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles:.cache/roles"
"Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles"
in result.stderr
)
assert result.returncode == 0
Expand All @@ -120,7 +120,7 @@ def test_run_role_name_from_meta(self):
result = run_ansible_lint(role_path, cwd=cwd)
assert len(result.stdout) == 0
assert (
"Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles:.cache/roles"
"Added ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:roles"
in result.stderr
)
assert result.returncode == 0
Expand Down
5 changes: 5 additions & 0 deletions test/TestCommandLineInvocationSameAsConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def _fake_pathlib_resolve(self):
file_config = cli.load_config(config)

for key, val in file_config.items():

# config_file does not make sense in file_config
if key == 'config_file':
continue

if key in {'exclude_paths', 'rulesdir'}:
val = [Path(p) for p in val]
assert val == getattr(options, key)
Expand Down

0 comments on commit 2bb568c

Please sign in to comment.