ansible-core 2.13.11__py3-none-any.whl → 2.13.12rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (41) hide show
  1. ansible/cli/config.py +1 -0
  2. ansible/cli/galaxy.py +6 -2
  3. ansible/cli/inventory.py +1 -1
  4. ansible/galaxy/role.py +33 -1
  5. ansible/module_utils/ansible_release.py +1 -1
  6. ansible/release.py +1 -1
  7. ansible_core-2.13.12rc1.dist-info/METADATA +128 -0
  8. {ansible_core-2.13.11.dist-info → ansible_core-2.13.12rc1.dist-info}/RECORD +39 -38
  9. {ansible_core-2.13.11.dist-info → ansible_core-2.13.12rc1.dist-info}/WHEEL +1 -1
  10. ansible_test/_data/completion/remote.txt +2 -2
  11. ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -0
  12. ansible_test/_data/requirements/sanity.changelog.txt +2 -0
  13. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -0
  14. ansible_test/_data/requirements/sanity.import.txt +2 -0
  15. ansible_test/_data/requirements/sanity.integration-aliases.txt +2 -0
  16. ansible_test/_data/requirements/sanity.pylint.txt +2 -0
  17. ansible_test/_data/requirements/sanity.runtime-metadata.txt +2 -0
  18. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -0
  19. ansible_test/_data/requirements/sanity.yamllint.txt +2 -0
  20. ansible_test/_internal/ansible_util.py +57 -4
  21. ansible_test/_internal/classification/__init__.py +6 -13
  22. ansible_test/_internal/classification/python.py +0 -1
  23. ansible_test/_internal/commands/sanity/__init__.py +35 -1
  24. ansible_test/_internal/commands/sanity/bin_symlinks.py +102 -0
  25. ansible_test/_internal/commands/sanity/integration_aliases.py +401 -0
  26. ansible_test/_internal/commands/sanity/validate_modules.py +5 -1
  27. ansible_test/_internal/constants.py +1 -0
  28. ansible_test/_internal/delegation.py +5 -2
  29. ansible_test/_internal/provider/layout/ansible.py +1 -1
  30. ansible_test/_internal/provider/source/unversioned.py +0 -3
  31. ansible_test/_internal/python_requirements.py +7 -0
  32. ansible_test/_internal/util.py +0 -2
  33. ansible_test/_util/target/setup/ConfigureRemotingForAnsible.ps1 +1 -1
  34. ansible_test/_util/target/setup/bootstrap.sh +2 -0
  35. ansible_test/_util/target/setup/requirements.py +63 -0
  36. ansible_core-2.13.11.dist-info/METADATA +0 -154
  37. ansible_test/_internal/commands/sanity/sanity_docs.py +0 -60
  38. {ansible_core-2.13.11.data → ansible_core-2.13.12rc1.data}/scripts/ansible-test +0 -0
  39. {ansible_core-2.13.11.dist-info → ansible_core-2.13.12rc1.dist-info}/COPYING +0 -0
  40. {ansible_core-2.13.11.dist-info → ansible_core-2.13.12rc1.dist-info}/entry_points.txt +0 -0
  41. {ansible_core-2.13.11.dist-info → ansible_core-2.13.12rc1.dist-info}/top_level.txt +0 -0
@@ -3,9 +3,11 @@ from __future__ import annotations
3
3
 
4
4
  import json
5
5
  import os
6
+ import shutil
6
7
  import typing as t
7
8
 
8
9
  from .constants import (
10
+ ANSIBLE_BIN_SYMLINK_MAP,
9
11
  SOFT_RLIMIT_NOFILE,
10
12
  )
11
13
 
@@ -17,12 +19,15 @@ from .util import (
17
19
  common_environment,
18
20
  ApplicationError,
19
21
  ANSIBLE_LIB_ROOT,
22
+ ANSIBLE_TEST_ROOT,
20
23
  ANSIBLE_TEST_DATA_ROOT,
21
- ANSIBLE_BIN_PATH,
24
+ ANSIBLE_ROOT,
22
25
  ANSIBLE_SOURCE_ROOT,
23
26
  ANSIBLE_TEST_TOOLS_ROOT,
27
+ MODE_FILE_EXECUTE,
24
28
  get_ansible_version,
25
29
  raw_command,
30
+ verified_chmod,
26
31
  )
27
32
 
28
33
  from .util_common import (
@@ -78,8 +83,10 @@ def ansible_environment(args, color=True, ansible_config=None): # type: (Common
78
83
  env = common_environment()
79
84
  path = env['PATH']
80
85
 
81
- if not path.startswith(ANSIBLE_BIN_PATH + os.path.pathsep):
82
- path = ANSIBLE_BIN_PATH + os.path.pathsep + path
86
+ ansible_bin_path = get_ansible_bin_path(args)
87
+
88
+ if not path.startswith(ansible_bin_path + os.path.pathsep):
89
+ path = ansible_bin_path + os.path.pathsep + path
83
90
 
84
91
  if not ansible_config:
85
92
  # use the default empty configuration unless one has been provided
@@ -196,6 +203,52 @@ def configure_plugin_paths(args): # type: (CommonConfig) -> t.Dict[str, str]
196
203
  return env
197
204
 
198
205
 
206
+ @mutex
207
+ def get_ansible_bin_path(args: CommonConfig) -> str:
208
+ """
209
+ Return a directory usable for PATH, containing only the ansible entry points.
210
+ If a temporary directory is required, it will be cached for the lifetime of the process and cleaned up at exit.
211
+ """
212
+ try:
213
+ return get_ansible_bin_path.bin_path # type: ignore[attr-defined]
214
+ except AttributeError:
215
+ pass
216
+
217
+ if ANSIBLE_SOURCE_ROOT:
218
+ # when running from source there is no need for a temporary directory since we already have known entry point scripts
219
+ bin_path = os.path.join(ANSIBLE_ROOT, 'bin')
220
+ else:
221
+ # when not running from source the installed entry points cannot be relied upon
222
+ # doing so would require using the interpreter specified by those entry points, which conflicts with using our interpreter and injector
223
+ # instead a temporary directory is created which contains only ansible entry points
224
+ # symbolic links cannot be used since the files are likely not executable
225
+ bin_path = create_temp_dir(prefix='ansible-test-', suffix='-bin')
226
+ bin_links = {os.path.join(bin_path, name): get_cli_path(path) for name, path in ANSIBLE_BIN_SYMLINK_MAP.items()}
227
+
228
+ if not args.explain:
229
+ for dst, src in bin_links.items():
230
+ shutil.copy(src, dst)
231
+ verified_chmod(dst, MODE_FILE_EXECUTE)
232
+
233
+ get_ansible_bin_path.bin_path = bin_path # type: ignore[attr-defined]
234
+
235
+ return bin_path
236
+
237
+
238
+ def get_cli_path(path: str) -> str:
239
+ """Return the absolute path to the CLI script from the given path which is relative to the `bin` directory of the original source tree layout."""
240
+ path_rewrite = {
241
+ '../lib/ansible/': ANSIBLE_LIB_ROOT,
242
+ '../test/lib/ansible_test/': ANSIBLE_TEST_ROOT,
243
+ }
244
+
245
+ for prefix, destination in path_rewrite.items():
246
+ if path.startswith(prefix):
247
+ return os.path.join(destination, path[len(prefix):])
248
+
249
+ raise RuntimeError(path)
250
+
251
+
199
252
  @mutex
200
253
  def get_ansible_python_path(args): # type: (CommonConfig) -> str
201
254
  """
@@ -231,7 +284,7 @@ def generate_egg_info(path): # type: (str) -> None
231
284
  # minimal PKG-INFO stub following the format defined in PEP 241
232
285
  # required for older setuptools versions to avoid a traceback when importing pkg_resources from packages like cryptography
233
286
  # newer setuptools versions are happy with an empty directory
234
- # including a stub here means we don't need to locate the existing file or have setup.py generate it when running from source
287
+ # including a stub here means we don't need to locate the existing file or run any tools to generate it when running from source
235
288
  pkg_info = '''
236
289
  Metadata-Version: 1.0
237
290
  Name: ansible
@@ -720,17 +720,6 @@ class PathMapper:
720
720
  if path.startswith('changelogs/'):
721
721
  return minimal
722
722
 
723
- if path.startswith('docs/'):
724
- return minimal
725
-
726
- if path.startswith('examples/'):
727
- if path == 'examples/scripts/ConfigureRemotingForAnsible.ps1':
728
- return {
729
- 'windows-integration': 'connection_winrm',
730
- }
731
-
732
- return minimal
733
-
734
723
  if path.startswith('hacking/'):
735
724
  return minimal
736
725
 
@@ -752,8 +741,12 @@ class PathMapper:
752
741
  return minimal
753
742
 
754
743
  if path.startswith('packaging/'):
755
- if path.startswith('packaging/pep517_backend/'):
756
- return packaging
744
+ packaging_target = f'packaging_{os.path.splitext(path.split(os.path.sep)[1])[0]}'
745
+
746
+ if packaging_target in self.integration_targets_by_name:
747
+ return {
748
+ 'integration': packaging_target,
749
+ }
757
750
 
758
751
  return minimal
759
752
 
@@ -257,7 +257,6 @@ class ModuleUtilFinder(ast.NodeVisitor):
257
257
  # The mapping is a tuple consisting of a path pattern to match and a replacement path.
258
258
  # During analyis, any relative imports not covered here will result in warnings, which can be fixed by adding the appropriate entry.
259
259
  path_map = (
260
- ('^hacking/build_library/build_ansible/', 'build_ansible/'),
261
260
  ('^lib/ansible/', 'ansible/'),
262
261
  ('^test/lib/ansible_test/_util/controller/sanity/validate-modules/', 'validate_modules/'),
263
262
  ('^test/units/', 'test/units/'),
@@ -70,6 +70,7 @@ from ...executor import (
70
70
  )
71
71
 
72
72
  from ...python_requirements import (
73
+ PipCommand,
73
74
  PipInstall,
74
75
  collect_requirements,
75
76
  run_pip,
@@ -1125,7 +1126,7 @@ def create_sanity_virtualenv(
1125
1126
  # The path to the virtual environment must be kept short to avoid the 127 character shebang length limit on Linux.
1126
1127
  # If the limit is exceeded, generated entry point scripts from pip installed packages will fail with syntax errors.
1127
1128
  virtualenv_install = json.dumps([command.serialize() for command in commands], indent=4)
1128
- virtualenv_hash = hashlib.sha256(to_bytes(virtualenv_install)).hexdigest()[:8]
1129
+ virtualenv_hash = hash_pip_commands(commands)
1129
1130
  virtualenv_cache = os.path.join(os.path.expanduser('~/.ansible/test/venv'))
1130
1131
  virtualenv_path = os.path.join(virtualenv_cache, label, f'{python.version}', virtualenv_hash)
1131
1132
  virtualenv_marker = os.path.join(virtualenv_path, 'marker.txt')
@@ -1165,6 +1166,39 @@ def create_sanity_virtualenv(
1165
1166
  return virtualenv_python
1166
1167
 
1167
1168
 
1169
+ def hash_pip_commands(commands: list[PipCommand]) -> str:
1170
+ """Return a short hash unique to the given list of pip commands, suitable for identifying the resulting sanity test environment."""
1171
+ serialized_commands = json.dumps([make_pip_command_hashable(command) for command in commands], indent=4)
1172
+
1173
+ return hashlib.sha256(to_bytes(serialized_commands)).hexdigest()[:8]
1174
+
1175
+
1176
+ def make_pip_command_hashable(command: PipCommand) -> tuple[str, dict[str, t.Any]]:
1177
+ """Return a serialized version of the given pip command that is suitable for hashing."""
1178
+ if isinstance(command, PipInstall):
1179
+ # The pre-build instructions for pip installs must be omitted, so they do not affect the hash.
1180
+ # This is allows the pre-build commands to be added without breaking sanity venv caching.
1181
+ # It is safe to omit these from the hash since they only affect packages used during builds, not what is installed in the venv.
1182
+ command = PipInstall(
1183
+ requirements=[omit_pre_build_from_requirement(*req) for req in command.requirements],
1184
+ constraints=list(command.constraints),
1185
+ packages=list(command.packages),
1186
+ )
1187
+
1188
+ return command.serialize()
1189
+
1190
+
1191
+ def omit_pre_build_from_requirement(path: str, requirements: str) -> tuple[str, str]:
1192
+ """Return the given requirements with pre-build instructions omitted."""
1193
+ lines = requirements.splitlines(keepends=True)
1194
+
1195
+ # CAUTION: This code must be kept in sync with the code which processes pre-build instructions in:
1196
+ # test/lib/ansible_test/_util/target/setup/requirements.py
1197
+ lines = [line for line in lines if not line.startswith('# pre-build ')]
1198
+
1199
+ return path, ''.join(lines)
1200
+
1201
+
1168
1202
  def check_sanity_virtualenv_yaml(python): # type: (VirtualPythonConfig) -> t.Optional[bool]
1169
1203
  """Return True if PyYAML has libyaml support for the given sanity virtual environment, False if it does not and None if it was not found."""
1170
1204
  virtualenv_path = os.path.dirname(os.path.dirname(python.path))
@@ -0,0 +1,102 @@
1
+ """Sanity test for symlinks in the bin directory."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import typing as t
6
+
7
+ from . import (
8
+ SanityVersionNeutral,
9
+ SanityMessage,
10
+ SanityFailure,
11
+ SanitySuccess,
12
+ SanityTargets,
13
+ )
14
+
15
+ from ...constants import (
16
+ __file__ as symlink_map_full_path,
17
+ )
18
+
19
+ from ...test import (
20
+ TestResult,
21
+ )
22
+
23
+ from ...config import (
24
+ SanityConfig,
25
+ )
26
+
27
+ from ...data import (
28
+ data_context,
29
+ )
30
+
31
+ from ...payload import (
32
+ ANSIBLE_BIN_SYMLINK_MAP,
33
+ )
34
+
35
+ from ...util import (
36
+ ANSIBLE_SOURCE_ROOT,
37
+ )
38
+
39
+
40
+ class BinSymlinksTest(SanityVersionNeutral):
41
+ """Sanity test for symlinks in the bin directory."""
42
+ ansible_only = True
43
+
44
+ @property
45
+ def can_ignore(self): # type: () -> bool
46
+ """True if the test supports ignore entries."""
47
+ return False
48
+
49
+ @property
50
+ def no_targets(self): # type: () -> bool
51
+ """True if the test does not use test targets. Mutually exclusive with all_targets."""
52
+ return True
53
+
54
+ def test(self, args, targets): # type: (SanityConfig, SanityTargets) -> TestResult
55
+ bin_root = os.path.join(ANSIBLE_SOURCE_ROOT, 'bin')
56
+ bin_names = os.listdir(bin_root)
57
+ bin_paths = sorted(os.path.join(bin_root, path) for path in bin_names)
58
+
59
+ errors = [] # type: t.List[t.Tuple[str, str]]
60
+
61
+ symlink_map_path = os.path.relpath(symlink_map_full_path, data_context().content.root)
62
+
63
+ for bin_path in bin_paths:
64
+ if not os.path.islink(bin_path):
65
+ errors.append((bin_path, 'not a symbolic link'))
66
+ continue
67
+
68
+ dest = os.readlink(bin_path)
69
+
70
+ if not os.path.exists(bin_path):
71
+ errors.append((bin_path, 'points to non-existent path "%s"' % dest))
72
+ continue
73
+
74
+ if not os.path.isfile(bin_path):
75
+ errors.append((bin_path, 'points to non-file "%s"' % dest))
76
+ continue
77
+
78
+ map_dest = ANSIBLE_BIN_SYMLINK_MAP.get(os.path.basename(bin_path))
79
+
80
+ if not map_dest:
81
+ errors.append((bin_path, 'missing from ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % symlink_map_path))
82
+ continue
83
+
84
+ if dest != map_dest:
85
+ errors.append((bin_path, 'points to "%s" instead of "%s" from ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % (dest, map_dest, symlink_map_path)))
86
+ continue
87
+
88
+ if not os.access(bin_path, os.X_OK):
89
+ errors.append((bin_path, 'points to non-executable file "%s"' % dest))
90
+ continue
91
+
92
+ for bin_name, dest in ANSIBLE_BIN_SYMLINK_MAP.items():
93
+ if bin_name not in bin_names:
94
+ bin_path = os.path.join(bin_root, bin_name)
95
+ errors.append((bin_path, 'missing symlink to "%s" defined in ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % (dest, symlink_map_path)))
96
+
97
+ messages = [SanityMessage(message=message, path=os.path.relpath(path, data_context().content.root), confidence=100) for path, message in errors]
98
+
99
+ if errors:
100
+ return SanityFailure(self.name, messages=messages)
101
+
102
+ return SanitySuccess(self.name)