feat: Move test formatter from .github/workflows/format_diff.py to python-gardenlinux-lib#294
feat: Move test formatter from .github/workflows/format_diff.py to python-gardenlinux-lib#294
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #294 +/- ##
==========================================
+ Coverage 91.51% 92.06% +0.54%
==========================================
Files 42 46 +4
Lines 2158 2457 +299
==========================================
+ Hits 1975 2262 +287
- Misses 183 195 +12 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| commit: str | ||
|
|
||
|
|
||
| class Formatter(object): |
There was a problem hiding this comment.
File name and class name should be the same. Furthermore the class name should indicate what it formats or into which format. I guess MarkdownFormatter might be most suitable as we do not intend to support multiple output formats anyway.
To not loose what it formats a package called gardenlinux.features.reproducibility might be suitable
There was a problem hiding this comment.
This file mixes output and parsing logic. Separating the functionality might allow reusability.
| diff_dir = pathlib.Path(gardenlinux_root).joinpath(diff_dir) | ||
|
|
||
| self._all = set() | ||
| self._flavors = os.listdir(diff_dir) |
There was a problem hiding this comment.
This variable indicates a different content then what it contains. Based on GardenLinux naming convention a flavor is a specific set of features as a string. E.g. gcp-gardener_prod_trustedboot resulting in a GardenLinux canonical name if appended with the CPU architecture, version and commit.
This variable contains files produced during one build if I understand it correctly.
|
|
||
| remove_arch = re.compile("(-arm64|-amd64)$") | ||
|
|
||
| def __init__( |
There was a problem hiding this comment.
The constructor does way too much. Usually a constructor should only set-up an instance. If needed it might call class methods to increase readability.
|
|
||
| self._parser = Parser(gardenlinux_root, feature_dir_name, logger) | ||
| if gardenlinux_root is None: | ||
| gardenlinux_root = self._parser._GARDENLINUX_ROOT |
There was a problem hiding this comment.
Accessing private members are discouraged. If needed Parser can be extended to provide the directory information used at an instance of it.
| :since: 1.0.0 | ||
| """ | ||
|
|
||
| if len(items) <= 10: |
There was a problem hiding this comment.
Magic numbers - please use a constant for the limit :)
| found.update(fnd) | ||
| s += " " + st.replace("\n", "\n ") + "\n" | ||
| # Remove last linebreak as the last line can contain spaces | ||
| return "\n".join(s.split("\n")[:-1]), found |
There was a problem hiding this comment.
Wouldn't rstrip() be more appropriate here?
| first = True | ||
| tree = None | ||
| for flavor in flavors: | ||
| if not flavor.startswith("bare-"): |
There was a problem hiding this comment.
Hidden magic is always a request for hard to debug issues later on. Please add # @TODO comments where applicable.
| """ | ||
|
|
||
| with open( | ||
| self._parser._feature_base_dir.joinpath(f"{node}/info.yaml"), "r" |
There was a problem hiding this comment.
You already accessed the private root directory variable from Parser. Best practice (if not otherwise possible) would be to store it in this instance in __init__() for later reuse.
There was a problem hiding this comment.
I would expect gl-diff to at least additionally produce the diff files formatted here as well. Based on if that would be feasible the file name should either indicate it's handling flavor build differential content or it's only parsing and providing output for it.
|
Thanks for your detailed feedback. I have heavily restricted the formatter and implemented all your comments. It's still a massive PR, as it also contains the difference generator now. So take your time! I was not able to find a python package or better way to unpack .oci containers, so I've implemented unpacking logic myself. It works so far. |
NotTheEvilOne
left a comment
There was a problem hiding this comment.
In general this is a nice feature. I'm not sure if gl-diff is the most suitable command line tool name for it. As you compare OCI images or tar files based on features you may want to name it something similar to gl-feature-fs-diff?
| print(result, end="") | ||
|
|
||
| if files != []: | ||
| exit(64) |
There was a problem hiding this comment.
What means "magic number" 64? Please use a constant :)
|
|
||
| _default_whitelist: list[str] = [] | ||
|
|
||
| _nightly_whitelist = [ |
There was a problem hiding this comment.
Please move this list to an external JSON file. You may want to use importlib.resources.read_text() :)
| self._parser = Parser(gardenlinux_root, feature_dir_name, logger) | ||
| self._feature_dir_name = Path(self._gardenlinux_root).joinpath(feature_dir_name) | ||
|
|
||
| self.all: set[str] = set() |
There was a problem hiding this comment.
Could you please rename or document these variables? self.all is unfortunately not really self explanatory.
| self, | ||
| ) -> Dict[frozenset[str], tuple[frozenset[str], nx.DiGraph]]: | ||
| """ | ||
| Intersects all features of the affected flavors and removes all features from unaffected flavors to identify features causing the issue |
There was a problem hiding this comment.
Please ensure you can't use Parser.filter() with additional_filter_func to get the information you want.
| self.missing_flavors: set[str] = set() | ||
| self.unexpected_falvors: set[str] = set() | ||
|
|
||
| def read_feature_info(self, feature: str) -> Any: |
There was a problem hiding this comment.
I think this method is unused. Additionally you can access this information utilizing Parser.graph.
| for flavor in flavors: | ||
| # Ignore bare flavors, as they may not be affected due to removing the file and could therefore disrupt the analysis | ||
| if not flavor.startswith("bare-"): | ||
| t = self._parser.filter(self._remove_arch.sub("", flavor)) |
There was a problem hiding this comment.
nit: t means filtered tree?
| :since: 1.0.0 | ||
| """ | ||
|
|
||
| info = self._diff_parser.read_feature_info(node) |
There was a problem hiding this comment.
This function can be replaced by Parser.filter_as_dict(). A prefix of a single number seems odd :(
| Nightly(*n.split(",")) for n in f.read().rstrip().split("\n") | ||
| ) | ||
| if nightly_a.run_number != "": | ||
| result += f"\n\nComparison of nightly **[#{nightly_a.run_number}](https://github.com/gardenlinux/gardenlinux/actions/runs/{nightly_a.id})** \ |
There was a problem hiding this comment.
This and the following URLs should be placed in a constant for future change. Maybe you can use a template for the generic structure?
| "✅" | ||
| if len(self._diff_parser.expected_falvors) | ||
| == len(self._diff_parser.successful) | ||
| else ("⚠️" if successrate >= 50.0 else "❌") |
There was a problem hiding this comment.
The threshold should be a constant :)
What this PR does / why we need it:
workflowsdirectory, to use features of the library and to be able to test the output with simple unit tests