antsibull-nox 0.0.1__py3-none-any.whl → 0.2.0__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.
antsibull_nox/__init__.py CHANGED
@@ -10,8 +10,71 @@ Antsibull Nox Helper.
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- from .sessions import add_lint_sessions # noqa: F401
13
+ from .collection import CollectionSource, setup_collection_sources
14
+ from .config import load_config_from_toml
15
+ from .interpret_config import interpret_config
16
+ from .sessions import (
17
+ ActionGroup,
18
+ add_all_ansible_test_sanity_test_sessions,
19
+ add_all_ansible_test_unit_test_sessions,
20
+ add_ansible_lint,
21
+ add_ansible_test_integration_sessions_default_container,
22
+ add_ansible_test_sanity_test_session,
23
+ add_ansible_test_session,
24
+ add_ansible_test_unit_test_session,
25
+ add_build_import_check,
26
+ add_docs_check,
27
+ add_extra_checks,
28
+ add_license_check,
29
+ add_lint_sessions,
30
+ add_matrix_generator,
31
+ )
14
32
 
15
- __version__ = "0.0.1"
33
+ __version__ = "0.2.0"
16
34
 
17
- __all__ = ("__version__",)
35
+
36
+ def setup(
37
+ *,
38
+ collection_sources: dict[str, str | CollectionSource] | None = None,
39
+ ) -> None:
40
+ """
41
+ Set-up antsibull-nox.
42
+ """
43
+ if collection_sources:
44
+ setup_collection_sources(
45
+ {
46
+ name: CollectionSource.parse(name, source)
47
+ for name, source in collection_sources.items()
48
+ }
49
+ )
50
+
51
+
52
+ def load_antsibull_nox_toml() -> None:
53
+ """
54
+ Load and interpret antsibull-nox.toml config file.
55
+ """
56
+ config = load_config_from_toml("antsibull-nox.toml")
57
+ interpret_config(config)
58
+
59
+
60
+ # pylint:disable=duplicate-code
61
+ __all__ = (
62
+ "__version__",
63
+ "ActionGroup",
64
+ "add_build_import_check",
65
+ "add_docs_check",
66
+ "add_extra_checks",
67
+ "add_license_check",
68
+ "add_lint_sessions",
69
+ "add_ansible_test_session",
70
+ "add_ansible_test_sanity_test_session",
71
+ "add_all_ansible_test_sanity_test_sessions",
72
+ "add_ansible_test_unit_test_session",
73
+ "add_all_ansible_test_unit_test_sessions",
74
+ "add_ansible_test_integration_sessions_default_container",
75
+ "add_ansible_lint",
76
+ "add_matrix_generator",
77
+ "CollectionSource",
78
+ "setup",
79
+ "load_antsibull_nox_toml",
80
+ )
@@ -0,0 +1,260 @@
1
+ # Author: Felix Fontein <felix@fontein.de>
2
+ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
3
+ # https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ # SPDX-FileCopyrightText: 2025, Ansible Project
6
+
7
+ """
8
+ Ansible-core version utilities.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import functools
14
+ import typing as t
15
+ from dataclasses import dataclass
16
+
17
+ from antsibull_fileutils.yaml import load_yaml_file
18
+ from packaging.specifiers import SpecifierSet as PckgSpecifierSet
19
+ from packaging.version import Version as PckgVersion
20
+
21
+ from .utils import Version, version_range
22
+
23
+ AnsibleCoreVersion = t.Union[Version, t.Literal["milestone", "devel"]]
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class AnsibleCoreInfo:
28
+ """
29
+ Information on an ansible-core version.
30
+ """
31
+
32
+ ansible_core_version: Version
33
+ controller_python_versions: tuple[Version, ...]
34
+ remote_python_versions: tuple[Version, ...]
35
+
36
+
37
+ _SUPPORTED_CORE_VERSIONS: dict[Version, AnsibleCoreInfo] = {
38
+ Version.parse(ansible_version): AnsibleCoreInfo(
39
+ ansible_core_version=Version.parse(ansible_version),
40
+ controller_python_versions=tuple(
41
+ Version.parse(v) for v in controller_python_versions
42
+ ),
43
+ remote_python_versions=tuple(Version.parse(v) for v in remote_python_versions),
44
+ )
45
+ for ansible_version, (controller_python_versions, remote_python_versions) in {
46
+ "2.9": [
47
+ ["2.7", "3.5", "3.6", "3.7", "3.8"],
48
+ ["2.6", "2.7", "3.5", "3.6", "3.7", "3.8"],
49
+ ],
50
+ "2.10": [
51
+ ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9"],
52
+ ["2.6", "2.7", "3.5", "3.6", "3.7", "3.8", "3.9"],
53
+ ],
54
+ "2.11": [
55
+ ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9"],
56
+ ["2.6", "2.7", "3.5", "3.6", "3.7", "3.8", "3.9"],
57
+ ],
58
+ "2.12": [
59
+ ["3.8", "3.9", "3.10"],
60
+ ["2.6", "2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"],
61
+ ],
62
+ "2.13": [
63
+ ["3.8", "3.9", "3.10"],
64
+ ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"],
65
+ ],
66
+ "2.14": [
67
+ ["3.9", "3.10", "3.11"],
68
+ ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"],
69
+ ],
70
+ "2.15": [
71
+ ["3.9", "3.10", "3.11"],
72
+ ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"],
73
+ ],
74
+ "2.16": [
75
+ ["3.10", "3.11", "3.12"],
76
+ ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"],
77
+ ],
78
+ "2.17": [
79
+ ["3.10", "3.11", "3.12"],
80
+ ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"],
81
+ ],
82
+ "2.18": [
83
+ ["3.11", "3.12", "3.13"],
84
+ ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"],
85
+ ],
86
+ "2.19": [
87
+ ["3.11", "3.12", "3.13"],
88
+ ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"],
89
+ ],
90
+ # The following might need updates. Look for the "``ansible-core`` support matrix" table in:
91
+ # https://github.com/ansible/ansible-documentation/blob/devel/docs/docsite/rst/reference_appendices/release_and_maintenance.rst?plain=1
92
+ # It contains commented-out entries for future ansible-core versions.
93
+ "2.20": [
94
+ ["3.12", "3.13", "3.14"],
95
+ ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"],
96
+ ],
97
+ "2.21": [
98
+ ["3.12", "3.13", "3.14"],
99
+ ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"],
100
+ ],
101
+ "2.22": [
102
+ ["3.13", "3.14", "3.15"],
103
+ ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"],
104
+ ],
105
+ "2.23": [
106
+ ["3.13", "3.14", "3.15"],
107
+ ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"],
108
+ ],
109
+ "2.24": [
110
+ ["3.14", "3.15", "3.16"],
111
+ ["3.11", "3.12", "3.13", "3.14", "3.15", "3.16"],
112
+ ],
113
+ "2.25": [
114
+ ["3.14", "3.15", "3.16"],
115
+ ["3.11", "3.12", "3.13", "3.14", "3.15", "3.16"],
116
+ ],
117
+ }.items()
118
+ }
119
+
120
+ _MIN_SUPPORTED_VERSION = Version.parse("2.9")
121
+ _CURRENT_DEVEL_VERSION = Version.parse("2.19")
122
+ _CURRENT_MILESTONE_VERSION = Version.parse("2.19")
123
+
124
+
125
+ def get_ansible_core_info(
126
+ core_version: AnsibleCoreVersion,
127
+ ) -> AnsibleCoreInfo:
128
+ """
129
+ Retrieve information on an ansible-core version.
130
+ """
131
+ version: Version
132
+ if core_version == "devel":
133
+ version = _CURRENT_DEVEL_VERSION
134
+ elif core_version == "milestone":
135
+ version = _CURRENT_MILESTONE_VERSION
136
+ else:
137
+ version = core_version
138
+ if version in _SUPPORTED_CORE_VERSIONS:
139
+ return _SUPPORTED_CORE_VERSIONS[version]
140
+ raise ValueError(f"Unknown ansible-core version {version}")
141
+
142
+
143
+ _ANSIBLE_REPO = "ansible/ansible"
144
+ _ANSIBLE_EOL_REPO = "ansible-community/eol-ansible"
145
+ _ANSIBLE_EOL_MAX_VERSION = Version(2, 14)
146
+
147
+
148
+ def get_ansible_core_package_name(
149
+ core_version: AnsibleCoreVersion,
150
+ *,
151
+ source: t.Literal["git", "pypi"] = "git",
152
+ ansible_repo: str | None = None,
153
+ branch_name: str | None = None,
154
+ ) -> str:
155
+ """
156
+ Return the package name for a specific ansible-core version.
157
+
158
+ The result can be passed to pip to install that version of ansible-core.
159
+ """
160
+ if not isinstance(core_version, Version):
161
+ # milestone and devel are not available from PyPI
162
+ source = "git"
163
+
164
+ if source == "git":
165
+ if branch_name is None:
166
+ if isinstance(core_version, str):
167
+ branch_name = core_version
168
+ else:
169
+ branch_name = f"stable-{core_version}"
170
+ if ansible_repo is None:
171
+ if (
172
+ isinstance(core_version, Version)
173
+ and core_version <= _ANSIBLE_EOL_MAX_VERSION
174
+ ):
175
+ ansible_repo = _ANSIBLE_EOL_REPO
176
+ else:
177
+ ansible_repo = _ANSIBLE_REPO
178
+ return f"https://github.com/{ansible_repo}/archive/{branch_name}.tar.gz"
179
+
180
+ assert isinstance(core_version, Version)
181
+ next_core_version = core_version.next_minor_version()
182
+ base = "ansible-core"
183
+ if core_version == Version(2, 9):
184
+ base = "ansible"
185
+ elif core_version == Version(2, 10):
186
+ base = "ansible-base"
187
+ return f"{base}>={core_version},<{next_core_version}"
188
+
189
+
190
+ @functools.cache
191
+ def _read_requires_ansible() -> PckgSpecifierSet:
192
+ path = "meta/runtime.yml"
193
+ try:
194
+ runtime_data = load_yaml_file(path)
195
+ except FileNotFoundError as exc:
196
+ raise ValueError(f"Cannot open {path}") from exc
197
+ except Exception as exc:
198
+ raise ValueError(f"Cannot parse {path}: {exc}") from exc
199
+
200
+ requires_ansible = runtime_data.get("requires_ansible")
201
+ if not isinstance(requires_ansible, str):
202
+ raise ValueError(f"{path} does not contain an 'requires_ansible' string")
203
+ try:
204
+ return PckgSpecifierSet(requires_ansible)
205
+ except Exception as exc:
206
+ raise ValueError(
207
+ f"{path} contains an invalid 'requires_ansible' string: {exc}"
208
+ ) from exc
209
+
210
+
211
+ @functools.cache
212
+ def get_supported_core_versions(
213
+ *,
214
+ include_devel: bool = False,
215
+ include_milestone: bool = False,
216
+ min_version: Version | None = None,
217
+ max_version: Version | None = None,
218
+ except_versions: tuple[AnsibleCoreVersion, ...] | None = None,
219
+ ) -> list[AnsibleCoreVersion]:
220
+ """
221
+ Extracts a list of supported ansible-core versions from meta/runtime.yml.
222
+
223
+ If ``min_version`` is specified, no version below that version will be returned.
224
+ If ``max_version`` is specified, no version above that version will be returned.
225
+ If ``except_versions`` is specified, no version in that tuple will be returned.
226
+ """
227
+ if except_versions is None:
228
+ except_versions = ()
229
+
230
+ ra_specifier = _read_requires_ansible()
231
+
232
+ result: list[AnsibleCoreVersion] = []
233
+ for version in version_range(
234
+ _MIN_SUPPORTED_VERSION, _CURRENT_DEVEL_VERSION, inclusive=False
235
+ ):
236
+ if version in except_versions:
237
+ continue
238
+ if min_version is not None and version < min_version:
239
+ continue
240
+ if max_version is not None and version > max_version:
241
+ continue
242
+ # We're using x.y.999 to check whether *some* ansible-core x.y version is supported.
243
+ # This is not entirely correct, since collections might specfiy that only certain older x.y
244
+ # versions are OK, but I'd consider such behavior a bug of the collection and something
245
+ # you really shouldn't do as a collection maintainer.
246
+ v = PckgVersion(f"{version.major}.{version.minor}.999")
247
+ if v in ra_specifier:
248
+ result.append(version)
249
+ if include_milestone and "milestone" not in except_versions:
250
+ result.append("milestone")
251
+ if include_devel and "devel" not in except_versions:
252
+ result.append("devel")
253
+ return result
254
+
255
+
256
+ __all__ = [
257
+ "AnsibleCoreInfo",
258
+ "get_ansible_core_info",
259
+ "get_ansible_core_package_name",
260
+ ]
@@ -0,0 +1,56 @@
1
+ # Author: Felix Fontein <felix@fontein.de>
2
+ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
3
+ # https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ # SPDX-FileCopyrightText: 2025, Ansible Project
6
+
7
+ """
8
+ Handle Ansible collections.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from pathlib import Path
14
+
15
+ from antsibull_fileutils.yaml import load_yaml_file, store_yaml_file
16
+
17
+ from .data import CollectionData, CollectionSource, SetupResult
18
+ from .install import (
19
+ Runner,
20
+ setup_collection_sources,
21
+ setup_collections,
22
+ setup_current_tree,
23
+ )
24
+ from .search import CollectionList, load_collection_data_from_disk
25
+
26
+
27
+ def force_collection_version(path: Path, *, version: str) -> bool:
28
+ """
29
+ Make sure galaxy.yml contains this version.
30
+
31
+ Returns ``True`` if the version was changed, and ``False`` if the version
32
+ was already set to this value.
33
+ """
34
+ galaxy_yml = path / "galaxy.yml"
35
+ try:
36
+ data = load_yaml_file(galaxy_yml)
37
+ except Exception as exc:
38
+ raise ValueError(f"Cannot parse {galaxy_yml}: {exc}") from exc
39
+ if data.get("version") == version:
40
+ return False
41
+ data["version"] = version
42
+ store_yaml_file(galaxy_yml, data)
43
+ return True
44
+
45
+
46
+ __all__ = [
47
+ "CollectionData",
48
+ "CollectionList",
49
+ "CollectionSource",
50
+ "SetupResult",
51
+ "Runner",
52
+ "load_collection_data_from_disk",
53
+ "setup_collections",
54
+ "setup_current_tree",
55
+ "setup_collection_sources",
56
+ ]
@@ -0,0 +1,106 @@
1
+ # Author: Felix Fontein <felix@fontein.de>
2
+ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
3
+ # https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ # SPDX-FileCopyrightText: 2025, Ansible Project
6
+
7
+ """
8
+ Data types for collections.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+
16
+
17
+ @dataclass
18
+ class CollectionData: # pylint: disable=too-many-instance-attributes
19
+ """
20
+ An Ansible collection.
21
+ """
22
+
23
+ collections_root_path: Path | None
24
+ path: Path
25
+ namespace: str
26
+ name: str
27
+ full_name: str
28
+ version: str | None
29
+ dependencies: dict[str, str]
30
+ current: bool
31
+
32
+ @classmethod
33
+ def create(
34
+ cls,
35
+ *,
36
+ collections_root_path: Path | None = None,
37
+ path: Path,
38
+ full_name: str,
39
+ version: str | None = None,
40
+ dependencies: dict[str, str] | None = None,
41
+ current: bool = False,
42
+ ):
43
+ """
44
+ Create a CollectionData object.
45
+ """
46
+ namespace, name = full_name.split(".", 1)
47
+ return CollectionData(
48
+ collections_root_path=collections_root_path,
49
+ path=path,
50
+ namespace=namespace,
51
+ name=name,
52
+ full_name=full_name,
53
+ version=version,
54
+ dependencies=dependencies or {},
55
+ current=current,
56
+ )
57
+
58
+
59
+ @dataclass
60
+ class SetupResult:
61
+ """
62
+ Information on how the collections are set up.
63
+ """
64
+
65
+ # The path of the ansible_collections directory.
66
+ root: Path
67
+
68
+ # Data on the current collection (as in the repository).
69
+ current_collection: CollectionData
70
+
71
+ # If it was installed, the path of the current collection inside the collection tree below root.
72
+ current_path: Path | None
73
+
74
+
75
+ @dataclass
76
+ class CollectionSource:
77
+ """
78
+ Collection installation source.
79
+ """
80
+
81
+ # The collection's name
82
+ name: str
83
+
84
+ # The collection's installation source (can be passed to 'ansible-galaxy collection install')
85
+ source: str
86
+
87
+ @staticmethod
88
+ def parse(collection_name: str, source: str | CollectionSource) -> CollectionSource:
89
+ """
90
+ Parse a CollectionSource object.
91
+ """
92
+ if isinstance(source, str):
93
+ return CollectionSource(name=collection_name, source=source)
94
+
95
+ if source.name != collection_name:
96
+ raise ValueError(
97
+ f"Collection name should be {collection_name!r}, but is {source.name!r}"
98
+ )
99
+ return source
100
+
101
+
102
+ __all__ = [
103
+ "CollectionData",
104
+ "SetupResult",
105
+ "CollectionSource",
106
+ ]
@@ -0,0 +1,23 @@
1
+ # Author: Felix Fontein <felix@fontein.de>
2
+ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
3
+ # https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ # SPDX-FileCopyrightText: 2025, Ansible Project
6
+
7
+ """
8
+ Extract tarballs.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import tarfile
14
+ from pathlib import Path
15
+
16
+
17
+ def extract_tarball(*, tarball: Path, destination: Path) -> None:
18
+ """
19
+ Extract the contents of the tarball ``tarball`` to the destination directory ``destination``.
20
+ """
21
+ destination.mkdir(exist_ok=True, parents=True)
22
+ with tarfile.open(tarball) as tar:
23
+ tar.extractall(path=destination, filter="data")