From ac058c9012902cad290aad3c52528bef47499980 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sat, 21 Oct 2023 16:27:05 +0200 Subject: [PATCH] feat(poly diff): print changes for projects and/or bricks (#128) * feat(poly diff): print affected projects, changed bricks as parseable output that is useful for running tests * bump version to 1.11.0 --- components/polylith/diff/collect.py | 24 ++++++- components/polylith/diff/report.py | 64 ++++++++--------- components/polylith/poetry/commands/diff.py | 68 ++++++++++++++----- .../poetry_polylith_plugin/pyproject.toml | 2 +- 4 files changed, 107 insertions(+), 51 deletions(-) diff --git a/components/polylith/diff/collect.py b/components/polylith/diff/collect.py index afb833c8..2a42416d 100644 --- a/components/polylith/diff/collect.py +++ b/components/polylith/diff/collect.py @@ -1,6 +1,6 @@ import subprocess from pathlib import Path -from typing import List, Union +from typing import List, Set, Union from polylith import repo, workspace @@ -59,3 +59,25 @@ def get_files(tag: str) -> List[Path]: ) return [Path(p) for p in res.stdout.decode("utf-8").split()] + + +def _affected(projects_data: List[dict], brick_type: str, bricks: List[str]) -> set: + res = { + p["path"].name: set(p.get(brick_type, [])).intersection(bricks) + for p in projects_data + } + + return {k for k, v in res.items() if v} + + +def get_projects_affected_by_changes( + projects_data: List[dict], + projects: List[str], + bases: List[str], + components: List[str], +) -> Set[str]: + a = _affected(projects_data, "components", components) + b = _affected(projects_data, "bases", bases) + c = set(projects) + + return {*a, *b, *c} diff --git a/components/polylith/diff/report.py b/components/polylith/diff/report.py index 59d68862..e4eef366 100644 --- a/components/polylith/diff/report.py +++ b/components/polylith/diff/report.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Set from polylith import info from polylith.reporting import theme @@ -22,14 +22,41 @@ def print_diff_details( console.print(table, overflow="ellipsis") -def print_detected_changes_in_projects(projects: List[str]) -> None: - if not projects: +def print_detected_changes(changes: List[str], markup: str, short: bool) -> None: + if not changes: return console = Console(theme=theme.poly_theme) - for project in sorted(projects): - console.print(f"[data]:gear: Changes found in [/][proj]{project}[/]") + if short: + console.print(",".join(changes)) + return + + for brick in changes: + console.print(f"[data]:gear: Changes found in [/][{markup}]{brick}[/]") + + +def print_detected_changes_in_bricks( + bases: List[str], components: List[str], short: bool +) -> None: + sorted_bases = sorted(bases) + sorted_components = sorted(components) + + if short: + print_detected_changes(sorted_components + sorted_bases, "data", short) + else: + print_detected_changes(sorted_components, "comp", short) + print_detected_changes(sorted_bases, "base", short) + + +def print_detected_changes_in_projects(projects: List[str], short: bool) -> None: + print_detected_changes(projects, "proj", short) + + +def print_projects_affected_by_changes(projects: Set[str], short: bool) -> None: + sorted_projects = sorted(list(projects)) + + print_detected_changes(sorted_projects, "proj", short) def print_diff_summary(tag: str, bases: List[str], components: List[str]) -> None: @@ -46,30 +73,3 @@ def print_diff_summary(tag: str, bases: List[str], components: List[str]) -> Non if bases: console.print(f"[base]Changed bases[/]: [data]{len(bases)}[/]") - - -def _changed_projects( - projects_data: List[dict], brick_type: str, bricks: List[str] -) -> set: - res = { - p["path"].name: set(p.get(brick_type, [])).intersection(bricks) - for p in projects_data - } - - return {k for k, v in res.items() if v} - - -def print_short_diff( - projects_data: List[dict], - projects: List[str], - bases: List[str], - components: List[str], -) -> None: - a = _changed_projects(projects_data, "components", components) - b = _changed_projects(projects_data, "bases", bases) - c = set(projects) - - res = {*a, *b, *c} - - console = Console(theme=theme.poly_theme) - console.print(",".join(res)) diff --git a/components/polylith/poetry/commands/diff.py b/components/polylith/poetry/commands/diff.py index 8162227a..3a35ee2e 100644 --- a/components/polylith/poetry/commands/diff.py +++ b/components/polylith/poetry/commands/diff.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import List, Set from cleo.helpers import option from poetry.console.commands.command import Command @@ -13,11 +14,59 @@ class DiffCommand(Command): option( long_name="short", short_name="s", - description="Print only changed projects", + description="Print short view", + flag=True, + ), + option( + long_name="bricks", + description="Print changed bricks", flag=True, ), ] + def has_partial_options(self) -> bool: + return any(self.option(k) for k in {"bricks"}) + + def print_partial_views( + self, + affected_projects: Set[str], + bases: List[str], + components: List[str], + ) -> None: + short = self.option("short") + + if short and not self.has_partial_options(): + diff.report.print_projects_affected_by_changes(affected_projects, short) + + return + + if self.option("bricks"): + diff.report.print_detected_changes_in_bricks(bases, components, short) + + def print_views(self, root: Path, tag: str) -> None: + ns = workspace.parser.get_namespace_from_config(root) + files = diff.collect.get_files(tag) + bases = diff.collect.get_changed_bases(files, ns) + components = diff.collect.get_changed_components(files, ns) + projects = diff.collect.get_changed_projects(files) + all_projects_data = info.get_bricks_in_projects(root, components, bases, ns) + projects_data = [p for p in all_projects_data if info.is_project(p)] + + affected_projects = diff.collect.get_projects_affected_by_changes( + projects_data, projects, bases, components + ) + + short = self.option("short") + + if not short and not self.has_partial_options(): + diff.report.print_diff_summary(tag, bases, components) + diff.report.print_detected_changes_in_projects(projects, short) + diff.report.print_diff_details(projects_data, bases, components) + + return + + self.print_partial_views(affected_projects, bases, components) + def handle(self) -> int: root = repo.get_workspace_root(Path.cwd()) tag = diff.collect.get_latest_tag(root) @@ -25,21 +74,6 @@ def handle(self) -> int: if not tag: self.line("No tags found in repository.") else: - ns = workspace.parser.get_namespace_from_config(root) - files = diff.collect.get_files(tag) - bases = diff.collect.get_changed_bases(files, ns) - components = diff.collect.get_changed_components(files, ns) - projects = diff.collect.get_changed_projects(files) - all_projects_data = info.get_bricks_in_projects(root, components, bases, ns) - projects_data = [p for p in all_projects_data if info.is_project(p)] - - short = self.option("short") - - if short: - diff.report.print_short_diff(projects_data, projects, bases, components) - else: - diff.report.print_diff_summary(tag, bases, components) - diff.report.print_detected_changes_in_projects(projects) - diff.report.print_diff_details(projects_data, bases, components) + self.print_views(root, tag) return 0 diff --git a/projects/poetry_polylith_plugin/pyproject.toml b/projects/poetry_polylith_plugin/pyproject.toml index abe8e638..4109a22e 100644 --- a/projects/poetry_polylith_plugin/pyproject.toml +++ b/projects/poetry_polylith_plugin/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-polylith-plugin" -version = "1.10.1" +version = "1.11.0" description = "A Poetry plugin that adds tooling support for the Polylith Architecture" authors = ["David Vujic"] homepage = "https://davidvujic.github.io/python-polylith-docs/"