Skip to content

Commit 7dbe148

Browse files
committed
Restructure GL features parsing and module structure
Signed-off-by: Tobias Wolf <[email protected]>
1 parent c221788 commit 7dbe148

File tree

7 files changed

+517
-190
lines changed

7 files changed

+517
-190
lines changed

poetry.lock

Lines changed: 77 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[tool.poetry]
2-
name = "python_gardenlinux_lib"
2+
name = "gardenlinux"
33
version = "0.6.0"
44
description = "Contains tools to work with the features directory of gardenlinux, for example deducting dependencies from feature sets or validating cnames"
55
authors = ["Garden Linux Maintainers <[email protected]>"]
66
license = "Apache-2.0"
77
readme = "README.md"
8-
packages = [{include = "python_gardenlinux_lib", from="src"}]
8+
packages = [{include = "gardenlinux", from="src"}, {include = "python_gardenlinux_lib", from="src"}]
99

1010
[tool.poetry.dependencies]
1111
python = "^3.10"
@@ -21,18 +21,17 @@ python-dotenv = "^1.0.1"
2121
cryptography = "^44.0.0"
2222
boto3 = "*"
2323

24-
2524
[tool.poetry.group.dev.dependencies]
2625
black = "^24.8.0"
2726

2827
[tool.poetry.scripts]
29-
gl-cname = "src.python_gardenlinux_lib.cname:main"
30-
flavors-parse = "python_gardenlinux_lib.flavors.__main__:main"
28+
gl-cname = "gardenlinux.features.cname_main:main"
29+
gl-features-parse = "gardenlinux.features.__main__:main"
30+
gl-flavors-parse = "gardenlinux.flavors.__main__:main"
31+
flavors-parse = "gardenlinux.flavors.__main__:main"
3132

3233
[tool.pytest.ini_options]
33-
pythonpath = [
34-
"src"
35-
]
34+
pythonpath = ["src"]
3635
norecursedirs = "test-data"
3736

3837
[build-system]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .parser import Parser
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
3+
from .parser import Parser
4+
5+
from functools import reduce
6+
from os.path import basename, dirname
7+
8+
import argparse
9+
import re
10+
import sys
11+
12+
13+
_ARGS_TYPE_ALLOWED = [
14+
"cname",
15+
"cname_base",
16+
"features",
17+
"platforms",
18+
"flags",
19+
"elements",
20+
"arch",
21+
"version",
22+
"graph"
23+
]
24+
25+
26+
def main():
27+
parser = argparse.ArgumentParser()
28+
29+
parser.add_argument("--arch", dest = "arch")
30+
parser.add_argument("--feature-dir", default = "features")
31+
parser.add_argument("--features", type = lambda arg: set([f for f in arg.split(",") if f]))
32+
parser.add_argument("--ignore", dest = "ignore", type = lambda arg: set([f for f in arg.split(",") if f]), default = set())
33+
parser.add_argument("--cname")
34+
parser.add_argument("--default-arch")
35+
parser.add_argument("--default-version")
36+
parser.add_argument("--version", dest="version")
37+
parser.add_argument("type", nargs="?", choices = _ARGS_TYPE_ALLOWED, default = "cname")
38+
39+
args = parser.parse_args()
40+
41+
assert bool(args.features) or bool(args.cname), "Please provide either `--features` or `--cname` argument"
42+
43+
arch = None
44+
cname_base = None
45+
commit_id = None
46+
version = None
47+
48+
if args.cname:
49+
re_match = re.match(
50+
"([a-zA-Z0-9]+(-[a-zA-Z0-9\\_\\-]*?)?)(-([a-z0-9]+)(-([a-z0-9.]+)-([a-z0-9]+))*)?$",
51+
args.cname
52+
)
53+
54+
assert re_match, f"Not a valid GardenLinux canonical name {args.cname}"
55+
56+
if re_match.lastindex == 1:
57+
data_splitted = re_match[1].split("-", 1)
58+
59+
if len(data_splitted) > 1:
60+
arch = data_splitted[1]
61+
62+
cname_base = data_splitted[0]
63+
else:
64+
arch = re_match[4]
65+
cname_base = re_match[1]
66+
commit_id = re_match[7]
67+
version = re_match[6]
68+
69+
input_features = Parser.get_cname_as_feature_set(cname_base)
70+
else:
71+
input_features = args.features
72+
73+
if args.arch is not None:
74+
arch = args.arch
75+
76+
if args.version is not None:
77+
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", args.version)
78+
assert re_match, f"Not a valid version {args.version}"
79+
80+
commit_id = re_match[3]
81+
version = re_match[1]
82+
83+
if arch is None or arch == "" and (args.type in ( "cname", "arch" )):
84+
assert args.default_arch, "Architecture could not be determined and no default architecture set"
85+
arch = args.default_arch
86+
87+
if not version and (args.type in ("cname", "version" )):
88+
assert args.default_version, "version not specified and no default version set"
89+
version = args.default_version
90+
91+
gardenlinux_root = dirname(args.feature_dir)
92+
feature_dir_name = basename(args.feature_dir)
93+
94+
if gardenlinux_root == "":
95+
gardenlinux_root = "."
96+
97+
if gardenlinux_root == "":
98+
gardenlinux_root = "."
99+
100+
additional_filter_func = lambda node: node not in args.ignore
101+
102+
if args.type == "arch":
103+
print(arch)
104+
elif args.type in ( "cname_base", "cname", "graph" ):
105+
graph = Parser(gardenlinux_root, feature_dir_name).filter(cname_base, additional_filter_func = additional_filter_func)
106+
107+
sorted_features = Parser.sort_reversed_graph_nodes(graph)
108+
109+
minimal_feature_set = get_minimal_feature_set(graph)
110+
111+
sorted_minimal_features = sort_subset(
112+
minimal_feature_set, sorted_features
113+
)
114+
115+
cname_base = get_cname_base(sorted_minimal_features)
116+
117+
if args.type == "cname_base":
118+
print(cname_base)
119+
elif args.type == "cname":
120+
cname = cname_base
121+
122+
if arch is not None:
123+
cname += f"-{arch}"
124+
125+
if commit_id is not None:
126+
cname += f"-{version}-{commit_id}"
127+
128+
print(cname)
129+
elif args.type == "graph":
130+
print(graph_as_mermaid_markup(cname_base, graph))
131+
elif args.type == "features":
132+
print(Parser(gardenlinux_root, feature_dir_name).filter_as_string(cname_base, additional_filter_func = additional_filter_func))
133+
elif args.type in ( "flags", "elements", "platforms" ):
134+
features_by_type = Parser(gardenlinux_root, feature_dir_name).filter_as_dict(cname_base, additional_filter_func = additional_filter_func)
135+
136+
if args.type == "platforms":
137+
print(",".join(features_by_type["platform"]))
138+
elif args.type == "elements":
139+
print(",".join(features_by_type["element"]))
140+
elif args.type == "flags":
141+
print(",".join(features_by_type["flag"]))
142+
elif args.type == "version":
143+
print(f"{version}-{commit_id}")
144+
145+
146+
def get_cname_base(sorted_features):
147+
return reduce(
148+
lambda a, b : a + ("-" if not b.startswith("_") else "") + b, sorted_features
149+
)
150+
151+
def get_minimal_feature_set(graph):
152+
return set([node for (node, degree) in graph.in_degree() if degree == 0])
153+
154+
def graph_as_mermaid_markup(cname_base, graph):
155+
"""
156+
Generates a mermaid.js representation of the graph.
157+
This is helpful to identify dependencies between features.
158+
159+
Syntax docs:
160+
https://mermaid.js.org/syntax/flowchart.html?id=flowcharts-basic-syntax
161+
"""
162+
markup = f"---\ntitle: Dependency Graph for Feature {cname_base}\n---\ngraph TD;\n"
163+
for u,v in graph.edges:
164+
markup += f" {u}-->{v};\n"
165+
return markup
166+
167+
def sort_subset(input_set, order_list):
168+
return [item for item in order_list if item in input_set]
169+
170+
171+
if __name__ == "__main__":
172+
main()
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env python3
22

3-
from .features import parse_features
4-
53
from functools import reduce
64
from os.path import basename, dirname
7-
85
import argparse
96
import re
107

8+
from .__main__ import get_cname_base, get_minimal_feature_set, sort_subset
9+
from .parser import Parser
10+
1111

1212
def main():
1313
parser = argparse.ArgumentParser()
@@ -24,12 +24,19 @@ def main():
2424
args.cname
2525
)
2626

27-
assert re_match, f"not a valid cname {args.cname}"
27+
assert re_match, f"Not a valid GardenLinux canonical name {args.cname}"
28+
29+
arch = None
30+
commit_id = None
31+
version = None
2832

2933
if re_match.lastindex == 1:
30-
cname_base, arch = re_match[1].split("-", 1)
31-
commit_id = None
32-
version = None
34+
data_splitted = re_match[1].split("-", 1)
35+
36+
if len(data_splitted) > 1:
37+
arch = data_splitted[1]
38+
39+
cname_base = data_splitted[0]
3340
else:
3441
arch = re_match[4]
3542
cname_base = re_match[1]
@@ -39,9 +46,11 @@ def main():
3946
if args.arch is not None:
4047
arch = args.arch
4148

49+
assert arch is None or arch == "", "Architecture could not be determined"
50+
4251
if args.version is not None:
43-
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", args.cname)
44-
assert re_match, f"not a valid version {args.version}"
52+
re_match = re.match("([a-z0-9.]+)(-([a-z0-9]+))?$", args.version)
53+
assert re_match, f"Not a valid version {args.version}"
4554

4655
commit_id = re_match[3]
4756
version = re_match[1]
@@ -52,34 +61,26 @@ def main():
5261
if gardenlinux_root == "":
5362
gardenlinux_root = "."
5463

55-
graph = parse_features.get_features_graph(
56-
cname_base, gardenlinux_root, feature_dir_name
57-
)
64+
graph = Parser(gardenlinux_root, feature_dir_name).filter(cname_base)
5865

59-
sorted_features = parse_features.sort_nodes(graph)
66+
sorted_features = Parser.sort_reversed_graph_nodes(graph)
6067

6168
minimal_feature_set = get_minimal_feature_set(graph)
6269

63-
sorted_minimal_features = parse_features.sort_set(
70+
sorted_minimal_features = sort_subset(
6471
minimal_feature_set, sorted_features
6572
)
6673

67-
cname_base = get_cname_base(sorted_minimal_features)
74+
cname = get_cname_base(sorted_minimal_features)
75+
76+
if arch is not None:
77+
cname += f"-{arch}"
6878

69-
cname = f"{cname_base}-{arch}"
7079
if commit_id is not None:
7180
cname += f"-{version}-{commit_id}"
7281

7382
print(cname)
7483

75-
def get_cname_base(sorted_features):
76-
return reduce(
77-
lambda a, b : a + ("-" if not b.startswith("_") else "") + b, sorted_features
78-
)
79-
80-
def get_minimal_feature_set(graph):
81-
return set([node for (node, degree) in graph.in_degree() if degree == 0])
82-
8384

8485
if __name__ == "__main__":
8586
main()

0 commit comments

Comments
 (0)