From ece422a836addc4af51ae4fd9443504ac9381734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Mon, 27 May 2024 14:30:11 -0300 Subject: [PATCH] Add --info option to display current settings (#28) --- cookieplone/cli.py | 22 ++++++++--- cookieplone/settings.py | 5 +++ cookieplone/utils/console.py | 71 ++++++++++++++++++++++++++++------ cookieplone/utils/internal.py | 73 ++++++++++++++++++++++++++++++----- news/27.feature | 1 + tests/utils/test_internal.py | 24 +++++++++++- 6 files changed, 169 insertions(+), 27 deletions(-) create mode 100644 news/27.feature diff --git a/cookieplone/cli.py b/cookieplone/cli.py index d1410b1..7becb7b 100644 --- a/cookieplone/cli.py +++ b/cookieplone/cli.py @@ -58,6 +58,12 @@ def cli( typer.Option("--output-dir", "-o", help="Where to generate the code."), ] = None, tag: Annotated[str, typer.Option(help="Tag.")] = "main", + info: Annotated[ + bool, + typer.Option( + "--info", help="Display information about cookieplone installation." + ), + ] = False, version: Annotated[ bool, typer.Option("--version", help="Display the version of cookieplone.") ] = False, @@ -114,13 +120,22 @@ def cli( ): """Generate a new Plone codebase.""" if version: - console.base_print(internal.version_info()) + console.version_screen() raise typer.Exit() configure_logger(stream_level="DEBUG" if verbose else "INFO", debug_file=debug_file) repository = os.environ.get(settings.REPO_LOCATION) if not repository: - repository = "gh:plone/cookieplone-templates" + repository = settings.REPO_DEFAULT + + passwd = os.environ.get( + settings.REPO_PASSWORD, os.environ.get("COOKIECUTTER_REPO_PASSWORD") + ) + tag = os.environ.get(settings.REPO_TAG) or tag + + if info: + console.info_screen(repository=repository, passwd=passwd, tag=tag) + raise typer.Exit() repo_path = get_base_repository(repository) if not template: @@ -129,9 +144,6 @@ def cli( else: console.welcome_screen() - passwd = os.environ.get( - settings.REPO_PASSWORD, os.environ.get("COOKIECUTTER_REPO_PASSWORD") - ) if not output_dir: output_dir = Path().cwd() diff --git a/cookieplone/settings.py b/cookieplone/settings.py index 31df728..6d8a677 100644 --- a/cookieplone/settings.py +++ b/cookieplone/settings.py @@ -29,8 +29,13 @@ } MIN_DOCKER_VERSION = "20.10" +## DEFAULT +COOKIEPLONE_REPO = "https://github.com/plone/cookieplone" +TEMPLATES_REPO = "https://github.com/plone/cookiecutter-plone" +REPO_DEFAULT = "gh:plone/cookieplone-templates" ## Config QUIET_MODE_VAR = "COOKIEPLONE_QUIET_MODE_SWITCH" REPO_LOCATION = "COOKIEPLONE_REPOSITORY" +REPO_TAG = "COOKIEPLONE_REPOSITORY_TAG" REPO_PASSWORD = "COOKIEPLONE_REPO_PASSWORD" # noQA:S105 diff --git a/cookieplone/utils/console.py b/cookieplone/utils/console.py index f90e6a1..f32f22f 100644 --- a/cookieplone/utils/console.py +++ b/cookieplone/utils/console.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: MIT import os +from pathlib import Path from textwrap import dedent from rich import print as base_print @@ -13,6 +14,8 @@ from cookieplone.settings import QUIET_MODE_VAR +from .internal import cookieplone_info, version_info + BANNER = """ .xxxxxxxxxxxxxx. ;xxxxxxxxxxxxxxxxxxxxxx; @@ -108,18 +111,28 @@ def panel(title: str, msg: str = "", subtitle: str = "", url: str = ""): ) -def table_available_templates(title: str, rows: list[list[str]]): - """Display a table of options.""" - table = Table(title=title, expand=True) - - table.add_column("#", justify="center", style="cyan", no_wrap=True) - table.add_column("Title", style="blue") - table.add_column("Description", justify="left", style="blue") +def create_table( + columns: list[dict] | None = None, rows: list[list[str]] | None = None, **kwargs +) -> Table: + """Create table.""" + table = Table(**kwargs) + for column in columns: + col_title = column.pop("title", "") + table.add_column(col_title, **column) + for row in rows: + table.add_row(*row) + return table - for idx, _, title, description in rows: - table.add_row(idx, title, description) - return table +def table_available_templates(title: str, rows: list[list[str]]) -> Table: + """Display a table of options.""" + columns = [ + {"title": "#", "justify": "center", "style": "cyan", "no_wrap": True}, + {"title": "Title", "style": "blue"}, + {"title": "Description", "justify": "left", "style": "blue"}, + ] + rows = [(idx, title, description) for idx, _, title, description in rows] + return create_table(columns, rows, title=title, expand=True) def welcome_screen(templates: list[list[str]] | None = None): @@ -127,7 +140,7 @@ def welcome_screen(templates: list[list[str]] | None = None): Align.center(f"[bold blue]{BANNER}[/bold blue]"), ] if templates: - items.append(Panel(table_available_templates("Templates", templates))) + items.append(Panel(table_available_templates(templates, title="Templates"))) panel = Panel( Group(*items), title="cookieplone", @@ -135,6 +148,42 @@ def welcome_screen(templates: list[list[str]] | None = None): base_print(panel) +def version_screen(): + """Print version information.""" + base_print(version_info()) + + +def info_screen(repository: str | Path, passwd: str, tag: str): + info = cookieplone_info(repository, passwd, tag) + title = info["title"] + subtitle = info["subtitle"] + panels = info["panels"] + columns = [ + {"title": "", "justify": "left", "style": "cyan", "no_wrap": True, "ratio": 1}, + {"title": "", "justify": "left", "style": "cyan", "no_wrap": True, "ratio": 3}, + ] + items = [] + for panel in panels.values(): + panel_title = panel.get("title") + panel_rows = panel.get("rows") + if panel_rows: + items.append( + Panel( + create_table( + columns, panel_rows, expand=True, box=None, show_header=False + ), + title=panel_title, + title_align="left", + ) + ) + panel = Panel( + Group(*items), + title=title, + subtitle=subtitle, + ) + base_print(panel) + + def enable_quiet_mode(): """Enable quiet mode.""" os.environ[QUIET_MODE_VAR] = "1" diff --git a/cookieplone/utils/internal.py b/cookieplone/utils/internal.py index 73c65c4..80bfa80 100644 --- a/cookieplone/utils/internal.py +++ b/cookieplone/utils/internal.py @@ -7,35 +7,88 @@ from cookiecutter import __version__ as __cookiecutter_version__ -from cookieplone import __version__ +from cookieplone import __version__, settings from cookieplone.utils import git -COOKIEPLONE_REPO = "https://github.com/plone/cookieplone" -TEMPLATES_REPO = "https://github.com/plone/cookiecutter-plone" +SIGNATURE = ( + "Made with [bold][red]❤️[/red][/bold] by the" + " [bold][blue]Plone Community[/blue][/bold]" +) + + +def __cookiecutter_location__() -> Path: + """Return the cookiecutter location.""" + import cookiecutter + + return Path(cookiecutter.__file__).parent + + +def __location__() -> Path: + """Return the cookieplone location.""" + return Path(__file__).parent.parent def version_info() -> str: """Return the Cookieplone version, location and Python powering it.""" python_version = sys.version # Get the root of cookieplone - location = Path(__file__).parent.parent return ( - f"Cookieplone {__version__} from {location} " + f"Cookieplone {__version__} from {__location__()} " f"(Cookiecutter {__cookiecutter_version__}, " f"Python {python_version})\n\n" - f"Made with [bold][red]❤️[/red][/bold] by the" - " [bold][blue]Plone Community[/blue][/bold]" + f"{SIGNATURE}" ) +def cookieplone_info(repository: str | Path, passwd: str = "", tag: str = "") -> dict: + """Print information about current configuration.""" + return { + "title": "cookieplone", + "subtitle": SIGNATURE, + "panels": { + "cookieplone": { + "title": "Installation :zap:", + "rows": [ + ["Version", __version__], + ["Location", f"{__location__()}"], + ], + }, + "repository": { + "title": "Repository :link:", + "rows": [ + [f"Location ({settings.REPO_LOCATION})", f"{repository}"], + [f"Tag ({settings.REPO_TAG})", tag], + [f"Password ({settings.REPO_PASSWORD})", "***" if passwd else ""], + ], + }, + "cookiecutter": { + "title": "Cookiecutter :cookie:", + "rows": [ + ["Version", __cookiecutter_version__], + ["Location", f"{__cookiecutter_location__()}"], + ], + }, + "python": { + "title": "Python :snake:", + "rows": [ + ["Version", f"{sys.version}"], + ], + }, + }, + } + + def signature_md(path: Path) -> str: """Return a signature, in markdown.""" date_info = f"{datetime.now()}" - cookieplone = f"[Cookieplone ({__version__})]({COOKIEPLONE_REPO})" + cookieplone = f"[Cookieplone ({__version__})]({settings.COOKIEPLONE_REPO})" commit = git.get_last_commit(path) + template_title = "cookiecutter-plone" if not commit: - template = f"[cookiecutter-plone]({TEMPLATES_REPO})" + template_link = settings.TEMPLATES_REPO else: sha = commit.hexsha - template = f"[cookiecutter-plone ({sha[:7]})]({TEMPLATES_REPO}/commit/{sha})" + template_title = f"{template_title} ({sha[:7]})" + template_link = f"{settings.TEMPLATES_REPO}/commit/{sha}" + template = f"[{template_title}]({template_link})" return f"""Generated using {cookieplone} and {template} on {date_info}""" diff --git a/news/27.feature b/news/27.feature new file mode 100644 index 0000000..578a31f --- /dev/null +++ b/news/27.feature @@ -0,0 +1 @@ +Add --info option to display current settings [@ericof] diff --git a/tests/utils/test_internal.py b/tests/utils/test_internal.py index 7b5fa63..7747c15 100644 --- a/tests/utils/test_internal.py +++ b/tests/utils/test_internal.py @@ -2,9 +2,10 @@ import sys from pathlib import Path +import pytest from cookiecutter import __version__ as __cookiecutter_version__ -from cookieplone import __version__ +from cookieplone import __version__, settings from cookieplone.utils import internal @@ -20,6 +21,27 @@ def test_version_info(): assert entry in result +@pytest.mark.parametrize( + "panel_id,panel_title", + [ + ["cookieplone", "Installation :zap:"], + ["repository", "Repository :link:"], + ["cookiecutter", "Cookiecutter :cookie:"], + ["python", "Python :snake:"], + ], +) +def test_cookieplone_info(panel_id: str, panel_title: str): + result = internal.cookieplone_info(settings.REPO_DEFAULT) + assert isinstance(result, dict) + assert result["title"] == "cookieplone" + assert result["subtitle"] == internal.SIGNATURE + panels = result["panels"] + assert isinstance(panels, dict) + panel = panels[panel_id] + assert isinstance(panel, dict) + assert panel["title"] == panel_title + + def test_signature_md_without_commit(no_repo): result = internal.signature_md(no_repo) assert isinstance(result, str)