gha-utils 4.14.0__py3-none-any.whl → 4.14.2__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 gha-utils might be problematic. Click here for more details.
- gha_utils/__init__.py +1 -1
- gha_utils/cli.py +21 -12
- gha_utils/metadata.py +1 -1
- gha_utils/test_plan.py +128 -80
- {gha_utils-4.14.0.dist-info → gha_utils-4.14.2.dist-info}/METADATA +6 -3
- gha_utils-4.14.2.dist-info/RECORD +14 -0
- gha_utils-4.14.0.dist-info/RECORD +0 -14
- {gha_utils-4.14.0.dist-info → gha_utils-4.14.2.dist-info}/WHEEL +0 -0
- {gha_utils-4.14.0.dist-info → gha_utils-4.14.2.dist-info}/entry_points.txt +0 -0
- {gha_utils-4.14.0.dist-info → gha_utils-4.14.2.dist-info}/top_level.txt +0 -0
gha_utils/__init__.py
CHANGED
gha_utils/cli.py
CHANGED
|
@@ -27,7 +27,7 @@ import click
|
|
|
27
27
|
from click_extra import (
|
|
28
28
|
Choice,
|
|
29
29
|
Context,
|
|
30
|
-
|
|
30
|
+
FloatRange,
|
|
31
31
|
argument,
|
|
32
32
|
echo,
|
|
33
33
|
extra_group,
|
|
@@ -279,29 +279,38 @@ def mailmap_sync(ctx, source, create_if_missing, destination_mailmap):
|
|
|
279
279
|
# `file_path` type.
|
|
280
280
|
type=click.Path(exists=True, executable=True, resolve_path=True),
|
|
281
281
|
required=True,
|
|
282
|
-
|
|
282
|
+
metavar="FILE_PATH",
|
|
283
|
+
help="Path to the binary file to test.",
|
|
283
284
|
)
|
|
284
285
|
@option(
|
|
285
286
|
"--plan",
|
|
286
287
|
type=file_path(exists=True, readable=True, resolve_path=True),
|
|
287
|
-
|
|
288
|
+
metavar="FILE_PATH",
|
|
289
|
+
help="Path to the test plan file in YAML. If not provided, a default test "
|
|
290
|
+
"plan will be executed.",
|
|
288
291
|
)
|
|
289
292
|
@option(
|
|
290
293
|
"-t",
|
|
291
294
|
"--timeout",
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
# Timeout passed to subprocess.run() is a float that is silently clamped to
|
|
296
|
+
# 0.0 is negative values are provided, so we mimic this behavior here:
|
|
297
|
+
# https://github.com/python/cpython/blob/5740b95076b57feb6293cda4f5504f706a7d622d/Lib/subprocess.py#L1596-L1597
|
|
298
|
+
type=FloatRange(min=0, clamp=True),
|
|
299
|
+
metavar="SECONDS",
|
|
300
|
+
help="Set the default timeout for each CLI call, if not specified in the "
|
|
301
|
+
"test plan.",
|
|
295
302
|
)
|
|
296
|
-
def test_plan(binary, plan, timeout):
|
|
303
|
+
def test_plan(binary: Path, plan: Path | None, timeout: float | None) -> None:
|
|
297
304
|
# Load test plan from workflow input, or use a default one.
|
|
298
305
|
if plan:
|
|
299
|
-
logging.
|
|
306
|
+
logging.info(f"Read test plan from {plan}")
|
|
300
307
|
test_plan = parse_test_plan(plan)
|
|
301
308
|
else:
|
|
302
|
-
logging.warning(
|
|
303
|
-
test_plan = DEFAULT_TEST_PLAN
|
|
309
|
+
logging.warning("No test plan provided: use default test plan.")
|
|
310
|
+
test_plan = DEFAULT_TEST_PLAN # type: ignore[assignment]
|
|
311
|
+
logging.debug(f"Test plan: {test_plan}")
|
|
304
312
|
|
|
305
313
|
for index, test_case in enumerate(test_plan):
|
|
306
|
-
logging.info(f"Run test #{index}")
|
|
307
|
-
|
|
314
|
+
logging.info(f"Run test #{index + 1}")
|
|
315
|
+
logging.debug(f"Test case parameters: {test_case}")
|
|
316
|
+
test_case.check_cli_test(binary, default_timeout=timeout)
|
gha_utils/metadata.py
CHANGED
gha_utils/test_plan.py
CHANGED
|
@@ -16,10 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
import logging
|
|
19
20
|
import re
|
|
21
|
+
import shlex
|
|
22
|
+
import sys
|
|
20
23
|
from dataclasses import asdict, dataclass, field
|
|
21
24
|
from pathlib import Path
|
|
22
|
-
from subprocess import run
|
|
25
|
+
from subprocess import TimeoutExpired, run
|
|
23
26
|
from typing import Generator, Sequence
|
|
24
27
|
|
|
25
28
|
import yaml
|
|
@@ -33,131 +36,171 @@ class TestCase:
|
|
|
33
36
|
cli_parameters: tuple[str, ...] | str = field(default_factory=tuple)
|
|
34
37
|
"""Parameters, arguments and options to pass to the CLI."""
|
|
35
38
|
|
|
39
|
+
timeout: float | str | None = None
|
|
36
40
|
exit_code: int | str | None = None
|
|
37
41
|
strip_ansi: bool = False
|
|
38
42
|
output_contains: tuple[str, ...] | str = field(default_factory=tuple)
|
|
39
43
|
stdout_contains: tuple[str, ...] | str = field(default_factory=tuple)
|
|
40
44
|
stderr_contains: tuple[str, ...] | str = field(default_factory=tuple)
|
|
41
|
-
output_regex_matches: tuple[str, ...] | str = field(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
output_regex_matches: tuple[re.Pattern | str, ...] | str = field(
|
|
46
|
+
default_factory=tuple
|
|
47
|
+
)
|
|
48
|
+
stdout_regex_matches: tuple[re.Pattern | str, ...] | str = field(
|
|
49
|
+
default_factory=tuple
|
|
50
|
+
)
|
|
51
|
+
stderr_regex_matches: tuple[re.Pattern | str, ...] | str = field(
|
|
52
|
+
default_factory=tuple
|
|
53
|
+
)
|
|
54
|
+
output_regex_fullmatch: re.Pattern | str | None = None
|
|
55
|
+
stdout_regex_fullmatch: re.Pattern | str | None = None
|
|
56
|
+
stderr_regex_fullmatch: re.Pattern | str | None = None
|
|
47
57
|
|
|
48
58
|
def __post_init__(self) -> None:
|
|
49
59
|
"""Normalize all fields."""
|
|
50
60
|
for field_id, field_data in asdict(self).items():
|
|
51
|
-
# Validates and normalize
|
|
61
|
+
# Validates and normalize integer properties.
|
|
52
62
|
if field_id == "exit_code":
|
|
53
63
|
if isinstance(field_data, str):
|
|
54
64
|
field_data = int(field_data)
|
|
55
65
|
elif field_data is not None and not isinstance(field_data, int):
|
|
56
66
|
raise ValueError(f"exit_code is not an integer: {field_data}")
|
|
57
67
|
|
|
68
|
+
# Validates and normalize float properties.
|
|
69
|
+
elif field_id == "timeout":
|
|
70
|
+
if isinstance(field_data, str):
|
|
71
|
+
field_data = float(field_data)
|
|
72
|
+
elif field_data is not None and not isinstance(field_data, float):
|
|
73
|
+
raise ValueError(f"timeout is not a float: {field_data}")
|
|
74
|
+
# Timeout can only be unset or positive.
|
|
75
|
+
if field_data and field_data < 0:
|
|
76
|
+
raise ValueError(f"timeout is negative: {field_data}")
|
|
77
|
+
|
|
78
|
+
# Validates and normalize boolean properties.
|
|
58
79
|
elif field_id == "strip_ansi":
|
|
59
80
|
if not isinstance(field_data, bool):
|
|
60
81
|
raise ValueError(f"strip_ansi is not a boolean: {field_data}")
|
|
61
82
|
|
|
62
|
-
# Validates and normalize regex fullmatch fields.
|
|
63
|
-
elif field_id.endswith("_fullmatch"):
|
|
64
|
-
if field_data:
|
|
65
|
-
if not isinstance(field_data, str):
|
|
66
|
-
raise ValueError(f"{field_id} is not a string: {field_data}")
|
|
67
|
-
# Normalize empty strings to None.
|
|
68
|
-
else:
|
|
69
|
-
field_data = None
|
|
70
|
-
|
|
71
83
|
# Validates and normalize tuple of strings.
|
|
72
84
|
else:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
if field_data:
|
|
86
|
+
# Wraps single string and other types into a tuple.
|
|
87
|
+
if isinstance(field_data, str) or not isinstance(
|
|
88
|
+
field_data, Sequence
|
|
89
|
+
):
|
|
90
|
+
# CLI parameters needs to be split on Unix-like systems.
|
|
91
|
+
# XXX If we need the same for Windows, have a look at:
|
|
92
|
+
# https://github.com/maxpat78/w32lex
|
|
93
|
+
if field_id == "cli_parameters" and sys.platform != "win32":
|
|
94
|
+
field_data = tuple(shlex.split(field_data))
|
|
95
|
+
else:
|
|
96
|
+
field_data = (field_data,)
|
|
97
|
+
|
|
98
|
+
for item in field_data:
|
|
99
|
+
if not isinstance(item, str):
|
|
100
|
+
raise ValueError(f"Invalid string in {field_id}: {item}")
|
|
101
|
+
# Ignore blank value.
|
|
102
|
+
field_data = tuple(i for i in field_data if i.strip())
|
|
86
103
|
|
|
87
|
-
# Validates
|
|
88
|
-
if
|
|
104
|
+
# Validates fields containing one or more regexes.
|
|
105
|
+
if "_regex_" in field_id and field_data:
|
|
106
|
+
# Compile all regexes.
|
|
107
|
+
valid_regexes = []
|
|
89
108
|
for regex in flatten((field_data,)):
|
|
90
109
|
try:
|
|
91
|
-
|
|
110
|
+
# Let dots in regex match newlines.
|
|
111
|
+
valid_regexes.append(re.compile(regex, re.DOTALL))
|
|
92
112
|
except re.error as ex:
|
|
93
113
|
raise ValueError(
|
|
94
114
|
f"Invalid regex in {field_id}: {regex}"
|
|
95
115
|
) from ex
|
|
116
|
+
# Normalize single regex to a single element.
|
|
117
|
+
if field_id.endswith("_fullmatch"):
|
|
118
|
+
if valid_regexes:
|
|
119
|
+
field_data = valid_regexes.pop()
|
|
120
|
+
else:
|
|
121
|
+
field_data = None
|
|
122
|
+
else:
|
|
123
|
+
field_data = tuple(valid_regexes)
|
|
96
124
|
|
|
97
125
|
setattr(self, field_id, field_data)
|
|
98
126
|
|
|
99
|
-
def check_cli_test(self, binary: str | Path,
|
|
127
|
+
def check_cli_test(self, binary: str | Path, default_timeout: float | None):
|
|
100
128
|
"""Run a CLI command and check its output against the test case.
|
|
101
129
|
|
|
102
130
|
..todo::
|
|
103
131
|
Add support for environment variables.
|
|
104
132
|
|
|
105
|
-
..todo::
|
|
106
|
-
Add support for ANSI code stripping.
|
|
107
|
-
|
|
108
133
|
..todo::
|
|
109
134
|
Add support for proper mixed stdout/stderr stream as a single,
|
|
110
135
|
intertwined output.
|
|
111
136
|
"""
|
|
137
|
+
if self.timeout is None and default_timeout is not None:
|
|
138
|
+
logging.info(f"Set default test case timeout to {default_timeout} seconds")
|
|
139
|
+
self.timeout = default_timeout
|
|
140
|
+
|
|
112
141
|
clean_args = args_cleanup(binary, self.cli_parameters)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
try:
|
|
143
|
+
result = run(
|
|
144
|
+
clean_args,
|
|
145
|
+
capture_output=True,
|
|
146
|
+
timeout=self.timeout, # type: ignore[arg-type]
|
|
147
|
+
# XXX Do not force encoding to let CLIs figure out by
|
|
148
|
+
# themselves the contextual encoding to use. This avoid
|
|
149
|
+
# UnicodeDecodeError on output in Window's console which still
|
|
150
|
+
# defaults to legacy encoding (e.g. cp1252, cp932, etc...):
|
|
151
|
+
#
|
|
152
|
+
# Traceback (most recent call last):
|
|
153
|
+
# File "…\__main__.py", line 49, in <module>
|
|
154
|
+
# File "…\__main__.py", line 45, in main
|
|
155
|
+
# File "…\click\core.py", line 1157, in __call__
|
|
156
|
+
# File "…\click_extra\commands.py", line 347, in main
|
|
157
|
+
# File "…\click\core.py", line 1078, in main
|
|
158
|
+
# File "…\click_extra\commands.py", line 377, in invoke
|
|
159
|
+
# File "…\click\core.py", line 1688, in invoke
|
|
160
|
+
# File "…\click_extra\commands.py", line 377, in invoke
|
|
161
|
+
# File "…\click\core.py", line 1434, in invoke
|
|
162
|
+
# File "…\click\core.py", line 783, in invoke
|
|
163
|
+
# File "…\cloup\_context.py", line 47, in new_func
|
|
164
|
+
# File "…\mpm\cli.py", line 570, in managers
|
|
165
|
+
# File "…\mpm\output.py", line 187, in print_table
|
|
166
|
+
# File "…\click_extra\tabulate.py", line 97, in render_csv
|
|
167
|
+
# File "encodings\cp1252.py", line 19, in encode
|
|
168
|
+
# UnicodeEncodeError: 'charmap' codec can't encode character
|
|
169
|
+
# '\u2713' in position 128: character maps to <undefined>
|
|
170
|
+
#
|
|
171
|
+
# encoding="utf-8",
|
|
172
|
+
text=True,
|
|
173
|
+
)
|
|
174
|
+
except TimeoutExpired:
|
|
175
|
+
raise TimeoutError(
|
|
176
|
+
f"CLI timed out after {self.timeout} seconds: {' '.join(clean_args)}"
|
|
177
|
+
)
|
|
178
|
+
|
|
144
179
|
print_cli_run(clean_args, result)
|
|
145
180
|
|
|
146
181
|
for field_id, field_data in asdict(self).items():
|
|
147
|
-
if field_id == "
|
|
182
|
+
if field_id == "exit_code":
|
|
183
|
+
if field_data is not None:
|
|
184
|
+
logging.info(f"Test exit code, expecting: {field_data}")
|
|
185
|
+
if result.returncode != field_data:
|
|
186
|
+
raise AssertionError(
|
|
187
|
+
f"CLI exited with code {result.returncode}, "
|
|
188
|
+
f"expected {field_data}"
|
|
189
|
+
)
|
|
190
|
+
# The specific exit code matches, let's proceed to the next test.
|
|
148
191
|
continue
|
|
149
192
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
193
|
+
# Ignore non-output fields, and empty test cases.
|
|
194
|
+
elif not (
|
|
195
|
+
field_id.startswith(("output_", "stdout_", "stderr_")) and field_data
|
|
196
|
+
):
|
|
197
|
+
continue
|
|
156
198
|
|
|
199
|
+
# Prepare output and name for comparison.
|
|
157
200
|
output = ""
|
|
158
201
|
name = ""
|
|
159
202
|
if field_id.startswith("output_"):
|
|
160
|
-
raise NotImplementedError("
|
|
203
|
+
raise NotImplementedError("<stdout>/<stderr> output mix")
|
|
161
204
|
# output = result.output
|
|
162
205
|
# name = "output"
|
|
163
206
|
elif field_id.startswith("stdout_"):
|
|
@@ -168,10 +211,12 @@ class TestCase:
|
|
|
168
211
|
name = "<stderr>"
|
|
169
212
|
|
|
170
213
|
if self.strip_ansi:
|
|
214
|
+
logging.info(f"Strip ANSI escape sequences from CLI's {name}")
|
|
171
215
|
output = strip_ansi(output)
|
|
172
216
|
|
|
173
217
|
if field_id.endswith("_contains"):
|
|
174
218
|
for sub_string in field_data:
|
|
219
|
+
logging.info(f"Check if CLI's {name} contains: {sub_string!r}")
|
|
175
220
|
if sub_string not in output:
|
|
176
221
|
raise AssertionError(
|
|
177
222
|
f"CLI's {name} does not contain {sub_string!r}"
|
|
@@ -179,18 +224,21 @@ class TestCase:
|
|
|
179
224
|
|
|
180
225
|
elif field_id.endswith("_regex_matches"):
|
|
181
226
|
for regex in field_data:
|
|
182
|
-
if
|
|
227
|
+
logging.info(f"Check if CLI's {name} matches: {sub_string!r}")
|
|
228
|
+
if not regex.search(output):
|
|
183
229
|
raise AssertionError(
|
|
184
|
-
f"CLI's {name} does not match regex {regex
|
|
230
|
+
f"CLI's {name} does not match regex {regex}"
|
|
185
231
|
)
|
|
186
232
|
|
|
187
233
|
elif field_id.endswith("_regex_fullmatch"):
|
|
188
234
|
regex = field_data
|
|
189
|
-
if not
|
|
235
|
+
if not regex.fullmatch(output):
|
|
190
236
|
raise AssertionError(
|
|
191
|
-
f"CLI's {name} does not fully match regex {regex
|
|
237
|
+
f"CLI's {name} does not fully match regex {regex}"
|
|
192
238
|
)
|
|
193
239
|
|
|
240
|
+
logging.info("All tests passed for CLI.")
|
|
241
|
+
|
|
194
242
|
|
|
195
243
|
DEFAULT_TEST_PLAN = (
|
|
196
244
|
# Output the version of the CLI.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: gha-utils
|
|
3
|
-
Version: 4.14.
|
|
3
|
+
Version: 4.14.2
|
|
4
4
|
Summary: ⚙️ CLI helpers for GitHub Actions + reuseable workflows
|
|
5
5
|
Author-email: Kevin Deldycke <kevin@deldycke.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/kdeldycke/workflows
|
|
@@ -103,7 +103,6 @@ Thanks to `uv`, you can install and run `gha-utils` in one command, without poll
|
|
|
103
103
|
|
|
104
104
|
```shell-session
|
|
105
105
|
$ uvx gha-utils
|
|
106
|
-
Installed 45 packages in 45ms
|
|
107
106
|
Usage: gha-utils [OPTIONS] COMMAND [ARGS]...
|
|
108
107
|
|
|
109
108
|
Options:
|
|
@@ -118,8 +117,11 @@ Options:
|
|
|
118
117
|
utils/*.{toml,yaml,yml,json,ini,xml}]
|
|
119
118
|
--show-params Show all CLI parameters, their provenance, defaults
|
|
120
119
|
and value, then exit.
|
|
121
|
-
|
|
120
|
+
--verbosity LEVEL Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
|
|
122
121
|
[default: WARNING]
|
|
122
|
+
-v, --verbose Increase the default WARNING verbosity by one level
|
|
123
|
+
for each additional repetition of the option.
|
|
124
|
+
[default: 0]
|
|
123
125
|
--version Show the version and exit.
|
|
124
126
|
-h, --help Show this message and exit.
|
|
125
127
|
|
|
@@ -127,6 +129,7 @@ Commands:
|
|
|
127
129
|
changelog Maintain a Markdown-formatted changelog
|
|
128
130
|
mailmap-sync Update Git's .mailmap file with missing contributors
|
|
129
131
|
metadata Output project metadata
|
|
132
|
+
test-plan Run a test plan from a file against a binary
|
|
130
133
|
```
|
|
131
134
|
|
|
132
135
|
```shell-session
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
gha_utils/__init__.py,sha256=dR8lNlVY_u847eZ0CTxmll40WHAkfXzWXN26hheu824,866
|
|
2
|
+
gha_utils/__main__.py,sha256=Dck9BjpLXmIRS83k0mghAMcYVYiMiFLltQdfRuMSP_Q,1703
|
|
3
|
+
gha_utils/changelog.py,sha256=oahY88A9FRV14f1JSFKIiYrN_TS7Jo3QlljXqJbeuaE,5892
|
|
4
|
+
gha_utils/cli.py,sha256=Gq2pGpIOmvXtsexmg6nwN2ZuZ1lpHrJ5A43CeuDjK68,10985
|
|
5
|
+
gha_utils/mailmap.py,sha256=naUqJYJnE3fLTjju1nd6WMm7ODiSaI2SHuJxRtmaFWs,6269
|
|
6
|
+
gha_utils/matrix.py,sha256=_afJD0K-xZLNxwykVnUhD0Gj9cdO0Z43g3VHa-q_tkI,11941
|
|
7
|
+
gha_utils/metadata.py,sha256=Oumkmo4-O549hxlw8zaTWIPkITpUxUjpulj_MXn6fjA,48649
|
|
8
|
+
gha_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
gha_utils/test_plan.py,sha256=B6EiiGWVTrNS7jSIO9EE0kebccx10Fn0ZPapJTOOyGA,11928
|
|
10
|
+
gha_utils-4.14.2.dist-info/METADATA,sha256=uPoJ9lj1TvWEt1Izsipp-4R6bsQ-gwHjnqwILdpL0fQ,20514
|
|
11
|
+
gha_utils-4.14.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
12
|
+
gha_utils-4.14.2.dist-info/entry_points.txt,sha256=8bJOwQYf9ZqsLhBR6gUCzvwLNI9f8tiiBrJ3AR0EK4o,54
|
|
13
|
+
gha_utils-4.14.2.dist-info/top_level.txt,sha256=C94Blb61YkkyPBwCdM3J_JPDjWH0lnKa5nGZeZ5M6yE,10
|
|
14
|
+
gha_utils-4.14.2.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
gha_utils/__init__.py,sha256=XhzcFkFs63M5SvQuWYvvNyqSQ8Z5nylmJq0ga2xTJGo,866
|
|
2
|
-
gha_utils/__main__.py,sha256=Dck9BjpLXmIRS83k0mghAMcYVYiMiFLltQdfRuMSP_Q,1703
|
|
3
|
-
gha_utils/changelog.py,sha256=oahY88A9FRV14f1JSFKIiYrN_TS7Jo3QlljXqJbeuaE,5892
|
|
4
|
-
gha_utils/cli.py,sha256=J6cqO-LlVXmLq0Z5Mmpv34ySbvVzVPqU1-c7iHqqITA,10348
|
|
5
|
-
gha_utils/mailmap.py,sha256=naUqJYJnE3fLTjju1nd6WMm7ODiSaI2SHuJxRtmaFWs,6269
|
|
6
|
-
gha_utils/matrix.py,sha256=_afJD0K-xZLNxwykVnUhD0Gj9cdO0Z43g3VHa-q_tkI,11941
|
|
7
|
-
gha_utils/metadata.py,sha256=YbWPxNwWxcTMj67q6I4adFXgLF11YBv6urAzNopWYHE,48657
|
|
8
|
-
gha_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
gha_utils/test_plan.py,sha256=6Anw8Aa7rlIdJH4XdfZddWjh_Q2VK7Ehq4UJ0cHAt2c,9467
|
|
10
|
-
gha_utils-4.14.0.dist-info/METADATA,sha256=aLM8hgjGOfr2tLuparflwcPmYp21mKW8bKSp3BNMMc8,20288
|
|
11
|
-
gha_utils-4.14.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
12
|
-
gha_utils-4.14.0.dist-info/entry_points.txt,sha256=8bJOwQYf9ZqsLhBR6gUCzvwLNI9f8tiiBrJ3AR0EK4o,54
|
|
13
|
-
gha_utils-4.14.0.dist-info/top_level.txt,sha256=C94Blb61YkkyPBwCdM3J_JPDjWH0lnKa5nGZeZ5M6yE,10
|
|
14
|
-
gha_utils-4.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|