antsibull-nox 0.2.0__py3-none-any.whl → 0.4.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 +7 -51
- antsibull_nox/_pydantic.py +98 -0
- antsibull_nox/ansible.py +15 -0
- antsibull_nox/cli.py +132 -0
- antsibull_nox/collection/__init__.py +2 -2
- antsibull_nox/collection/data.py +12 -0
- antsibull_nox/collection/install.py +194 -79
- antsibull_nox/collection/search.py +136 -34
- antsibull_nox/config.py +51 -2
- antsibull_nox/data/action-groups.py +2 -2
- antsibull_nox/data/antsibull-nox-lint-config.py +29 -0
- antsibull_nox/data/file-yamllint.py +138 -0
- antsibull_nox/data/license-check.py +5 -1
- antsibull_nox/data/plugin-yamllint.py +54 -24
- antsibull_nox/init.py +83 -0
- antsibull_nox/interpret_config.py +29 -8
- antsibull_nox/lint_config.py +113 -0
- antsibull_nox/sessions/__init__.py +70 -0
- antsibull_nox/sessions/ansible_lint.py +60 -0
- antsibull_nox/sessions/ansible_test.py +559 -0
- antsibull_nox/sessions/build_import_check.py +147 -0
- antsibull_nox/sessions/collections.py +145 -0
- antsibull_nox/sessions/docs_check.py +78 -0
- antsibull_nox/sessions/extra_checks.py +127 -0
- antsibull_nox/sessions/license_check.py +73 -0
- antsibull_nox/sessions/lint.py +694 -0
- antsibull_nox/sessions/utils.py +206 -0
- {antsibull_nox-0.2.0.dist-info → antsibull_nox-0.4.0.dist-info}/METADATA +2 -2
- antsibull_nox-0.4.0.dist-info/RECORD +41 -0
- antsibull_nox-0.4.0.dist-info/entry_points.txt +2 -0
- antsibull_nox/sessions.py +0 -1712
- antsibull_nox-0.2.0.dist-info/RECORD +0 -25
- {antsibull_nox-0.2.0.dist-info → antsibull_nox-0.4.0.dist-info}/WHEEL +0 -0
- {antsibull_nox-0.2.0.dist-info → antsibull_nox-0.4.0.dist-info}/licenses/LICENSES/GPL-3.0-or-later.txt +0 -0
antsibull_nox/__init__.py
CHANGED
@@ -10,71 +10,27 @@ Antsibull Nox Helper.
|
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
-
from .
|
14
|
-
|
15
|
-
|
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,
|
13
|
+
from .config import (
|
14
|
+
CONFIG_FILENAME,
|
15
|
+
load_config_from_toml,
|
31
16
|
)
|
17
|
+
from .interpret_config import interpret_config
|
18
|
+
from .sessions.ansible_test import add_ansible_test_session
|
32
19
|
|
33
|
-
__version__ = "0.
|
34
|
-
|
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
|
-
)
|
20
|
+
__version__ = "0.4.0"
|
50
21
|
|
51
22
|
|
52
23
|
def load_antsibull_nox_toml() -> None:
|
53
24
|
"""
|
54
25
|
Load and interpret antsibull-nox.toml config file.
|
55
26
|
"""
|
56
|
-
config = load_config_from_toml(
|
27
|
+
config = load_config_from_toml(CONFIG_FILENAME)
|
57
28
|
interpret_config(config)
|
58
29
|
|
59
30
|
|
60
31
|
# pylint:disable=duplicate-code
|
61
32
|
__all__ = (
|
62
33
|
"__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
34
|
"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
35
|
"load_antsibull_nox_toml",
|
80
36
|
)
|
@@ -0,0 +1,98 @@
|
|
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: Ansible Project, 2024
|
6
|
+
|
7
|
+
# ========================================================================
|
8
|
+
# **Vendored** from antsibull-core.
|
9
|
+
#
|
10
|
+
# I vendored this to avoid depending on antsibull-core only for this.
|
11
|
+
# TBH, most of this should be part of pydantic anyway. See
|
12
|
+
# https://github.com/pydantic/pydantic/discussions/2652 for a discussion
|
13
|
+
# on this.
|
14
|
+
#
|
15
|
+
# pylint: disable=missing-function-docstring
|
16
|
+
# ========================================================================
|
17
|
+
|
18
|
+
"""
|
19
|
+
Helpers for pydantic.
|
20
|
+
"""
|
21
|
+
|
22
|
+
from __future__ import annotations
|
23
|
+
|
24
|
+
import typing as t
|
25
|
+
from collections.abc import Callable, Collection
|
26
|
+
|
27
|
+
import pydantic as p
|
28
|
+
|
29
|
+
if t.TYPE_CHECKING:
|
30
|
+
from typing_extensions import TypeGuard
|
31
|
+
|
32
|
+
|
33
|
+
def _is_basemodel(a_type: t.Any) -> TypeGuard[type[p.BaseModel]]:
|
34
|
+
try:
|
35
|
+
return issubclass(a_type, p.BaseModel)
|
36
|
+
except TypeError:
|
37
|
+
# If inspect.isclass(a_type) is checked first, no TypeError happens for
|
38
|
+
# Python 3.11+.
|
39
|
+
|
40
|
+
# On Python 3.9 and 3.10, issubclass(dict[int, int], p.BaseModel) raises
|
41
|
+
# "TypeError: issubclass() arg 1 must be a class".
|
42
|
+
# (https://github.com/pydantic/pydantic/discussions/5970)
|
43
|
+
return False
|
44
|
+
|
45
|
+
|
46
|
+
def _modify_config(
|
47
|
+
cls: type[p.BaseModel],
|
48
|
+
processed_classes: set[type[p.BaseModel]],
|
49
|
+
change_config: Callable[[p.ConfigDict], bool],
|
50
|
+
) -> bool:
|
51
|
+
if cls in processed_classes:
|
52
|
+
return False
|
53
|
+
change = False
|
54
|
+
for field_info in cls.model_fields.values():
|
55
|
+
if _is_basemodel(field_info.annotation):
|
56
|
+
change |= _modify_config(
|
57
|
+
field_info.annotation,
|
58
|
+
processed_classes,
|
59
|
+
change_config,
|
60
|
+
)
|
61
|
+
for subcls in t.get_args(field_info.annotation):
|
62
|
+
if _is_basemodel(subcls):
|
63
|
+
change |= _modify_config(subcls, processed_classes, change_config)
|
64
|
+
change |= change_config(cls.model_config)
|
65
|
+
if change:
|
66
|
+
cls.model_rebuild(force=True)
|
67
|
+
processed_classes.add(cls)
|
68
|
+
return change
|
69
|
+
|
70
|
+
|
71
|
+
def set_extras(
|
72
|
+
models: type[p.BaseModel] | Collection[type[p.BaseModel]],
|
73
|
+
value: t.Literal["allow", "ignore", "forbid"],
|
74
|
+
) -> None:
|
75
|
+
def change_config(model_config: p.ConfigDict) -> bool:
|
76
|
+
if model_config.get("extra") == value:
|
77
|
+
return False
|
78
|
+
model_config["extra"] = value
|
79
|
+
return True
|
80
|
+
|
81
|
+
processed_classes: set[type[p.BaseModel]] = set()
|
82
|
+
if isinstance(models, Collection):
|
83
|
+
for cls in models:
|
84
|
+
_modify_config(cls, processed_classes, change_config)
|
85
|
+
else:
|
86
|
+
_modify_config(models, processed_classes, change_config)
|
87
|
+
|
88
|
+
|
89
|
+
def forbid_extras(models: type[p.BaseModel] | Collection[type[p.BaseModel]]) -> None:
|
90
|
+
set_extras(models, "forbid")
|
91
|
+
|
92
|
+
|
93
|
+
def get_formatted_error_messages(error: p.ValidationError) -> list[str]:
|
94
|
+
def format_error(err) -> str:
|
95
|
+
location = " -> ".join(str(loc) for loc in err["loc"])
|
96
|
+
return f'{location}: {err["msg"]}'
|
97
|
+
|
98
|
+
return [format_error(err) for err in error.errors()]
|
antsibull_nox/ansible.py
CHANGED
@@ -253,8 +253,23 @@ def get_supported_core_versions(
|
|
253
253
|
return result
|
254
254
|
|
255
255
|
|
256
|
+
def parse_ansible_core_version(
|
257
|
+
version: str | AnsibleCoreVersion,
|
258
|
+
) -> AnsibleCoreVersion:
|
259
|
+
"""
|
260
|
+
Coerce a string or a AnsibleCoreVersion to a AnsibleCoreVersion.
|
261
|
+
"""
|
262
|
+
if version in ("devel", "milestone"):
|
263
|
+
# For some reason mypy doesn't notice that
|
264
|
+
return t.cast(AnsibleCoreVersion, version)
|
265
|
+
if isinstance(version, Version):
|
266
|
+
return version
|
267
|
+
return Version.parse(version)
|
268
|
+
|
269
|
+
|
256
270
|
__all__ = [
|
257
271
|
"AnsibleCoreInfo",
|
258
272
|
"get_ansible_core_info",
|
259
273
|
"get_ansible_core_package_name",
|
274
|
+
"parse_ansible_core_version",
|
260
275
|
]
|
antsibull_nox/cli.py
ADDED
@@ -0,0 +1,132 @@
|
|
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
|
+
# PYTHON_ARGCOMPLETE_OK
|
8
|
+
|
9
|
+
"""Entrypoint to the antsibull-docs script."""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
import argparse
|
14
|
+
import os
|
15
|
+
import os.path
|
16
|
+
import sys
|
17
|
+
from collections.abc import Callable
|
18
|
+
|
19
|
+
from . import __version__
|
20
|
+
from .init import create_initial_config as _create_initial_config
|
21
|
+
from .lint_config import lint_config as _lint_config
|
22
|
+
|
23
|
+
try:
|
24
|
+
import argcomplete
|
25
|
+
|
26
|
+
HAS_ARGCOMPLETE = True
|
27
|
+
except ImportError:
|
28
|
+
HAS_ARGCOMPLETE = False
|
29
|
+
|
30
|
+
|
31
|
+
def lint_config(_: argparse.Namespace) -> int:
|
32
|
+
"""
|
33
|
+
Lint antsibull-nox config file.
|
34
|
+
"""
|
35
|
+
errors = _lint_config()
|
36
|
+
for error in errors:
|
37
|
+
print(error)
|
38
|
+
return 0 if len(errors) == 0 else 3
|
39
|
+
|
40
|
+
|
41
|
+
def create_initial_config(_: argparse.Namespace) -> int:
|
42
|
+
"""
|
43
|
+
Create noxfile.py and antsibull-nox.toml.
|
44
|
+
"""
|
45
|
+
try:
|
46
|
+
_create_initial_config()
|
47
|
+
except Exception as exc: # pylint: disable=broad-exception-caught
|
48
|
+
print(f"Error: {exc}", file=sys.stderr)
|
49
|
+
return 3
|
50
|
+
return 0
|
51
|
+
|
52
|
+
|
53
|
+
# Mapping from command line subcommand names to functions which implement those.
|
54
|
+
# The functions need to take a single argument, the processed list of args.
|
55
|
+
ARGS_MAP: dict[str, Callable[[argparse.Namespace], int]] = {
|
56
|
+
"lint-config": lint_config,
|
57
|
+
"init": create_initial_config,
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
class InvalidArgumentError(Exception):
|
62
|
+
"""
|
63
|
+
Error while parsing arguments.
|
64
|
+
"""
|
65
|
+
|
66
|
+
|
67
|
+
def parse_args(program_name: str, args: list[str]) -> argparse.Namespace:
|
68
|
+
"""
|
69
|
+
Parse the command line arguments.
|
70
|
+
"""
|
71
|
+
|
72
|
+
toplevel_parser = argparse.ArgumentParser(
|
73
|
+
prog=program_name,
|
74
|
+
description="Script to manage generated documentation for ansible",
|
75
|
+
)
|
76
|
+
toplevel_parser.add_argument(
|
77
|
+
"--version",
|
78
|
+
action="version",
|
79
|
+
version=__version__,
|
80
|
+
help="Print the antsibull-nox version",
|
81
|
+
)
|
82
|
+
subparsers = toplevel_parser.add_subparsers(
|
83
|
+
title="Subcommands", dest="command", help="for help use: `SUBCOMMANDS -h`"
|
84
|
+
)
|
85
|
+
subparsers.required = True
|
86
|
+
|
87
|
+
subparsers.add_parser(
|
88
|
+
"lint-config",
|
89
|
+
description="Lint antsibull-nox configuration file",
|
90
|
+
)
|
91
|
+
|
92
|
+
subparsers.add_parser(
|
93
|
+
"init",
|
94
|
+
description="Create noxfile and antsibull-nox configuration file",
|
95
|
+
)
|
96
|
+
|
97
|
+
# This must come after all parser setup
|
98
|
+
if HAS_ARGCOMPLETE:
|
99
|
+
argcomplete.autocomplete(toplevel_parser)
|
100
|
+
|
101
|
+
parsed_args: argparse.Namespace = toplevel_parser.parse_args(args)
|
102
|
+
return parsed_args
|
103
|
+
|
104
|
+
|
105
|
+
def run(args: list[str]) -> int:
|
106
|
+
"""
|
107
|
+
Run the program.
|
108
|
+
"""
|
109
|
+
program_name = os.path.basename(args[0])
|
110
|
+
try:
|
111
|
+
parsed_args: argparse.Namespace = parse_args(program_name, args[1:])
|
112
|
+
except InvalidArgumentError as e:
|
113
|
+
print(e, file=sys.stderr)
|
114
|
+
return 2
|
115
|
+
|
116
|
+
return ARGS_MAP[parsed_args.command](parsed_args)
|
117
|
+
|
118
|
+
|
119
|
+
def main() -> int:
|
120
|
+
"""
|
121
|
+
Entrypoint called from the script.
|
122
|
+
|
123
|
+
Return codes:
|
124
|
+
:0: Success
|
125
|
+
:1: Unhandled error. See the Traceback for more information.
|
126
|
+
:2: There was a problem with the command line arguments
|
127
|
+
"""
|
128
|
+
return run(sys.argv)
|
129
|
+
|
130
|
+
|
131
|
+
if __name__ == "__main__":
|
132
|
+
sys.exit(main())
|
@@ -21,7 +21,7 @@ from .install import (
|
|
21
21
|
setup_collections,
|
22
22
|
setup_current_tree,
|
23
23
|
)
|
24
|
-
from .search import CollectionList, load_collection_data_from_disk
|
24
|
+
from .search import GALAXY_YML, CollectionList, load_collection_data_from_disk
|
25
25
|
|
26
26
|
|
27
27
|
def force_collection_version(path: Path, *, version: str) -> bool:
|
@@ -31,7 +31,7 @@ def force_collection_version(path: Path, *, version: str) -> bool:
|
|
31
31
|
Returns ``True`` if the version was changed, and ``False`` if the version
|
32
32
|
was already set to this value.
|
33
33
|
"""
|
34
|
-
galaxy_yml = path /
|
34
|
+
galaxy_yml = path / GALAXY_YML
|
35
35
|
try:
|
36
36
|
data = load_yaml_file(galaxy_yml)
|
37
37
|
except Exception as exc:
|
antsibull_nox/collection/data.py
CHANGED
@@ -10,6 +10,8 @@ Data types for collections.
|
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
12
|
|
13
|
+
import base64
|
14
|
+
import hashlib
|
13
15
|
from dataclasses import dataclass
|
14
16
|
from pathlib import Path
|
15
17
|
|
@@ -98,6 +100,16 @@ class CollectionSource:
|
|
98
100
|
)
|
99
101
|
return source
|
100
102
|
|
103
|
+
def identifier(self) -> str:
|
104
|
+
"""
|
105
|
+
Compute a source identifier.
|
106
|
+
"""
|
107
|
+
hasher = hashlib.sha256()
|
108
|
+
hasher.update(self.name.encode("utf-8"))
|
109
|
+
hasher.update(b"::")
|
110
|
+
hasher.update(self.source.encode("utf-8"))
|
111
|
+
return base64.b32encode(hasher.digest())[:16].decode("ascii")
|
112
|
+
|
101
113
|
|
102
114
|
__all__ = [
|
103
115
|
"CollectionData",
|