forked from conan-io/conan-center-index
-
Notifications
You must be signed in to change notification settings - Fork 0
/
conandata_yaml_linter.py
125 lines (110 loc) · 5.05 KB
/
conandata_yaml_linter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import argparse
from strictyaml import (
load,
Map,
Str,
YAMLValidationError,
MapPattern,
Optional,
Seq,
Enum,
Any,
)
from yaml_linting import file_path
CONANDATA_YAML_URL = "https://github.com/conan-io/conan-center-index/blob/master/docs/adding_packages/conandata_yml_format.md"
def main():
parser = argparse.ArgumentParser(
description="Validate Conan's 'conandata.yaml' file to ConanCenterIndex's requirements."
)
parser.add_argument(
"path",
nargs="?",
type=file_path,
help="file to validate.",
)
args = parser.parse_args()
patch_fields = Map(
{
"patch_file": Str(),
"patch_description": Str(),
"patch_type": Enum(
["official", "conan", "portability", "bugfix", "vulnerability"]
),
Optional("patch_source"): Str(),
# No longer required for v2 recipes with layouts
Optional("base_path"): Str(),
}
)
schema = Map(
{
"sources": MapPattern(Str(), Any(), minimum_keys=1),
Optional("patches"): MapPattern(Str(), Seq(Any()), minimum_keys=1),
}
)
with open(args.path, encoding="utf-8") as f:
content = f.read()
try:
parsed = load(content, schema)
except YAMLValidationError as error:
pretty_print_yaml_validate_error(args, error) # Error when "source" is missing or when "patches" has no versions
return
except BaseException as error:
pretty_print_yaml_validate_error(args, error) # YAML could not be parsed
return
if "patches" in parsed:
for version in parsed["patches"]:
patches = parsed["patches"][version]
if version not in parsed["sources"]:
print(
f"::warning file={args.path},line={patches.start_line},endline={patches.end_line},"
f"title=conandata.yml inconsistency"
f"::Patch(es) are listed for version `{version}`, but there is source for this version."
f" You should either remove `{version}` from the `patches` section, or add it to the"
f" `sources` section"
)
for i, patch in enumerate(patches):
# Individual report errors for each patch object
try:
parsed["patches"][version][i].revalidate(patch_fields)
except YAMLValidationError as error:
pretty_print_yaml_validate_warning(args, error) # Warning when patch fields are not followed
continue
# Make sure `patch_source` exists where it's encouraged
type = parsed["patches"][version][i]["patch_type"]
if (
type in ["official", "bugfix", "vulnerability"]
and not "patch_source" in patch
):
print(
f"::warning file={args.path},line={type.start_line},endline={type.end_line},"
f"title=conandata.yml schema warning"
f"::'patch_type' should have 'patch_source' as per {CONANDATA_YAML_URL}#patch_type"
" it is expected to have a source (e.g. a URL) to where it originates from to help with"
" reviewing and consumers to evaluate patches"
)
# v2 migrations suggestion
if "base_path" in parsed["patches"][version][i]:
base_path = parsed["patches"][version][i]["base_path"]
print(
f"::notice file={args.path},line={base_path.start_line},endline={base_path.end_line},"
f"title=conandata.yml v2 migration suggestion"
"::'base_path' should not be required once a recipe has been upgraded to take advantage of"
" layouts (see https://docs.conan.io/en/latest/reference/conanfile/tools/layout.html) and"
" the new helper (see https://docs.conan.io/en/latest/reference/conanfile/tools/files/patches.html#conan-tools-files-apply-conandata-patches)"
)
def pretty_print_yaml_validate_error(args, error):
snippet = error.context_mark.get_snippet().replace("\n", "%0A")
print(
f"::error file={args.path},line={error.context_mark.line},endline={error.problem_mark.line+1},"
f"title=conandata.yml schema error"
f"::Schema outlined in {CONANDATA_YAML_URL}#patches-fields is not followed.%0A%0A{error.problem} in %0A{snippet}%0A"
)
def pretty_print_yaml_validate_warning(args, error):
snippet = error.context_mark.get_snippet().replace("\n", "%0A")
print(
f"::warning file={args.path},line={error.context_mark.line},endline={error.problem_mark.line+1},"
f"title=conandata.yml schema warning"
f"::Schema outlined in {CONANDATA_YAML_URL}#patches-fields is not followed.%0A%0A{error.problem} in %0A{snippet}%0A"
)
if __name__ == "__main__":
main()