antsibull-nox 0.2.0__py3-none-any.whl → 0.3.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.
@@ -0,0 +1,147 @@
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
+ Create nox build import check session.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ import nox
18
+
19
+ from ..collection import (
20
+ force_collection_version,
21
+ load_collection_data_from_disk,
22
+ )
23
+ from ..paths import (
24
+ copy_collection,
25
+ remove_path,
26
+ )
27
+ from .utils import (
28
+ ci_group,
29
+ compose_description,
30
+ install,
31
+ nox_has_verbosity,
32
+ silence_run_verbosity,
33
+ )
34
+
35
+
36
+ def add_build_import_check(
37
+ *,
38
+ make_build_import_check_default: bool = True,
39
+ ansible_core_package: str = "ansible-core",
40
+ run_galaxy_importer: bool = True,
41
+ galaxy_importer_package: str = "galaxy-importer",
42
+ galaxy_importer_config_path: (
43
+ str | os.PathLike | None
44
+ ) = None, # https://github.com/ansible/galaxy-importer#configuration
45
+ galaxy_importer_always_show_logs: bool = False,
46
+ ) -> None:
47
+ """
48
+ Add license-check session for license checks.
49
+ """
50
+
51
+ def compose_dependencies() -> list[str]:
52
+ deps = [ansible_core_package]
53
+ if run_galaxy_importer:
54
+ deps.append(galaxy_importer_package)
55
+ return deps
56
+
57
+ def build_import_check(session: nox.Session) -> None:
58
+ install(session, *compose_dependencies())
59
+
60
+ tmp = Path(session.create_tmp())
61
+ collection_dir = tmp / "collection"
62
+ remove_path(collection_dir)
63
+ copy_collection(Path.cwd(), collection_dir)
64
+
65
+ collection = load_collection_data_from_disk(
66
+ collection_dir, accept_manifest=False
67
+ )
68
+ version = collection.version
69
+ if not version:
70
+ version = "0.0.1"
71
+ force_collection_version(collection_dir, version=version)
72
+
73
+ with session.chdir(collection_dir):
74
+ build_ran = session.run("ansible-galaxy", "collection", "build") is not None
75
+
76
+ tarball = (
77
+ collection_dir
78
+ / f"{collection.namespace}-{collection.name}-{version}.tar.gz"
79
+ )
80
+ if build_ran and not tarball.is_file():
81
+ files = "\n".join(
82
+ f"* {path.name}"
83
+ for path in collection_dir.iterdir()
84
+ if not path.is_dir()
85
+ )
86
+ session.error(f"Cannot find file {tarball}! List of all files:\n{files}")
87
+
88
+ if run_galaxy_importer and tarball.is_file():
89
+ env = {}
90
+ if galaxy_importer_config_path:
91
+ env["GALAXY_IMPORTER_CONFIG"] = str(
92
+ Path(galaxy_importer_config_path).absolute()
93
+ )
94
+ with session.chdir(collection_dir), silence_run_verbosity():
95
+ import_log = session.run(
96
+ "python",
97
+ "-m",
98
+ "galaxy_importer.main",
99
+ tarball.name,
100
+ env=env,
101
+ silent=True,
102
+ )
103
+ if import_log is not None:
104
+ with ci_group("Run Galaxy importer") as (indent, is_collapsed):
105
+ if (
106
+ is_collapsed
107
+ or galaxy_importer_always_show_logs
108
+ or nox_has_verbosity()
109
+ ):
110
+ for line in import_log.splitlines():
111
+ print(f"{indent}{line}")
112
+ sys.stdout.flush()
113
+ error_prefix = "ERROR:"
114
+ errors = []
115
+ for line in import_log.splitlines():
116
+ if line.startswith(error_prefix):
117
+ errors.append(line[len(error_prefix) :].strip())
118
+ if errors:
119
+ messages = "\n".join(f"* {error}" for error in errors)
120
+ session.warn(
121
+ "Galaxy importer emitted the following non-fatal"
122
+ f" error{'' if len(errors) == 1 else 's'}:\n{messages}"
123
+ )
124
+
125
+ build_import_check.__doc__ = compose_description(
126
+ prefix={
127
+ "one": "Run build and import checker:",
128
+ "other": "Run build and import checkers:",
129
+ },
130
+ programs={
131
+ "build-collection": True,
132
+ "galaxy-importer": (
133
+ "test whether Galaxy will import built collection"
134
+ if run_galaxy_importer
135
+ else False
136
+ ),
137
+ },
138
+ )
139
+ nox.session(
140
+ name="build-import-check",
141
+ default=make_build_import_check_default,
142
+ )(build_import_check)
143
+
144
+
145
+ __all__ = [
146
+ "add_build_import_check",
147
+ ]
@@ -0,0 +1,137 @@
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
+ Create nox sessions.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import subprocess
15
+ import typing as t
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+
19
+ import nox
20
+
21
+ from ..collection import (
22
+ CollectionData,
23
+ setup_collections,
24
+ setup_current_tree,
25
+ )
26
+ from ..paths import (
27
+ create_temp_directory,
28
+ )
29
+
30
+
31
+ @dataclass
32
+ class CollectionSetup:
33
+ """
34
+ Information on the setup collections.
35
+ """
36
+
37
+ # The path of the ansible_collections directory where all dependent collections
38
+ # are installed. Is currently identical to current_root, but that might change
39
+ # or depend on options in the future.
40
+ collections_root: Path
41
+
42
+ # The directory in which ansible_collections can be found, as well as
43
+ # ansible_collections/<namespace>/<name> points to a copy of the current collection.
44
+ current_place: Path
45
+
46
+ # The path of the ansible_collections directory that contains the current collection.
47
+ # The following is always true:
48
+ # current_root == current_place / "ansible_collections"
49
+ current_root: Path
50
+
51
+ # Data on the current collection (as in the repository).
52
+ current_collection: CollectionData
53
+
54
+ # The path of the current collection inside the collection tree below current_root.
55
+ # The following is always true:
56
+ # current_path == current_root / current_collection.namespace / current_collection.name
57
+ current_path: Path
58
+
59
+ def prefix_current_paths(self, paths: list[str]) -> list[str]:
60
+ """
61
+ Prefix the list of given paths with ``current_path``.
62
+ """
63
+ result = []
64
+ for path in paths:
65
+ prefixed_path = (self.current_path / path).relative_to(self.current_place)
66
+ if prefixed_path.exists():
67
+ result.append(str(prefixed_path))
68
+ return result
69
+
70
+
71
+ def _run_subprocess(args: list[str]) -> tuple[bytes, bytes]:
72
+ p = subprocess.run(args, check=True, capture_output=True)
73
+ return p.stdout, p.stderr
74
+
75
+
76
+ def prepare_collections(
77
+ session: nox.Session,
78
+ *,
79
+ install_in_site_packages: bool,
80
+ extra_deps_files: list[str | os.PathLike] | None = None,
81
+ extra_collections: list[str] | None = None,
82
+ install_out_of_tree: bool = False, # can not be used with install_in_site_packages=True
83
+ ) -> CollectionSetup | None:
84
+ """
85
+ Install collections in site-packages.
86
+ """
87
+ if install_out_of_tree and install_in_site_packages:
88
+ raise ValueError(
89
+ "install_out_of_tree=True cannot be combined with install_in_site_packages=True"
90
+ )
91
+ if isinstance(session.virtualenv, nox.virtualenv.PassthroughEnv):
92
+ session.warn("No venv. Skip preparing collections...")
93
+ return None
94
+ if install_in_site_packages:
95
+ purelib = (
96
+ session.run(
97
+ "python",
98
+ "-c",
99
+ "import sysconfig; print(sysconfig.get_path('purelib'))",
100
+ silent=True,
101
+ )
102
+ or ""
103
+ ).strip()
104
+ if not purelib:
105
+ session.warn(
106
+ "Cannot find site-packages (probably due to install-only run)."
107
+ " Skip preparing collections..."
108
+ )
109
+ return None
110
+ place = Path(purelib)
111
+ elif install_out_of_tree:
112
+ place = create_temp_directory(f"antsibull-nox-{session.name}-collection-root-")
113
+ else:
114
+ place = Path(session.virtualenv.location) / "collection-root"
115
+ place.mkdir(exist_ok=True)
116
+ setup = setup_collections(
117
+ place,
118
+ _run_subprocess,
119
+ extra_deps_files=extra_deps_files,
120
+ extra_collections=extra_collections,
121
+ with_current=False,
122
+ global_cache_dir=session.cache_dir,
123
+ )
124
+ current_setup = setup_current_tree(place, setup.current_collection)
125
+ return CollectionSetup(
126
+ collections_root=setup.root,
127
+ current_place=place,
128
+ current_root=current_setup.root,
129
+ current_collection=setup.current_collection,
130
+ current_path=t.cast(Path, current_setup.current_path),
131
+ )
132
+
133
+
134
+ __all__ = [
135
+ "CollectionSetup",
136
+ "prepare_collections",
137
+ ]
@@ -0,0 +1,78 @@
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
+ Create nox docs check session.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import typing as t
14
+
15
+ import nox
16
+
17
+ from .collections import (
18
+ CollectionSetup,
19
+ prepare_collections,
20
+ )
21
+ from .utils import install
22
+
23
+
24
+ def add_docs_check(
25
+ *,
26
+ make_docs_check_default: bool = True,
27
+ antsibull_docs_package: str = "antsibull-docs",
28
+ ansible_core_package: str = "ansible-core",
29
+ validate_collection_refs: t.Literal["self", "dependent", "all"] | None = None,
30
+ extra_collections: list[str] | None = None,
31
+ ) -> None:
32
+ """
33
+ Add docs-check session for linting.
34
+ """
35
+
36
+ def compose_dependencies() -> list[str]:
37
+ deps = [antsibull_docs_package, ansible_core_package]
38
+ return deps
39
+
40
+ def execute_antsibull_docs(
41
+ session: nox.Session, prepared_collections: CollectionSetup
42
+ ) -> None:
43
+ with session.chdir(prepared_collections.current_path):
44
+ collections_path = f"{prepared_collections.current_place}"
45
+ command = [
46
+ "antsibull-docs",
47
+ "lint-collection-docs",
48
+ "--plugin-docs",
49
+ "--skip-rstcheck",
50
+ ".",
51
+ ]
52
+ if validate_collection_refs:
53
+ command.extend(["--validate-collection-refs", validate_collection_refs])
54
+ session.run(*command, env={"ANSIBLE_COLLECTIONS_PATH": collections_path})
55
+
56
+ def docs_check(session: nox.Session) -> None:
57
+ install(session, *compose_dependencies())
58
+ prepared_collections = prepare_collections(
59
+ session,
60
+ install_in_site_packages=False,
61
+ extra_collections=extra_collections,
62
+ install_out_of_tree=True,
63
+ )
64
+ if not prepared_collections:
65
+ session.warn("Skipping antsibull-docs...")
66
+ if prepared_collections:
67
+ execute_antsibull_docs(session, prepared_collections)
68
+
69
+ docs_check.__doc__ = "Run 'antsibull-docs lint-collection-docs'"
70
+ nox.session(
71
+ name="docs-check",
72
+ default=make_docs_check_default,
73
+ )(docs_check)
74
+
75
+
76
+ __all__ = [
77
+ "add_docs_check",
78
+ ]
@@ -0,0 +1,127 @@
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
+ Create nox extra checks session.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import asdict, dataclass
14
+
15
+ import nox
16
+
17
+ from .utils import (
18
+ compose_description,
19
+ run_bare_script,
20
+ )
21
+
22
+
23
+ @dataclass
24
+ class ActionGroup:
25
+ """
26
+ Defines an action group.
27
+ """
28
+
29
+ # Name of the action group.
30
+ name: str
31
+ # Regex pattern to match modules that could belong to this action group.
32
+ pattern: str
33
+ # Doc fragment that members of the action group must have, but no other module
34
+ # must have
35
+ doc_fragment: str
36
+ # Exclusion list of modules that match the regex, but should not be part of the
37
+ # action group. All other modules matching the regex are assumed to be part of
38
+ # the action group.
39
+ exclusions: list[str] | None = None
40
+
41
+
42
+ def add_extra_checks(
43
+ *,
44
+ make_extra_checks_default: bool = True,
45
+ # no-unwanted-files:
46
+ run_no_unwanted_files: bool = True,
47
+ no_unwanted_files_module_extensions: (
48
+ list[str] | None
49
+ ) = None, # default: .cs, .ps1, .psm1, .py
50
+ no_unwanted_files_other_extensions: list[str] | None = None, # default: .py, .pyi
51
+ no_unwanted_files_yaml_extensions: list[str] | None = None, # default: .yml, .yaml
52
+ no_unwanted_files_skip_paths: list[str] | None = None, # default: []
53
+ no_unwanted_files_skip_directories: list[str] | None = None, # default: []
54
+ no_unwanted_files_yaml_directories: (
55
+ list[str] | None
56
+ ) = None, # default: plugins/test/, plugins/filter/
57
+ no_unwanted_files_allow_symlinks: bool = False,
58
+ # action-groups:
59
+ run_action_groups: bool = False,
60
+ action_groups_config: list[ActionGroup] | None = None,
61
+ ) -> None:
62
+ """
63
+ Add extra-checks session for extra checks.
64
+ """
65
+
66
+ def execute_no_unwanted_files(session: nox.Session) -> None:
67
+ run_bare_script(
68
+ session,
69
+ "no-unwanted-files",
70
+ extra_data={
71
+ "module_extensions": no_unwanted_files_module_extensions
72
+ or [".cs", ".ps1", ".psm1", ".py"],
73
+ "other_extensions": no_unwanted_files_other_extensions
74
+ or [".py", ".pyi"],
75
+ "yaml_extensions": no_unwanted_files_yaml_extensions
76
+ or [".yml", ".yaml"],
77
+ "skip_paths": no_unwanted_files_skip_paths or [],
78
+ "skip_directories": no_unwanted_files_skip_directories or [],
79
+ "yaml_directories": no_unwanted_files_yaml_directories
80
+ or ["plugins/test/", "plugins/filter/"],
81
+ "allow_symlinks": no_unwanted_files_allow_symlinks,
82
+ },
83
+ )
84
+
85
+ def execute_action_groups(session: nox.Session) -> None:
86
+ if action_groups_config is None:
87
+ session.warn("Skipping action-groups since config is not provided...")
88
+ return
89
+ run_bare_script(
90
+ session,
91
+ "action-groups",
92
+ extra_data={
93
+ "config": [asdict(cfg) for cfg in action_groups_config],
94
+ },
95
+ )
96
+
97
+ def extra_checks(session: nox.Session) -> None:
98
+ if run_no_unwanted_files:
99
+ execute_no_unwanted_files(session)
100
+ if run_action_groups:
101
+ execute_action_groups(session)
102
+
103
+ extra_checks.__doc__ = compose_description(
104
+ prefix={
105
+ "one": "Run extra checker:",
106
+ "other": "Run extra checkers:",
107
+ },
108
+ programs={
109
+ "no-unwanted-files": (
110
+ "checks for unwanted files in plugins/"
111
+ if run_no_unwanted_files
112
+ else False
113
+ ),
114
+ "action-groups": "validate action groups" if run_action_groups else False,
115
+ },
116
+ )
117
+ nox.session(
118
+ name="extra-checks",
119
+ python=False,
120
+ default=make_extra_checks_default,
121
+ )(extra_checks)
122
+
123
+
124
+ __all__ = [
125
+ "ActionGroup",
126
+ "add_extra_checks",
127
+ ]
@@ -0,0 +1,73 @@
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
+ Create nox license check session.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import nox
14
+
15
+ from .utils import (
16
+ compose_description,
17
+ install,
18
+ run_bare_script,
19
+ )
20
+
21
+
22
+ def add_license_check(
23
+ *,
24
+ make_license_check_default: bool = True,
25
+ run_reuse: bool = True,
26
+ reuse_package: str = "reuse",
27
+ run_license_check: bool = True,
28
+ license_check_extra_ignore_paths: list[str] | None = None,
29
+ ) -> None:
30
+ """
31
+ Add license-check session for license checks.
32
+ """
33
+
34
+ def compose_dependencies() -> list[str]:
35
+ deps = []
36
+ if run_reuse:
37
+ deps.append(reuse_package)
38
+ return deps
39
+
40
+ def license_check(session: nox.Session) -> None:
41
+ install(session, *compose_dependencies())
42
+ if run_reuse:
43
+ session.run("reuse", "lint")
44
+ if run_license_check:
45
+ run_bare_script(
46
+ session,
47
+ "license-check",
48
+ extra_data={
49
+ "extra_ignore_paths": license_check_extra_ignore_paths or [],
50
+ },
51
+ )
52
+
53
+ license_check.__doc__ = compose_description(
54
+ prefix={
55
+ "one": "Run license checker:",
56
+ "other": "Run license checkers:",
57
+ },
58
+ programs={
59
+ "reuse": run_reuse,
60
+ "license-check": (
61
+ "ensure GPLv3+ for plugins" if run_license_check else False
62
+ ),
63
+ },
64
+ )
65
+ nox.session(
66
+ name="license-check",
67
+ default=make_license_check_default,
68
+ )(license_check)
69
+
70
+
71
+ __all__ = [
72
+ "add_license_check",
73
+ ]