From 47cff82bd37eed33c3b1d48822c7efe2efa5f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Fri, 19 Apr 2024 21:08:00 -0600 Subject: [PATCH] Add git helper functions (Fixes #4) --- cookieplone/cli.py | 29 +++++++++------------------- cookieplone/settings.py | 2 ++ cookieplone/utils/console.py | 14 +++++++++++--- cookieplone/utils/git.py | 32 +++++++++++++++++++++++++++++++ cookieplone/utils/internal.py | 36 +++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 cookieplone/utils/git.py create mode 100644 cookieplone/utils/internal.py diff --git a/cookieplone/cli.py b/cookieplone/cli.py index e9ed91e..9d918d7 100644 --- a/cookieplone/cli.py +++ b/cookieplone/cli.py @@ -1,21 +1,19 @@ """Main `cookieplone` CLI.""" import os -import sys from pathlib import Path from typing import Annotated import typer -from cookiecutter import __version__ as __cookiecutter_version__ from cookiecutter.log import configure_logger from rich import print from rich.prompt import Prompt -from cookieplone import __version__, data +from cookieplone import data, settings from cookieplone.exceptions import GeneratorException from cookieplone.generator import generate from cookieplone.repository import get_base_repository, get_template_options -from cookieplone.utils import console +from cookieplone.utils import console, internal def validate_extra_context(value: list[str] | None = None): @@ -42,17 +40,6 @@ def prompt_for_template(base_path: Path) -> str: return choices[answer] -def version_info() -> str: - """Return the Cookieplone version, location and Python powering it.""" - python_version = sys.version - location = Path(__file__).parent - return ( - f"Cookieplone {__version__} from {location} " - f"(Cookiecutter {__cookiecutter_version__}, " - f"Python {python_version})" - ) - - def cli( template: Annotated[str, typer.Argument(help="Template to be used.")] = "", extra_context: Annotated[ @@ -120,16 +107,15 @@ def cli( ): """Generate a new Plone codebase.""" if version: - info = version_info - print(info) + print(internal.version_info) raise typer.Exit() - repository = os.environ.get("COOKIEPLONE_REPOSITORY") + repository = os.environ.get(settings.REPO_LOCATION) if not repository: repository = "gh:plone/cookiecutter-plone" + repo_path = get_base_repository(repository) if not template: # Display template options - repo_path = get_base_repository(repository) template = prompt_for_template(Path(repo_path)) else: console.welcome_screen() @@ -137,11 +123,14 @@ def cli( if replay_file: replay = replay_file passwd = os.environ.get( - "COOKIECUTTER_REPO_PASSWORD", os.environ.get("COOKIEPLONE_REPO_PASSWORD") + settings.REPO_PASSWORD, os.environ.get("COOKIECUTTER_REPO_PASSWORD") ) if not output_dir: output_dir = Path().cwd() configure_logger(stream_level="DEBUG" if verbose else "INFO", debug_file=debug_file) + # Annotate extra_context + extra_context = extra_context if extra_context else {} + extra_context["__generator_signature"] = internal.signature_md(repo_path) # Run generator try: generate( diff --git a/cookieplone/settings.py b/cookieplone/settings.py index 4a08c39..706663f 100644 --- a/cookieplone/settings.py +++ b/cookieplone/settings.py @@ -29,3 +29,5 @@ ## Config QUIET_MODE_VAR = "COOKIEPLONE_QUIET_MODE_SWITCH" +REPO_LOCATION = "COOKIEPLONE_REPOSITORY" +REPO_PASSWORD = "COOKIEPLONE_REPO_PASSWORD" # noQA:S105 diff --git a/cookieplone/utils/console.py b/cookieplone/utils/console.py index eb0e437..06034b9 100644 --- a/cookieplone/utils/console.py +++ b/cookieplone/utils/console.py @@ -2,6 +2,8 @@ from textwrap import dedent from rich import print as base_print +from rich.align import Align +from rich.console import Group from rich.markup import escape from rich.panel import Panel from rich.table import Table @@ -118,10 +120,16 @@ def table_available_templates(title: str, rows: list[list[str]]): def welcome_screen(templates: list[list[str]] | None = None): - print_plone_banner() + items = [ + Align.center(f"[bold blue]{BANNER}[/bold blue]"), + ] if templates: - table = table_available_templates("Templates", templates) - _print(table) + items.append(Panel(table_available_templates("Templates", templates))) + panel = Panel( + Group(*items), + title="cookieplone", + ) + base_print(panel) def enable_quiet_mode(): diff --git a/cookieplone/utils/git.py b/cookieplone/utils/git.py new file mode 100644 index 0000000..8249e3c --- /dev/null +++ b/cookieplone/utils/git.py @@ -0,0 +1,32 @@ +from pathlib import Path + +from git import Commit, Repo +from git.exc import InvalidGitRepositoryError + + +def repo_from_path(path: Path) -> Repo | None: + """Return the repo for the given path.""" + try: + repo = Repo(path) + except InvalidGitRepositoryError: + repo = None + return repo + + +def check_path_is_repository(path: Path) -> bool: + """Check if given path is a Git Repository.""" + return bool(repo_from_path(path)) + + +def initialize_repository(path: Path) -> Repo: + """Initialize a git repository and add all files.""" + if not check_path_is_repository(path): + repo = Repo.init(path) + repo.git.add(path) + return repo + + +def get_last_commit(path: Path) -> Commit | None: + """Return the last commit for a repo.""" + repo = repo_from_path(path) + return repo.head.commit if repo else None diff --git a/cookieplone/utils/internal.py b/cookieplone/utils/internal.py new file mode 100644 index 0000000..46ce8f8 --- /dev/null +++ b/cookieplone/utils/internal.py @@ -0,0 +1,36 @@ +import sys +from datetime import datetime +from pathlib import Path + +from cookiecutter import __version__ as __cookiecutter_version__ + +from cookieplone import __version__ +from cookieplone.utils import git + +COOKIEPLONE_REPO = "https://github.com/plone/cookieplone" +TEMPLATES_REPO = "https://github.com/plone/cookiecutter-plone" + + +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"(Cookiecutter {__cookiecutter_version__}, " + f"Python {python_version})" + ) + + +def signature_md(path: Path) -> str: + """Return a signature, in markdown.""" + date_info = f"{datetime.now()}" + cookieplone = f"[Cookieplone ({__version__})]({COOKIEPLONE_REPO})" + commit = git.get_last_commit(path) + if not commit: + template = f"[cookiecutter-plone]({TEMPLATES_REPO})" + else: + sha = commit.hexsha + template = f"[cookiecutter-plone ({sha[:7]})]({TEMPLATES_REPO}/commit/{sha})" + return f"""Generated using {cookieplone} and {template} on {date_info}"""