Skip to content

Commit

Permalink
Add support for SSH clone and Cookiecutter directories
Browse files Browse the repository at this point in the history
* Add support for folders and SSH clone
* Update CHANGELOG.md
  • Loading branch information
RobertoPrevato authored Aug 20, 2023
1 parent aa0f7ac commit d8026ab
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 24 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.0.4] - 2023-08-20 :sun_with_face:

- Adds support for Git repositories cloned over SSH.
- Adds support for Cookiecutter [directories](https://cookiecutter.readthedocs.io/en/stable/cli_options.html#cmdoption-cookiecutter-directory).

## [0.0.3] - 2023-06-28 :wrench:

- Adds support for conditional questions.
Expand Down
5 changes: 4 additions & 1 deletion blacksheepcli/create/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ def create_project(

assert destination is not None
ProjectManager().bootstrap(
template_obj.source, template_obj.tag or None, {"project_name": name}
template_obj.source,
template_obj.tag or None,
template_obj.folder,
{"project_name": name},
)

print_instructions(destination)
10 changes: 8 additions & 2 deletions blacksheepcli/create/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ def validate_name(value: Optional[str]):


class ProjectManager:
def bootstrap(self, source: str, checkout: Optional[str] = None, data=None):
def bootstrap(
self,
source: str,
checkout: Optional[str] = None,
folder: Optional[str] = None,
data=None,
):
# Lazy import, to not slow down the CLI start-up for every command
from blacksheepcli.common.cookiemod import cookiecutter

# https://cookiecutter.readthedocs.io/en/stable/advanced/calling_from_python.html

cookiecutter(source, checkout=checkout, extra_context=data)
cookiecutter(source, checkout=checkout, directory=folder, extra_context=data)
30 changes: 24 additions & 6 deletions blacksheepcli/templates/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from json import dumps
from typing import Optional

import questionary

Expand All @@ -21,6 +22,18 @@ def templates():
"""


def _display_source_details(template: Template):
from rich.text import Text

text = Text(template.source)

if template.tag:
text.append(f"\ntag: {template.tag}", style="yellow")
if template.folder:
text.append(f"\nfolder: {template.folder}", style="yellow")
return text


@click.command(name="details")
def describe_templates():
"""
Expand All @@ -38,14 +51,16 @@ def describe_templates():
table.add_column("Description", justify="left", style="green")

for template in templates:
table.add_row(template.id, template.source, template.description)
table.add_row(
template.id, _display_source_details(template), template.description
)

console = Console()
console.print(table)


@click.command(name="list")
@click.option("--source", "-s", is_flag=True, help="Include the source in the output.s")
@click.option("--source", "-s", is_flag=True, help="Include the source in the output.")
def list_templates(source: bool):
"""
Lists all available templates.
Expand All @@ -63,6 +78,7 @@ def list_templates(source: bool):
@click.command(name="add")
@click.argument("name")
@click.argument("source")
@click.argument("folder", required=False)
@click.option(
"-d",
"--description",
Expand All @@ -71,11 +87,13 @@ def list_templates(source: bool):
help="Template description.",
)
@click.option("-f", "--force", is_flag=True, help="Force update if the template exists")
def add_template(name: str, source: str, description: str, force: bool):
def add_template(
name: str, source: str, folder: Optional[str], description: str, force: bool
):
"""
Add a template, by name and source, with optional description. If a specific tag
should be used for a Git repository, it can be specified at the end of the source,
using an "@" sign. Example: https://github.com/Neoteroi/BlackSheep-Foo@v2
using an "@" sign. Example: https://github.com/Neoteroi/BlackSheep-Foo$v2
This command fails if a template exists with the same name. To overwrite an existing
template, use the -f, or --force flag. Templates can later be used to scaffold new
Expand All @@ -86,10 +104,10 @@ def add_template(name: str, source: str, description: str, force: bool):
blacksheep templates add foo https://github.com/Neoteroi/BlackSheep-Foo
-d 'Some nice template! 🐃'
blacksheep templates add foo2 https://github.com/Neoteroi/BlackSheepFoo@v2
blacksheep templates add foo2 'https://github.com/Neoteroi/BlackSheepFoo$v2'
"""
manager = TemplatesManager()
manager.add_template(name, source, description, force)
manager.add_template(name, source, folder, description, force)


@click.command(name="remove")
Expand Down
9 changes: 8 additions & 1 deletion blacksheepcli/templates/data/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def update_template(self, template: Template):
current = next((value for value in templates if value.id == template.id), None)
if current:
current.source = template.source
current.tag = template.tag
current.description = template.description
else:
current = template
Expand All @@ -105,6 +106,12 @@ def get_templates(self) -> List[Template]:
except KeyError:
return []
return [
Template(item.get("id"), item.get("source"), item.get("description"))
Template(
item["id"],
item["source"],
item.get("description", ""),
item.get("tag", ""),
folder=item.get("folder"),
)
for item in sorted(templates, key=lambda item: item["id"])
]
29 changes: 21 additions & 8 deletions blacksheepcli/templates/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ class Template:
id: str
source: str
description: str = ""
tag: str = ""
folder: Optional[str] = None

@property
def tag(self) -> str:
if "@" in self.source:
return self.source.split("@")[-1]
return ""
def __post_init__(self):
if "$" in self.source:
tag = self.source.split("$")[-1]
self.tag = tag
self.source = self.source[: -(len(tag) + 1)]


class TemplateError(ClickException):
Expand Down Expand Up @@ -64,15 +66,26 @@ class TemplatesManager:
def __init__(self, provider: Optional[TemplatesDataProvider] = None) -> None:
self._provider = provider or _get_default_data_provider()

def add_template(self, name: str, source: str, description: str, force: bool):
def add_template(
self,
name: str,
source: str,
folder: Optional[str],
description: str,
force: bool,
):
try:
self.get_template_by_name(name)
except TemplateNotFoundError:
# good
self._provider.add_template(Template(name, source, description))
self._provider.add_template(
Template(name, source, description, folder=folder)
)
else:
if force:
self._provider.update_template(Template(name, source, description))
self._provider.update_template(
Template(name, source, description, folder=folder)
)
else:
raise TemplateConflictError(name)

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "blacksheep-cli"
version = "0.0.3"
version = "0.0.4"
authors = [{ name = "Roberto Prevato", email = "[email protected]" }]
description = "🛠️ CLI to bootstrap BlackSheep projects"
readme = "README.md"
Expand All @@ -24,7 +24,7 @@ keywords = [
"CLI",
"developer experience",
"projects scaffolding",
"project templates"
"project templates",
]

dependencies = [
Expand All @@ -35,7 +35,7 @@ dependencies = [
"essentials-configuration[full]",
"cookiecutter",
"questionary",
"pathvalidate==3.0.0"
"pathvalidate==3.0.0",
]

[project.scripts]
Expand Down
6 changes: 3 additions & 3 deletions tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def test_list_templates():
"source,expected_tag",
[
["~/projects/github/blacksheep-api", ""],
["~/projects/github/blacksheep-api@v2", "v2"],
["~/projects/github/blacksheep-api$v2", "v2"],
["https://github.com/Neoteroi/BlackSheep-API", ""],
["https://github.com/Neoteroi/BlackSheep-API@", ""],
["https://github.com/Neoteroi/BlackSheep-API@v2", "v2"],
["https://github.com/Neoteroi/BlackSheep-API$", ""],
["https://github.com/Neoteroi/BlackSheep-API$v2", "v2"],
],
)
def test_template_source_tag(source, expected_tag):
Expand Down

0 comments on commit d8026ab

Please sign in to comment.