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/config.py
CHANGED
@@ -15,6 +15,7 @@ import typing as t
|
|
15
15
|
|
16
16
|
import pydantic as p
|
17
17
|
|
18
|
+
from ._pydantic import forbid_extras, get_formatted_error_messages
|
18
19
|
from .ansible import AnsibleCoreVersion
|
19
20
|
from .utils import Version
|
20
21
|
|
@@ -24,6 +25,9 @@ except ImportError:
|
|
24
25
|
from tomli import load as _load_toml # type: ignore
|
25
26
|
|
26
27
|
|
28
|
+
CONFIG_FILENAME = "antsibull-nox.toml"
|
29
|
+
|
30
|
+
|
27
31
|
def _parse_version(value: t.Any) -> Version:
|
28
32
|
if isinstance(value, Version):
|
29
33
|
return value
|
@@ -45,6 +49,18 @@ def _parse_ansible_core_version(value: t.Any) -> AnsibleCoreVersion:
|
|
45
49
|
raise ValueError("Must be ansible-core version string")
|
46
50
|
|
47
51
|
|
52
|
+
def _validate_collection_name(value: str) -> str:
|
53
|
+
parts = value.split(".")
|
54
|
+
if len(parts) != 2:
|
55
|
+
raise ValueError("Collection name must be of the form '<namespace>.<name>'")
|
56
|
+
if not parts[0].isidentifier():
|
57
|
+
raise ValueError("Collection namespace must be Python identifier")
|
58
|
+
if not parts[1].isidentifier():
|
59
|
+
raise ValueError("Collection name must be Python identifier")
|
60
|
+
return value
|
61
|
+
|
62
|
+
|
63
|
+
CollectionName = t.Annotated[str, p.AfterValidator(_validate_collection_name)]
|
48
64
|
PVersion = t.Annotated[Version, p.BeforeValidator(_parse_version)]
|
49
65
|
PAnsibleCoreVersion = t.Annotated[
|
50
66
|
AnsibleCoreVersion, p.BeforeValidator(_parse_ansible_core_version)
|
@@ -101,6 +117,9 @@ class SessionLint(_BaseModel):
|
|
101
117
|
mypy_ansible_core_package: t.Optional[str] = "ansible-core"
|
102
118
|
mypy_extra_deps: list[str] = []
|
103
119
|
|
120
|
+
# antsibull-nox config lint:
|
121
|
+
run_antsibullnox_config_lint: bool = True
|
122
|
+
|
104
123
|
|
105
124
|
class SessionDocsCheck(_BaseModel):
|
106
125
|
"""
|
@@ -112,7 +131,7 @@ class SessionDocsCheck(_BaseModel):
|
|
112
131
|
antsibull_docs_package: str = "antsibull-docs"
|
113
132
|
ansible_core_package: str = "ansible-core"
|
114
133
|
validate_collection_refs: t.Optional[t.Literal["self", "dependent", "all"]] = None
|
115
|
-
extra_collections: list[
|
134
|
+
extra_collections: list[CollectionName] = []
|
116
135
|
|
117
136
|
|
118
137
|
class SessionLicenseCheck(_BaseModel):
|
@@ -183,6 +202,7 @@ class SessionBuildImportCheck(_BaseModel):
|
|
183
202
|
galaxy_importer_package: str = "galaxy-importer"
|
184
203
|
# https://github.com/ansible/galaxy-importer#configuration
|
185
204
|
galaxy_importer_config_path: t.Optional[p.FilePath] = None
|
205
|
+
galaxy_importer_always_show_logs: bool = False
|
186
206
|
|
187
207
|
|
188
208
|
class DevelLikeBranch(_BaseModel):
|
@@ -220,6 +240,9 @@ class SessionAnsibleTestSanity(_BaseModel):
|
|
220
240
|
min_version: t.Optional[PVersion] = None
|
221
241
|
max_version: t.Optional[PVersion] = None
|
222
242
|
except_versions: list[PAnsibleCoreVersion] = []
|
243
|
+
skip_tests: list[str] = []
|
244
|
+
allow_disabled: bool = False
|
245
|
+
enable_optional_errors: bool = False
|
223
246
|
|
224
247
|
|
225
248
|
class SessionAnsibleTestUnits(_BaseModel):
|
@@ -316,7 +339,10 @@ class Config(_BaseModel):
|
|
316
339
|
The contents of a antsibull-nox config file.
|
317
340
|
"""
|
318
341
|
|
319
|
-
collection_sources: dict[
|
342
|
+
collection_sources: dict[CollectionName, CollectionSource] = {}
|
343
|
+
collection_sources_per_ansible: dict[
|
344
|
+
PAnsibleCoreVersion, dict[CollectionName, CollectionSource]
|
345
|
+
] = {}
|
320
346
|
sessions: Sessions = Sessions()
|
321
347
|
|
322
348
|
|
@@ -330,3 +356,26 @@ def load_config_from_toml(path: str | os.PathLike) -> Config:
|
|
330
356
|
except ValueError as exc:
|
331
357
|
raise ValueError(f"Error while reading {path}: {exc}") from exc
|
332
358
|
return Config.model_validate(data)
|
359
|
+
|
360
|
+
|
361
|
+
def lint_config_toml() -> list[str]:
|
362
|
+
"""
|
363
|
+
Lint config files
|
364
|
+
"""
|
365
|
+
path = CONFIG_FILENAME
|
366
|
+
errors = []
|
367
|
+
forbid_extras(Config)
|
368
|
+
try:
|
369
|
+
with open(path, "rb") as f:
|
370
|
+
data = _load_toml(f)
|
371
|
+
Config.model_validate(data)
|
372
|
+
except p.ValidationError as exc:
|
373
|
+
for error in get_formatted_error_messages(exc):
|
374
|
+
errors.append(f"{path}:{error}")
|
375
|
+
except ValueError as exc:
|
376
|
+
errors.append(f"{path}:{exc}")
|
377
|
+
except FileNotFoundError:
|
378
|
+
errors.append(f"{path}: File does not exist")
|
379
|
+
except IOError as exc:
|
380
|
+
errors.append(f"{path}:{exc}")
|
381
|
+
return errors
|
@@ -17,7 +17,7 @@ import typing as t
|
|
17
17
|
import yaml
|
18
18
|
|
19
19
|
from antsibull_nox.data.antsibull_nox_data_util import setup
|
20
|
-
from antsibull_nox.sessions import ActionGroup
|
20
|
+
from antsibull_nox.sessions.extra_checks import ActionGroup
|
21
21
|
|
22
22
|
|
23
23
|
def compile_patterns(
|
@@ -41,7 +41,7 @@ def load_redirects(
|
|
41
41
|
try:
|
42
42
|
with open(meta_runtime, "rb") as f:
|
43
43
|
data = yaml.safe_load(f)
|
44
|
-
action_groups = data
|
44
|
+
action_groups = data.get("action_groups", {})
|
45
45
|
except Exception as exc:
|
46
46
|
errors.append(f"{meta_runtime}: cannot load action groups: {exc}")
|
47
47
|
return {}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
# Copyright (c) 2025, Felix Fontein <felix@fontein.de>
|
4
|
+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt
|
5
|
+
# or https://www.gnu.org/licenses/gpl-3.0.txt)
|
6
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
7
|
+
|
8
|
+
"""Run antsibull-nox lint-config."""
|
9
|
+
|
10
|
+
from __future__ import annotations
|
11
|
+
|
12
|
+
import sys
|
13
|
+
|
14
|
+
from antsibull_nox.data.antsibull_nox_data_util import setup
|
15
|
+
from antsibull_nox.lint_config import lint_config
|
16
|
+
|
17
|
+
|
18
|
+
def main() -> int:
|
19
|
+
"""Main entry point."""
|
20
|
+
_, __ = setup()
|
21
|
+
|
22
|
+
errors = lint_config()
|
23
|
+
for error in errors:
|
24
|
+
print(error)
|
25
|
+
return len(errors) > 0
|
26
|
+
|
27
|
+
|
28
|
+
if __name__ == "__main__":
|
29
|
+
sys.exit(main())
|
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
# Copyright (c) 2025, Felix Fontein <felix@fontein.de>
|
4
|
+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt
|
5
|
+
# or https://www.gnu.org/licenses/gpl-3.0.txt)
|
6
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
7
|
+
|
8
|
+
"""Make sure all plugin and module documentation adheres to yamllint."""
|
9
|
+
|
10
|
+
from __future__ import annotations
|
11
|
+
|
12
|
+
import io
|
13
|
+
import os
|
14
|
+
import sys
|
15
|
+
import traceback
|
16
|
+
import typing as t
|
17
|
+
|
18
|
+
from antsibull_nox_data_util import setup # type: ignore
|
19
|
+
from yamllint import linter
|
20
|
+
from yamllint.cli import find_project_config_filepath
|
21
|
+
from yamllint.config import YamlLintConfig
|
22
|
+
from yamllint.linter import PROBLEM_LEVELS
|
23
|
+
|
24
|
+
REPORT_LEVELS: set[PROBLEM_LEVELS] = {
|
25
|
+
"warning",
|
26
|
+
"error",
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
def lint(
|
31
|
+
*,
|
32
|
+
errors: list[dict[str, t.Any]],
|
33
|
+
path: str,
|
34
|
+
data: str,
|
35
|
+
config: YamlLintConfig,
|
36
|
+
) -> None:
|
37
|
+
try:
|
38
|
+
problems = linter.run(
|
39
|
+
io.StringIO(data),
|
40
|
+
config,
|
41
|
+
path,
|
42
|
+
)
|
43
|
+
for problem in problems:
|
44
|
+
if problem.level not in REPORT_LEVELS:
|
45
|
+
continue
|
46
|
+
msg = f"{problem.level}: {problem.desc}"
|
47
|
+
if problem.rule:
|
48
|
+
msg += f" ({problem.rule})"
|
49
|
+
errors.append(
|
50
|
+
{
|
51
|
+
"path": path,
|
52
|
+
"line": problem.line,
|
53
|
+
"col": problem.column,
|
54
|
+
"message": msg,
|
55
|
+
}
|
56
|
+
)
|
57
|
+
except Exception as exc:
|
58
|
+
error = str(exc).replace("\n", " / ")
|
59
|
+
errors.append(
|
60
|
+
{
|
61
|
+
"path": path,
|
62
|
+
"line": 1,
|
63
|
+
"col": 1,
|
64
|
+
"message": (
|
65
|
+
f"Internal error while linting YAML: exception {type(exc)}:"
|
66
|
+
f" {error}; traceback: {traceback.format_exc()!r}"
|
67
|
+
),
|
68
|
+
}
|
69
|
+
)
|
70
|
+
|
71
|
+
|
72
|
+
def process_yaml_file(
|
73
|
+
errors: list[dict[str, t.Any]],
|
74
|
+
path: str,
|
75
|
+
config: YamlLintConfig,
|
76
|
+
) -> None:
|
77
|
+
try:
|
78
|
+
with open(path, "rt", encoding="utf-8") as stream:
|
79
|
+
data = stream.read()
|
80
|
+
except Exception as exc:
|
81
|
+
errors.append(
|
82
|
+
{
|
83
|
+
"path": path,
|
84
|
+
"line": 1,
|
85
|
+
"col": 1,
|
86
|
+
"message": (
|
87
|
+
f"Error while parsing Python code: exception {type(exc)}:"
|
88
|
+
f" {exc}; traceback: {traceback.format_exc()!r}"
|
89
|
+
),
|
90
|
+
}
|
91
|
+
)
|
92
|
+
return
|
93
|
+
|
94
|
+
lint(
|
95
|
+
errors=errors,
|
96
|
+
path=path,
|
97
|
+
data=data,
|
98
|
+
config=config,
|
99
|
+
)
|
100
|
+
|
101
|
+
|
102
|
+
def main() -> int:
|
103
|
+
"""Main entry point."""
|
104
|
+
paths, extra_data = setup()
|
105
|
+
config: str | None = extra_data.get("config")
|
106
|
+
|
107
|
+
if config is None:
|
108
|
+
config = find_project_config_filepath()
|
109
|
+
|
110
|
+
if config:
|
111
|
+
yamllint_config = YamlLintConfig(file=config)
|
112
|
+
else:
|
113
|
+
yamllint_config = YamlLintConfig(content="extends: default")
|
114
|
+
|
115
|
+
errors: list[dict[str, t.Any]] = []
|
116
|
+
for path in paths:
|
117
|
+
if not os.path.isfile(path):
|
118
|
+
continue
|
119
|
+
process_yaml_file(errors, path, yamllint_config)
|
120
|
+
|
121
|
+
errors.sort(
|
122
|
+
key=lambda error: (error["path"], error["line"], error["col"], error["message"])
|
123
|
+
)
|
124
|
+
for error in errors:
|
125
|
+
prefix = f"{error['path']}:{error['line']}:{error['col']}: "
|
126
|
+
msg = error["message"]
|
127
|
+
if "note" in error:
|
128
|
+
msg = f"{msg}\nNote: {error['note']}"
|
129
|
+
for i, line in enumerate(msg.splitlines()):
|
130
|
+
print(f"{prefix}{line}")
|
131
|
+
if i == 0:
|
132
|
+
prefix = " " * len(prefix)
|
133
|
+
|
134
|
+
return len(errors) > 0
|
135
|
+
|
136
|
+
|
137
|
+
if __name__ == "__main__":
|
138
|
+
sys.exit(main())
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
11
11
|
|
12
12
|
import glob
|
13
13
|
import os
|
14
|
+
import stat
|
14
15
|
import sys
|
15
16
|
|
16
17
|
from antsibull_nox.data.antsibull_nox_data_util import get_list_of_strings, setup
|
@@ -108,7 +109,10 @@ def main() -> int:
|
|
108
109
|
path = path[2:]
|
109
110
|
if path in ignore_paths or path.startswith("tests/output/"):
|
110
111
|
continue
|
111
|
-
|
112
|
+
sr = os.stat(path)
|
113
|
+
if not stat.S_ISREG(sr.st_mode):
|
114
|
+
continue
|
115
|
+
if sr.st_size == 0:
|
112
116
|
continue
|
113
117
|
if not path.endswith(".license") and os.path.exists(path + ".license"):
|
114
118
|
path = path + ".license"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
|
3
|
-
# Copyright (c)
|
3
|
+
# Copyright (c) 2025, Felix Fontein <felix@fontein.de>
|
4
4
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt
|
5
5
|
# or https://www.gnu.org/licenses/gpl-3.0.txt)
|
6
6
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
11
11
|
|
12
12
|
import ast
|
13
13
|
import io
|
14
|
+
import os
|
14
15
|
import re
|
15
16
|
import sys
|
16
17
|
import traceback
|
@@ -43,6 +44,13 @@ def lint(
|
|
43
44
|
config: YamlLintConfig,
|
44
45
|
extra_for_errors: dict[str, t.Any] | None = None,
|
45
46
|
) -> None:
|
47
|
+
# If the string start with optional whitespace + linebreak, skip that line
|
48
|
+
idx = data.find("\n")
|
49
|
+
if idx >= 0 and (idx == 0 or data[:idx].isspace()):
|
50
|
+
data = data[idx + 1 :]
|
51
|
+
row_offset += 1
|
52
|
+
col_offset = 0
|
53
|
+
|
46
54
|
try:
|
47
55
|
problems = linter.run(
|
48
56
|
io.StringIO(data),
|
@@ -83,6 +91,20 @@ def lint(
|
|
83
91
|
errors[-1].update(extra_for_errors)
|
84
92
|
|
85
93
|
|
94
|
+
def iterate_targets(
|
95
|
+
assignment: ast.Assign,
|
96
|
+
) -> t.Iterable[tuple[ast.Constant, str, str]]:
|
97
|
+
if not isinstance(assignment.value, ast.Constant):
|
98
|
+
return
|
99
|
+
if not isinstance(assignment.value.value, str):
|
100
|
+
return
|
101
|
+
for target in assignment.targets:
|
102
|
+
try:
|
103
|
+
yield assignment.value, assignment.value.value, target.id # type: ignore
|
104
|
+
except AttributeError:
|
105
|
+
continue
|
106
|
+
|
107
|
+
|
86
108
|
def process_python_file(
|
87
109
|
errors: list[dict[str, t.Any]],
|
88
110
|
path: str,
|
@@ -106,33 +128,39 @@ def process_python_file(
|
|
106
128
|
)
|
107
129
|
return
|
108
130
|
|
109
|
-
|
131
|
+
is_doc_fragment = path.startswith("plugins/doc_fragments/")
|
132
|
+
|
133
|
+
# We look for top-level assignments and classes
|
110
134
|
for child in root.body:
|
135
|
+
if (
|
136
|
+
is_doc_fragment
|
137
|
+
and isinstance(child, ast.ClassDef)
|
138
|
+
and child.name == "ModuleDocFragment"
|
139
|
+
):
|
140
|
+
for fragment in child.body:
|
141
|
+
if not isinstance(fragment, ast.Assign):
|
142
|
+
continue
|
143
|
+
for constant, data, fragment_name in iterate_targets(fragment):
|
144
|
+
lint(
|
145
|
+
errors=errors,
|
146
|
+
path=path,
|
147
|
+
data=data,
|
148
|
+
row_offset=constant.lineno - 1,
|
149
|
+
col_offset=constant.col_offset - 1,
|
150
|
+
section=fragment_name,
|
151
|
+
config=config,
|
152
|
+
)
|
111
153
|
if not isinstance(child, ast.Assign):
|
112
154
|
continue
|
113
|
-
|
114
|
-
continue
|
115
|
-
if not isinstance(child.value.value, str):
|
116
|
-
continue
|
117
|
-
for target in child.targets:
|
118
|
-
try:
|
119
|
-
section = target.id # type: ignore
|
120
|
-
except AttributeError:
|
121
|
-
continue
|
155
|
+
for constant, data, section in iterate_targets(child):
|
122
156
|
if section not in ("DOCUMENTATION", "EXAMPLES", "RETURN"):
|
123
157
|
continue
|
124
158
|
|
125
|
-
#
|
126
|
-
data
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# If the string start with optional whitespace + linebreak, skip that line
|
131
|
-
idx = data.find("\n")
|
132
|
-
if idx >= 0 and (idx == 0 or data[:idx].isspace()):
|
133
|
-
data = data[idx + 1 :]
|
134
|
-
row_offset += 1
|
135
|
-
col_offset = 0
|
159
|
+
# Handle special values
|
160
|
+
if data in ("#", " # ") and section == "RETURN":
|
161
|
+
# Not skipping it here could result in all kind of linting errors,
|
162
|
+
# like no document start, or trailing space.
|
163
|
+
continue
|
136
164
|
|
137
165
|
# Check for non-YAML examples
|
138
166
|
if section == EXAMPLES_SECTION:
|
@@ -145,8 +173,8 @@ def process_python_file(
|
|
145
173
|
errors=errors,
|
146
174
|
path=path,
|
147
175
|
data=data,
|
148
|
-
row_offset=
|
149
|
-
col_offset=col_offset,
|
176
|
+
row_offset=constant.lineno - 1,
|
177
|
+
col_offset=constant.col_offset - 1,
|
150
178
|
section=section,
|
151
179
|
config=config_examples if section == EXAMPLES_SECTION else config,
|
152
180
|
)
|
@@ -219,6 +247,8 @@ def main() -> int:
|
|
219
247
|
|
220
248
|
errors: list[dict[str, t.Any]] = []
|
221
249
|
for path in paths:
|
250
|
+
if not os.path.isfile(path):
|
251
|
+
continue
|
222
252
|
if path.endswith(".py"):
|
223
253
|
process_python_file(errors, path, yamllint_config, yamllint_config_examples)
|
224
254
|
if path.endswith((".yml", ".yaml")):
|
antsibull_nox/init.py
ADDED
@@ -0,0 +1,83 @@
|
|
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
|
+
"""Tool to create initial configuration."""
|
8
|
+
|
9
|
+
from pathlib import Path
|
10
|
+
|
11
|
+
from .collection.search import GALAXY_YML
|
12
|
+
from .config import CONFIG_FILENAME
|
13
|
+
from .lint_config import NOXFILE_PY
|
14
|
+
|
15
|
+
NOXFILE_CONTENT = r"""
|
16
|
+
# The following metadata allows Python runners and nox to install the required
|
17
|
+
# dependencies for running this Python script:
|
18
|
+
#
|
19
|
+
# /// script
|
20
|
+
# dependencies = ["nox>=2025.02.09", "antsibull-nox"]
|
21
|
+
# ///
|
22
|
+
|
23
|
+
import sys
|
24
|
+
|
25
|
+
import nox
|
26
|
+
|
27
|
+
|
28
|
+
# We try to import antsibull-nox, and if that doesn't work, provide a more useful
|
29
|
+
# error message to the user.
|
30
|
+
try:
|
31
|
+
import antsibull_nox
|
32
|
+
except ImportError:
|
33
|
+
print("You need to install antsibull-nox in the same Python environment as nox.")
|
34
|
+
sys.exit(1)
|
35
|
+
|
36
|
+
|
37
|
+
antsibull_nox.load_antsibull_nox_toml()
|
38
|
+
|
39
|
+
|
40
|
+
# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar.
|
41
|
+
# Requires nox >= 2025.02.09
|
42
|
+
if __name__ == "__main__":
|
43
|
+
nox.main()
|
44
|
+
"""
|
45
|
+
|
46
|
+
CONFIG_CONTENT = r"""
|
47
|
+
[sessions]
|
48
|
+
|
49
|
+
[sessions.lint]
|
50
|
+
# disable reformatting for now
|
51
|
+
run_isort = false
|
52
|
+
|
53
|
+
# disable reformatting for now
|
54
|
+
run_black = false
|
55
|
+
|
56
|
+
# Add more configuration settings here to adjust to your collection;
|
57
|
+
# see https://ansible.readthedocs.io/projects/antsibull-nox/config-file/#basic-linting-sessions
|
58
|
+
|
59
|
+
[sessions.docs_check]
|
60
|
+
# Add configuration settings here to adjust to your collection;
|
61
|
+
# see https://ansible.readthedocs.io/projects/antsibull-nox/config-file/#collection-documentation-check
|
62
|
+
"""
|
63
|
+
|
64
|
+
|
65
|
+
def create_initial_config(*, path: Path | None = None) -> None:
|
66
|
+
"""
|
67
|
+
Write noxfile.py and antsibull-nox.toml for a collection.
|
68
|
+
"""
|
69
|
+
if path is None:
|
70
|
+
path = Path()
|
71
|
+
|
72
|
+
galaxy_yml = path / GALAXY_YML
|
73
|
+
if not galaxy_yml.is_file():
|
74
|
+
raise ValueError(f"Cannot find {galaxy_yml}")
|
75
|
+
|
76
|
+
noxfile = path / NOXFILE_PY
|
77
|
+
config_file = path / CONFIG_FILENAME
|
78
|
+
for file in (noxfile, config_file):
|
79
|
+
if file.exists():
|
80
|
+
raise ValueError(f"{file} already exists")
|
81
|
+
|
82
|
+
noxfile.write_text(NOXFILE_CONTENT)
|
83
|
+
config_file.write_text(CONFIG_CONTENT)
|
@@ -20,19 +20,21 @@ from .config import (
|
|
20
20
|
DevelLikeBranch,
|
21
21
|
Sessions,
|
22
22
|
)
|
23
|
-
from .sessions import
|
24
|
-
|
23
|
+
from .sessions import add_matrix_generator
|
24
|
+
from .sessions.ansible_lint import add_ansible_lint
|
25
|
+
from .sessions.ansible_test import (
|
25
26
|
add_all_ansible_test_sanity_test_sessions,
|
26
27
|
add_all_ansible_test_unit_test_sessions,
|
27
|
-
add_ansible_lint,
|
28
28
|
add_ansible_test_integration_sessions_default_container,
|
29
|
-
|
30
|
-
|
29
|
+
)
|
30
|
+
from .sessions.build_import_check import add_build_import_check
|
31
|
+
from .sessions.docs_check import add_docs_check
|
32
|
+
from .sessions.extra_checks import (
|
33
|
+
ActionGroup,
|
31
34
|
add_extra_checks,
|
32
|
-
add_license_check,
|
33
|
-
add_lint_sessions,
|
34
|
-
add_matrix_generator,
|
35
35
|
)
|
36
|
+
from .sessions.license_check import add_license_check
|
37
|
+
from .sessions.lint import add_lint_sessions
|
36
38
|
from .utils import Version
|
37
39
|
|
38
40
|
|
@@ -44,6 +46,18 @@ def _interpret_config(config: Config) -> None:
|
|
44
46
|
for name, source in config.collection_sources.items()
|
45
47
|
}
|
46
48
|
)
|
49
|
+
if config.collection_sources_per_ansible:
|
50
|
+
for (
|
51
|
+
ansible_core_version,
|
52
|
+
collection_sources,
|
53
|
+
) in config.collection_sources_per_ansible.items():
|
54
|
+
setup_collection_sources(
|
55
|
+
{
|
56
|
+
name: CollectionSource(name=name, source=source.source)
|
57
|
+
for name, source in collection_sources.items()
|
58
|
+
},
|
59
|
+
ansible_core_version=ansible_core_version,
|
60
|
+
)
|
47
61
|
|
48
62
|
|
49
63
|
def _convert_action_groups(
|
@@ -116,6 +130,7 @@ def _add_sessions(sessions: Sessions) -> None:
|
|
116
130
|
mypy_package=sessions.lint.mypy_package,
|
117
131
|
mypy_ansible_core_package=sessions.lint.mypy_ansible_core_package,
|
118
132
|
mypy_extra_deps=sessions.lint.mypy_extra_deps,
|
133
|
+
run_antsibullnox_config_lint=sessions.lint.run_antsibullnox_config_lint,
|
119
134
|
)
|
120
135
|
if sessions.docs_check:
|
121
136
|
add_docs_check(
|
@@ -172,6 +187,9 @@ def _add_sessions(sessions: Sessions) -> None:
|
|
172
187
|
run_galaxy_importer=sessions.build_import_check.run_galaxy_importer,
|
173
188
|
galaxy_importer_package=sessions.build_import_check.galaxy_importer_package,
|
174
189
|
galaxy_importer_config_path=sessions.build_import_check.galaxy_importer_config_path,
|
190
|
+
galaxy_importer_always_show_logs=(
|
191
|
+
sessions.build_import_check.galaxy_importer_always_show_logs
|
192
|
+
),
|
175
193
|
)
|
176
194
|
if sessions.ansible_test_sanity:
|
177
195
|
add_all_ansible_test_sanity_test_sessions(
|
@@ -186,6 +204,9 @@ def _add_sessions(sessions: Sessions) -> None:
|
|
186
204
|
except_versions=_convert_except_versions(
|
187
205
|
sessions.ansible_test_sanity.except_versions
|
188
206
|
),
|
207
|
+
skip_tests=sessions.ansible_test_sanity.skip_tests,
|
208
|
+
allow_disabled=sessions.ansible_test_sanity.allow_disabled,
|
209
|
+
enable_optional_errors=sessions.ansible_test_sanity.enable_optional_errors,
|
189
210
|
)
|
190
211
|
if sessions.ansible_test_units:
|
191
212
|
add_all_ansible_test_unit_test_sessions(
|