Skip to content

Commit c50eb8d

Browse files
committed
Compatible difference formatter (not tested)
1 parent 6b9c356 commit c50eb8d

File tree

3 files changed

+79
-15
lines changed

3 files changed

+79
-15
lines changed

src/gardenlinux/features/reproducibility/comparator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,6 @@ def generate(
262262
return {}, False
263263

264264
with self._unpack(a) as unpacked_a, self._unpack(b) as unpacked_b:
265-
return self._compare_directories(unpacked_a, unpacked_b)
265+
return self._compare_directories(Path(unpacked_a), Path(unpacked_b))
266266
else:
267267
return self._compare_directories(a, b)

src/gardenlinux/features/reproducibility/diff_parser.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import re
1010
from os import PathLike
1111
from pathlib import Path
12-
from typing import Dict, Optional
12+
import json
13+
from typing import Dict, Optional, Any
1314

1415
import networkx as nx
1516

@@ -32,7 +33,7 @@ class DiffParser(object):
3233

3334
_remove_arch = re.compile("(-arm64|-amd64)$")
3435
_GARDENLINUX_ROOT: str = os.getenv("GL_ROOT_DIR", ".")
35-
_SUFFIX = "-diff.txt"
36+
_SUFFIX = "-diff.json"
3637

3738
def __init__(
3839
self,
@@ -63,6 +64,33 @@ def __init__(
6364
self.missing_flavors: set[str] = set()
6465
self.unexpected_falvors: set[str] = set()
6566

67+
def _map_files_to_flavors(
68+
self,
69+
files: Dict[str, Any],
70+
flavor: str,
71+
dictionary: Dict[str, set[str]],
72+
prefix: str = "",
73+
) -> None:
74+
"""
75+
Map hierarchical files to a comma-separated representation and bundle the flavors having issues with the file
76+
77+
:param files: Dictionary of the archives and files
78+
:param flavor: The flavor of the current files
79+
:param dictionary: The dictionary in which the files have to be inserted
80+
:param prefix: Archive prefix path of the file (comma-separated)
81+
82+
:since: 1.0.0
83+
"""
84+
for file in files:
85+
if files[file] == {}:
86+
if prefix + file not in dictionary:
87+
dictionary[prefix + file] = set()
88+
dictionary[prefix + file].add(flavor)
89+
else:
90+
self._map_files_to_flavors(
91+
files[file], flavor, dictionary, prefix=prefix + file + ","
92+
)
93+
6694
def sort_features(self, graph: nx.DiGraph) -> list[str]:
6795
"""
6896
Forward sorting implemented in the parser
@@ -109,24 +137,23 @@ def parse(
109137

110138
flavor = flavor.rstrip(self._SUFFIX)
111139
self.all_flavors.add(flavor)
112-
if content == "":
140+
if content == "{}":
113141
self.reproducible_flavors.add(flavor)
114142
elif content == "whitelist\n":
115143
self.reproducible_flavors.add(flavor)
116144
self.passed_by_whitelist.add(flavor)
117145
else:
118-
non_reproducible_flavors[flavor] = content.split("\n")[:-1]
146+
non_reproducible_flavors[flavor] = json.loads(content)
119147

120148
self.missing_flavors = self.expected_falvors - self.all_flavors
121149
self.unexpected_falvors = self.all_flavors - self.expected_falvors
122150

123151
# Map files to flavors
124152
affected_flavors: Dict[str, set[str]] = {} # {file: {flavors...}}
125153
for flavor in non_reproducible_flavors:
126-
for file in non_reproducible_flavors[flavor]:
127-
if file not in affected_flavors:
128-
affected_flavors[file] = set()
129-
affected_flavors[file].add(flavor)
154+
self._map_files_to_flavors(
155+
non_reproducible_flavors[flavor], flavor, affected_flavors
156+
)
130157

131158
# Merge files affected_flavors by the same flavors by mapping flavor sets to files
132159
self._bundled: Dict[frozenset[str], set[str]] = {} # {{flavors...}: {files...}}

src/gardenlinux/features/reproducibility/markdown_formatter.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from os import PathLike
99
from pathlib import Path
1010
from string import Template
11-
from typing import Collection, Optional
11+
from typing import Collection, Optional, Any
1212

1313
import networkx as nx
1414
from attr import dataclass
@@ -116,7 +116,7 @@ def _treeStr(
116116
# Remove last linebreak as the last line can contain spaces
117117
return s.rstrip(), found
118118

119-
def _dropdown(self, items: Collection[str]) -> str:
119+
def _dropdown(self, items: Collection[str], sort: bool = True) -> str:
120120
"""
121121
Converts the items into a markdown dropwon list if the length is 10 or more
122122
@@ -127,16 +127,53 @@ def _dropdown(self, items: Collection[str]) -> str:
127127
"""
128128

129129
if len(items) <= self._DROPDOWN_THRESHOLD:
130-
return "<br>".join([f"`{item}`" for item in sorted(items)])
130+
return "<br>".join(
131+
[f"`{item}`" for item in (sorted(items) if sort else items)]
132+
)
131133
else:
132-
for first in sorted(items):
134+
for first in sorted(items) if sort else items:
133135
return (
134136
f"<details><summary>{first}...</summary>"
135-
+ "<br>".join([f"`{item}`" for item in sorted(items)])
137+
+ "<br>".join(
138+
[f"`{item}`" for item in (sorted(items) if sort else items)]
139+
)
136140
+ "</details>"
137141
)
138142
return ""
139143

144+
def _format_files(self, files: Collection[str]) -> list[str]:
145+
"""
146+
Rebuild a dictionary from the comma-separated hierarchy and return a representation with indentation
147+
148+
:param files: List of files
149+
150+
:return: list(str) Sorted and formatted files
151+
:since: 1.0.0
152+
"""
153+
154+
dictionary: dict[str, Any] = {}
155+
156+
def fill_dictionary(d: dict[str, Any], parts: list[str]) -> None:
157+
if parts[0] not in d:
158+
d[parts[0]] = {}
159+
if len(parts) > 1:
160+
fill_dictionary(d[parts[0]], parts[1:])
161+
162+
for file in files:
163+
fill_dictionary(dictionary, file.split(","))
164+
165+
def dict_to_strs(d: dict[str, Any], prefix: str = "") -> list[str]:
166+
lines = []
167+
for part in sorted(d):
168+
if d[part] == {}:
169+
lines.append(prefix + part)
170+
else:
171+
lines.append(prefix + part + ":")
172+
lines = lines + dict_to_strs(d[part], prefix=prefix + " ")
173+
return lines
174+
175+
return dict_to_strs(dictionary)
176+
140177
def _format_nighlty_stats(self) -> str:
141178
"""
142179
Parses nightly_stats file and formats a human readable result
@@ -283,7 +320,7 @@ def sorting_function(files: frozenset[str]) -> tuple[int, str]:
283320
for files in sorted(trees, key=sorting_function):
284321
flavors, tree = trees[files]
285322
row = "|"
286-
row += self._dropdown(files)
323+
row += self._dropdown(self._format_files(files), sort=False)
287324
row += "|"
288325
row += f"**{round(100 * (len(flavors) / len(self._diff_parser.expected_falvors)), 1)}%** affected<br>"
289326
row += self._dropdown(flavors)

0 commit comments

Comments
 (0)