Skip to content

Commit 51644b7

Browse files
go165go165rockygeekz
authored
fix: avoid project cache creation in offline mode (#5066)
Signed-off-by: go165 <196723798+go165@users.noreply.github.com> Co-authored-by: go165 <196723798+go165@users.noreply.github.com> Co-authored-by: RAKESH S <124438543+rockygeekz@users.noreply.github.com>
1 parent 2a3eefe commit 51644b7

4 files changed

Lines changed: 101 additions & 13 deletions

File tree

src/ansiblelint/__main__.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,26 +143,28 @@ def initialize_options(arguments: list[str] | None = None) -> BaseFileLock | Non
143143
or options.list_rules
144144
or options.list_tags
145145
):
146-
options.cache_dir = get_cache_dir(pathlib.Path(options.project_dir))
146+
options.cache_dir = get_cache_dir(
147+
pathlib.Path(options.project_dir),
148+
isolated=not options.offline,
149+
)
147150

148151
options.project_dir = Path(options.project_dir).resolve().as_posix()
149152

150153
# add a lock file so we do not have two instances running inside at the same time
151-
if options.cache_dir:
154+
if options.cache_dir and not options.offline:
152155
options.cache_dir.mkdir(parents=True, exist_ok=True)
153156

154157
# lock file can only be used if cache_dir is set and writable
155-
if not options.offline: # pragma: no cover
156-
cache_dir_lock = FileLock(
157-
f"{options.cache_dir}/.lock",
158+
cache_dir_lock = FileLock(
159+
f"{options.cache_dir}/.lock",
160+
)
161+
try:
162+
cache_dir_lock.acquire(timeout=180)
163+
except Timeout: # pragma: no cover
164+
_logger.error( # noqa: TRY400
165+
"Timeout waiting for another instance of ansible-lint to release the lock.",
158166
)
159-
try:
160-
cache_dir_lock.acquire(timeout=180)
161-
except Timeout: # pragma: no cover
162-
_logger.error( # noqa: TRY400
163-
"Timeout waiting for another instance of ansible-lint to release the lock.",
164-
)
165-
sys.exit(RC.LOCK_TIMEOUT)
167+
sys.exit(RC.LOCK_TIMEOUT)
166168

167169
# Avoid extra output noise from Ansible about using devel versions
168170
if "ANSIBLE_DEVEL_WARNING" not in os.environ: # pragma: no branch

src/ansiblelint/app.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, options: Options):
4949
# Without require_module, our _set_collections_basedir may fail
5050
self.runtime = Runtime(
5151
project_dir=Path(options.project_dir),
52-
isolated=True,
52+
isolated=not options.offline,
5353
require_module=True,
5454
verbosity=options.verbosity,
5555
)
@@ -403,6 +403,19 @@ def _add_collections_path_if_needed(
403403
collections_paths.insert(0, str(mock_path))
404404

405405

406+
def _add_module_path_if_needed(
407+
options: Options,
408+
module_paths: list[str],
409+
) -> None:
410+
"""Add plain mock modules path to module_paths if module mocks exist."""
411+
if options.cache_dir and any(
412+
len(module_name.split(".")) < 3 for module_name in options.mock_modules
413+
):
414+
mock_path = options.cache_dir / "modules"
415+
if str(mock_path) not in module_paths:
416+
module_paths.insert(0, str(mock_path))
417+
418+
406419
def get_app(*, offline: bool | None = None, cached: bool = False) -> App:
407420
"""Return the application instance, caching the return value."""
408421
# Avoids ever running the app initialization twice if cached argument
@@ -445,6 +458,7 @@ def get_app(*, offline: bool | None = None, cached: bool = False) -> App:
445458

446459
# https://github.com/ansible/ansible-lint/issues/4973
447460
_add_collections_path_if_needed(app.options, app.runtime.config.collections_paths)
461+
_add_module_path_if_needed(app.options, app.runtime.config.default_module_path)
448462

449463
app.runtime.prepare_environment(
450464
install_local=(not offline),

test/test_app.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,36 @@ def test_app_fixed_violations_coverage(tmp_path: Path) -> None:
7979
assert exit_code == RC.FIXED_VIOLATIONS
8080

8181

82+
def test_add_module_path_for_plain_mock_modules(tmp_path: Path) -> None:
83+
"""Plain module mocks are exposed through Ansible's module path."""
84+
from ansiblelint.app import _add_module_path_if_needed
85+
from ansiblelint.config import Options
86+
87+
options = Options()
88+
options.cache_dir = tmp_path / ".ansible"
89+
options.mock_modules = ["zuul_return", "ns.coll.mod"]
90+
module_paths = ["/usr/share/ansible/plugins/modules"]
91+
92+
_add_module_path_if_needed(options, module_paths)
93+
94+
assert module_paths[0] == str(options.cache_dir / "modules")
95+
96+
97+
def test_add_module_path_skips_collection_only_mocks(tmp_path: Path) -> None:
98+
"""Collection module mocks are exposed through collection paths instead."""
99+
from ansiblelint.app import _add_module_path_if_needed
100+
from ansiblelint.config import Options
101+
102+
options = Options()
103+
options.cache_dir = tmp_path / ".ansible"
104+
options.mock_modules = ["ns.coll.mod"]
105+
module_paths = ["/usr/share/ansible/plugins/modules"]
106+
107+
_add_module_path_if_needed(options, module_paths)
108+
109+
assert module_paths == ["/usr/share/ansible/plugins/modules"]
110+
111+
82112
def test_ignore_file_with_skip_and_strict(tmp_path: Path) -> None:
83113
"""Test that .ansible-lint-ignore with skip qualifier returns exit code 0 with --strict.
84114

test/test_main.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tempfile
88
import time
99
from collections.abc import Mapping
10+
from copy import deepcopy
1011
from http.client import RemoteDisconnected
1112
from os.path import abspath
1213
from pathlib import Path
@@ -121,6 +122,47 @@ def test_get_version_warning_offline(mocker: MockerFixture) -> None:
121122
assert get_version_warning() == "" # noqa: PLC1901
122123

123124

125+
@pytest.mark.parametrize(
126+
("offline", "cache_created"),
127+
(
128+
pytest.param(True, False, id="offline"),
129+
pytest.param(False, True, id="online"),
130+
),
131+
)
132+
def test_initialize_options_cache_dir_creation(
133+
tmp_path: Path,
134+
offline: bool,
135+
cache_created: bool,
136+
) -> None:
137+
"""Check that offline mode does not create an isolated cache directory."""
138+
from ansiblelint.__main__ import initialize_options
139+
140+
old_options = deepcopy(options)
141+
project_dir = tmp_path / "project"
142+
project_dir.mkdir()
143+
cache_dir = project_dir / ".ansible"
144+
145+
arguments = ["--config-file", "/dev/null", "--project-dir", str(project_dir)]
146+
if offline:
147+
arguments.append("--offline")
148+
149+
cache_dir_lock = initialize_options(arguments)
150+
try:
151+
expected_cache_dir = (
152+
Path(os.environ.get("ANSIBLE_HOME", "~/.ansible")).expanduser()
153+
if offline
154+
else cache_dir
155+
)
156+
assert options.cache_dir == expected_cache_dir
157+
assert cache_dir.exists() is cache_created
158+
finally:
159+
if cache_dir_lock:
160+
cache_dir_lock.release()
161+
Path(cache_dir_lock.lock_file).unlink(missing_ok=True)
162+
options.__dict__.clear()
163+
options.__dict__.update(old_options.__dict__)
164+
165+
124166
@pytest.mark.parametrize(
125167
("lintable"),
126168
(

0 commit comments

Comments
 (0)