forgepy-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- builders/__init__.py +0 -0
- builders/base_builder.py +17 -0
- builders/file_builder.py +31 -0
- builders/folder_builder.py +30 -0
- builders/python_tools_builder.py +64 -0
- cli/__init__.py +3 -0
- cli/command.py +52 -0
- cli/commands/__init__.py +32 -0
- cli/commands/component_command.py +177 -0
- cli/commands/config_command.py +174 -0
- cli/commands/create_command.py +161 -0
- cli/commands/list_command.py +40 -0
- cli/commands/version_command.py +29 -0
- cli/dispatcher.py +61 -0
- cli/parser.py +84 -0
- components/__init__.py +1 -0
- components/base_component.py +30 -0
- components/component_context.py +21 -0
- components/component_installer.py +44 -0
- components/component_manifest.py +102 -0
- components/component_metadata.py +50 -0
- components/component_registry.py +74 -0
- components/component_state.py +243 -0
- components/component_validation.py +88 -0
- components/github_actions_component.py +75 -0
- components/pytest_component.py +40 -0
- components/ruff_component.py +40 -0
- config/__init__.py +0 -0
- config/default_structure.py +18 -0
- config/user_config.py +238 -0
- config/version.py +16 -0
- core/__init__.py +0 -0
- core/environment_builder.py +33 -0
- core/git_builder.py +90 -0
- core/project_generator.py +125 -0
- core/requirements_installer.py +50 -0
- core/vscode_builder.py +103 -0
- forgepy_cli-1.0.0.dist-info/METADATA +176 -0
- forgepy_cli-1.0.0.dist-info/RECORD +74 -0
- forgepy_cli-1.0.0.dist-info/WHEEL +5 -0
- forgepy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- forgepy_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- forgepy_cli-1.0.0.dist-info/top_level.txt +8 -0
- main.py +20 -0
- models/__init__.py +1 -0
- models/project_config.py +87 -0
- templates/__init__.py +0 -0
- templates/app_template.py +20 -0
- templates/basic/basic_files.py +23 -0
- templates/basic/basic_template.py +32 -0
- templates/changelog_template.py +15 -0
- templates/cli/cli_files.py +70 -0
- templates/cli/cli_template.py +55 -0
- templates/env_template.py +20 -0
- templates/gitignore_template.py +27 -0
- templates/library/library_files.py +28 -0
- templates/library/library_template.py +56 -0
- templates/license_template.py +21 -0
- templates/pyproject_template.py +15 -0
- templates/readme_template.py +15 -0
- templates/requirements_template.py +13 -0
- templates/template_engine/base_template.py +58 -0
- templates/template_engine/file_template.py +76 -0
- templates/template_engine/package_name.py +30 -0
- templates/template_engine/template_context.py +24 -0
- templates/template_engine/template_files.py +10 -0
- templates/template_engine/template_metadata.py +59 -0
- templates/template_engine/template_registry.py +107 -0
- templates/template_manager.py +58 -0
- templates/vscode/__init__.py +0 -0
- templates/vscode/extensions_template.py +32 -0
- templates/vscode/launch_template.py +41 -0
- templates/vscode/settings_template.py +37 -0
- templates/vscode/tasks_template.py +66 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Library Template Files
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from templates.template_manager import TemplateManager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LibraryFiles:
|
|
12
|
+
"""Build the minimal file mapping for a Python library project."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def build(
|
|
16
|
+
project_name: str,
|
|
17
|
+
package_name: str,
|
|
18
|
+
) -> dict[str, str]:
|
|
19
|
+
template_manager = TemplateManager()
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
"README.md": template_manager.get_readme(project_name),
|
|
23
|
+
".gitignore": template_manager.get_gitignore(),
|
|
24
|
+
"requirements.txt": "",
|
|
25
|
+
"pyproject.toml": template_manager.get_pyproject(project_name),
|
|
26
|
+
f"{package_name}/__init__.py": "",
|
|
27
|
+
"tests/__init__.py": "",
|
|
28
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Library Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from templates.library.library_files import LibraryFiles
|
|
11
|
+
from templates.template_engine.file_template import FileTemplate
|
|
12
|
+
from templates.template_engine.package_name import normalize_package_name
|
|
13
|
+
from templates.template_engine.template_context import TemplateContext
|
|
14
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LibraryTemplate(FileTemplate):
|
|
18
|
+
"""Generate a minimal reusable Python package project."""
|
|
19
|
+
|
|
20
|
+
_METADATA = TemplateMetadata(
|
|
21
|
+
name="library",
|
|
22
|
+
description="Reusable Python package template.",
|
|
23
|
+
version="0.1.0",
|
|
24
|
+
author="Rendy Zou",
|
|
25
|
+
tags=("python", "library", "package"),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
_DEFAULT_VSCODE_ENTRY_POINT = None
|
|
29
|
+
|
|
30
|
+
def _build_context(
|
|
31
|
+
self,
|
|
32
|
+
project_path: Path,
|
|
33
|
+
) -> TemplateContext:
|
|
34
|
+
return TemplateContext(
|
|
35
|
+
project_path=project_path,
|
|
36
|
+
package_name=self._normalize_package_name(project_path.name),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def _folders(self, context: TemplateContext) -> tuple[str, ...]:
|
|
40
|
+
return (
|
|
41
|
+
context.require_package_name(),
|
|
42
|
+
"tests",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def _files(self, context: TemplateContext) -> dict[str, str]:
|
|
46
|
+
return LibraryFiles.build(
|
|
47
|
+
project_name=context.project_name,
|
|
48
|
+
package_name=context.require_package_name(),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def _normalize_package_name(project_name: str) -> str:
|
|
53
|
+
return normalize_package_name(
|
|
54
|
+
project_name,
|
|
55
|
+
package_label="library",
|
|
56
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
License Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_license(author: str = "Your Name") -> str:
|
|
10
|
+
return f"""MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 {author}
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
21
|
+
"""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
PyProject Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_pyproject(project_name: str) -> str:
|
|
10
|
+
return f"""[project]
|
|
11
|
+
name = "{project_name}"
|
|
12
|
+
version = "0.1.0"
|
|
13
|
+
description = ""
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
"""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Readme Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_readme(project_name: str) -> str:
|
|
10
|
+
return f"""# {project_name}
|
|
11
|
+
|
|
12
|
+
Generated by ForgePy.
|
|
13
|
+
|
|
14
|
+
Version: 0.1
|
|
15
|
+
"""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Base Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseTemplate(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Seluruh template harus mewarisi class ini.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def name(self) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Nama template.
|
|
24
|
+
"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def metadata(self) -> TemplateMetadata:
|
|
29
|
+
"""Return compatibility metadata for legacy template subclasses."""
|
|
30
|
+
|
|
31
|
+
return TemplateMetadata(
|
|
32
|
+
name=self.name,
|
|
33
|
+
description="",
|
|
34
|
+
version="",
|
|
35
|
+
author="",
|
|
36
|
+
tags=(),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def vscode_entry_point(self) -> str | None:
|
|
41
|
+
"""Return the file VS Code should launch for this template."""
|
|
42
|
+
|
|
43
|
+
return "app.py"
|
|
44
|
+
|
|
45
|
+
def preflight(self, project_path: Path) -> None:
|
|
46
|
+
"""Validate template-specific inputs before project creation."""
|
|
47
|
+
|
|
48
|
+
del project_path
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def create(
|
|
52
|
+
self,
|
|
53
|
+
project_path: Path,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Membuat project.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Shared execution for templates backed by folders and file mappings."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import ClassVar
|
|
6
|
+
|
|
7
|
+
from builders.file_builder import FileBuilder
|
|
8
|
+
from builders.folder_builder import FolderBuilder
|
|
9
|
+
from templates.template_engine.base_template import BaseTemplate
|
|
10
|
+
from templates.template_engine.template_context import TemplateContext
|
|
11
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FileTemplate(BaseTemplate):
|
|
15
|
+
"""Write template-owned folders and files from focused subclass hooks."""
|
|
16
|
+
|
|
17
|
+
_METADATA: ClassVar[TemplateMetadata]
|
|
18
|
+
_DEFAULT_VSCODE_ENTRY_POINT: ClassVar[str | None]
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self._vscode_entry_point: str | None = (
|
|
22
|
+
self._DEFAULT_VSCODE_ENTRY_POINT
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def metadata(self) -> TemplateMetadata:
|
|
27
|
+
return self._METADATA
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def name(self) -> str:
|
|
31
|
+
return self.metadata.name
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def vscode_entry_point(self) -> str | None:
|
|
35
|
+
return self._vscode_entry_point
|
|
36
|
+
|
|
37
|
+
def preflight(self, project_path: Path) -> None:
|
|
38
|
+
"""Build context to validate template-specific inputs."""
|
|
39
|
+
|
|
40
|
+
self._build_context(project_path)
|
|
41
|
+
|
|
42
|
+
def create(self, project_path: Path) -> None:
|
|
43
|
+
context = self._build_context(project_path)
|
|
44
|
+
|
|
45
|
+
FolderBuilder().create(
|
|
46
|
+
context.project_path,
|
|
47
|
+
list(self._folders(context)),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
file_builder = FileBuilder()
|
|
51
|
+
|
|
52
|
+
for filename, content in self._files(context).items():
|
|
53
|
+
file_builder.write(
|
|
54
|
+
context.project_path / filename,
|
|
55
|
+
content,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self._vscode_entry_point = self._vscode_entry_point_for(context)
|
|
59
|
+
|
|
60
|
+
def _build_context(self, project_path: Path) -> TemplateContext:
|
|
61
|
+
return TemplateContext(project_path=project_path)
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def _folders(self, context: TemplateContext) -> tuple[str, ...]:
|
|
65
|
+
"""Return template-owned folders in creation order."""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def _files(self, context: TemplateContext) -> dict[str, str]:
|
|
69
|
+
"""Return template-owned file content in write order."""
|
|
70
|
+
|
|
71
|
+
def _vscode_entry_point_for(
|
|
72
|
+
self,
|
|
73
|
+
context: TemplateContext,
|
|
74
|
+
) -> str | None:
|
|
75
|
+
del context
|
|
76
|
+
return self._DEFAULT_VSCODE_ENTRY_POINT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import keyword
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def normalize_package_name(
|
|
6
|
+
project_name: str,
|
|
7
|
+
*,
|
|
8
|
+
package_label: str,
|
|
9
|
+
) -> str:
|
|
10
|
+
"""Return a stable ASCII Python package name for a project."""
|
|
11
|
+
|
|
12
|
+
package_name = re.sub(
|
|
13
|
+
r"[^a-z0-9_]+",
|
|
14
|
+
"_",
|
|
15
|
+
project_name.lower(),
|
|
16
|
+
).strip("_")
|
|
17
|
+
|
|
18
|
+
if not package_name:
|
|
19
|
+
raise ValueError(
|
|
20
|
+
"Project name must contain letters or digits for the "
|
|
21
|
+
f"{package_label} package."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if package_name[0].isdigit():
|
|
25
|
+
package_name = f"_{package_name}"
|
|
26
|
+
|
|
27
|
+
if keyword.iskeyword(package_name):
|
|
28
|
+
package_name = f"{package_name}_"
|
|
29
|
+
|
|
30
|
+
return package_name
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Per-generation data used by file-based templates."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class TemplateContext:
|
|
9
|
+
"""Keep project and optional package names together during generation."""
|
|
10
|
+
|
|
11
|
+
project_path: Path
|
|
12
|
+
package_name: str | None = None
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def project_name(self) -> str:
|
|
16
|
+
return self.project_path.name
|
|
17
|
+
|
|
18
|
+
def require_package_name(self) -> str:
|
|
19
|
+
"""Return the package name for package-oriented templates."""
|
|
20
|
+
|
|
21
|
+
if self.package_name is None:
|
|
22
|
+
raise ValueError("Template context does not define a package name.")
|
|
23
|
+
|
|
24
|
+
return self.package_name
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Template Metadata
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class TemplateMetadata:
|
|
13
|
+
"""Describe a registered project template."""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
description: str
|
|
17
|
+
version: str
|
|
18
|
+
author: str
|
|
19
|
+
tags: tuple[str, ...]
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
for field_name in (
|
|
23
|
+
"name",
|
|
24
|
+
"description",
|
|
25
|
+
"version",
|
|
26
|
+
"author",
|
|
27
|
+
):
|
|
28
|
+
if not isinstance(getattr(self, field_name), str):
|
|
29
|
+
raise TypeError(
|
|
30
|
+
f"Template metadata {field_name} must be a string."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if not self.name.strip():
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"Template metadata name must not be empty."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if isinstance(self.tags, (str, bytes)):
|
|
39
|
+
raise TypeError(
|
|
40
|
+
"Template metadata tags must be an iterable of strings."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
normalized_tags = tuple(self.tags)
|
|
45
|
+
except TypeError as error:
|
|
46
|
+
raise TypeError(
|
|
47
|
+
"Template metadata tags must be an iterable of strings."
|
|
48
|
+
) from error
|
|
49
|
+
|
|
50
|
+
if not all(isinstance(tag, str) for tag in normalized_tags):
|
|
51
|
+
raise TypeError(
|
|
52
|
+
"Template metadata tags must contain only strings."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
object.__setattr__(
|
|
56
|
+
self,
|
|
57
|
+
"tags",
|
|
58
|
+
normalized_tags,
|
|
59
|
+
)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Template Registry
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from templates.basic.basic_template import BasicTemplate
|
|
11
|
+
from templates.cli.cli_template import CliTemplate
|
|
12
|
+
from templates.library.library_template import LibraryTemplate
|
|
13
|
+
from templates.template_engine.base_template import BaseTemplate
|
|
14
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class _TemplateRegistration:
|
|
19
|
+
template: BaseTemplate
|
|
20
|
+
metadata: TemplateMetadata
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TemplateRegistry:
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
|
|
27
|
+
self._registrations: dict[str, _TemplateRegistration] = {}
|
|
28
|
+
|
|
29
|
+
self.register(BasicTemplate())
|
|
30
|
+
self.register(LibraryTemplate())
|
|
31
|
+
self.register(CliTemplate())
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def templates(self) -> dict[str, BaseTemplate]:
|
|
35
|
+
"""Return a compatibility snapshot of registered templates."""
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
name: registration.template
|
|
39
|
+
for name, registration in self._registrations.items()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def register(
|
|
43
|
+
self,
|
|
44
|
+
template: BaseTemplate,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Register a template using its metadata name as the lookup key."""
|
|
47
|
+
|
|
48
|
+
if not isinstance(template, BaseTemplate):
|
|
49
|
+
raise TypeError("Template must inherit from BaseTemplate.")
|
|
50
|
+
|
|
51
|
+
template_name = template.name
|
|
52
|
+
|
|
53
|
+
if not isinstance(template_name, str):
|
|
54
|
+
raise TypeError("Template name must be a string.")
|
|
55
|
+
|
|
56
|
+
if not template_name.strip():
|
|
57
|
+
raise ValueError("Template name must not be empty.")
|
|
58
|
+
|
|
59
|
+
metadata = template.metadata
|
|
60
|
+
|
|
61
|
+
if not isinstance(metadata, TemplateMetadata):
|
|
62
|
+
raise TypeError("Template metadata must be TemplateMetadata.")
|
|
63
|
+
|
|
64
|
+
if not metadata.name.strip():
|
|
65
|
+
raise ValueError("Template metadata name must not be empty.")
|
|
66
|
+
|
|
67
|
+
if template_name != metadata.name:
|
|
68
|
+
raise ValueError(
|
|
69
|
+
"Template name and metadata name must match."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if metadata.name in self._registrations:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Template '{metadata.name}' is already registered."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
self._registrations[metadata.name] = _TemplateRegistration(
|
|
78
|
+
template=template,
|
|
79
|
+
metadata=metadata,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def get(
|
|
83
|
+
self,
|
|
84
|
+
name: str,
|
|
85
|
+
) -> BaseTemplate:
|
|
86
|
+
|
|
87
|
+
return self._registrations[name].template
|
|
88
|
+
|
|
89
|
+
def get_metadata(
|
|
90
|
+
self,
|
|
91
|
+
name: str,
|
|
92
|
+
) -> TemplateMetadata:
|
|
93
|
+
"""Return metadata for one registered template."""
|
|
94
|
+
|
|
95
|
+
return self._registrations[name].metadata
|
|
96
|
+
|
|
97
|
+
def list_templates(self) -> dict[str, BaseTemplate]:
|
|
98
|
+
|
|
99
|
+
return self.templates
|
|
100
|
+
|
|
101
|
+
def list_metadata(self) -> tuple[TemplateMetadata, ...]:
|
|
102
|
+
"""Return registered metadata in registration order."""
|
|
103
|
+
|
|
104
|
+
return tuple(
|
|
105
|
+
registration.metadata
|
|
106
|
+
for registration in self._registrations.values()
|
|
107
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Template Manager
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from templates.app_template import get_app
|
|
9
|
+
from templates.gitignore_template import get_gitignore
|
|
10
|
+
from templates.readme_template import get_readme
|
|
11
|
+
from templates.requirements_template import get_requirements
|
|
12
|
+
|
|
13
|
+
from templates.license_template import get_license
|
|
14
|
+
from templates.changelog_template import get_changelog
|
|
15
|
+
from templates.env_template import (
|
|
16
|
+
get_env,
|
|
17
|
+
get_env_example,
|
|
18
|
+
)
|
|
19
|
+
from templates.pyproject_template import get_pyproject
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TemplateManager:
|
|
23
|
+
|
|
24
|
+
def get_app(
|
|
25
|
+
self,
|
|
26
|
+
project_name: str,
|
|
27
|
+
) -> str:
|
|
28
|
+
return get_app(project_name)
|
|
29
|
+
|
|
30
|
+
def get_gitignore(self) -> str:
|
|
31
|
+
return get_gitignore()
|
|
32
|
+
|
|
33
|
+
def get_readme(
|
|
34
|
+
self,
|
|
35
|
+
project_name: str,
|
|
36
|
+
) -> str:
|
|
37
|
+
return get_readme(project_name)
|
|
38
|
+
|
|
39
|
+
def get_requirements(self) -> str:
|
|
40
|
+
return get_requirements()
|
|
41
|
+
|
|
42
|
+
def get_license(self) -> str:
|
|
43
|
+
return get_license()
|
|
44
|
+
|
|
45
|
+
def get_changelog(self) -> str:
|
|
46
|
+
return get_changelog()
|
|
47
|
+
|
|
48
|
+
def get_env(self) -> str:
|
|
49
|
+
return get_env()
|
|
50
|
+
|
|
51
|
+
def get_env_example(self) -> str:
|
|
52
|
+
return get_env_example()
|
|
53
|
+
|
|
54
|
+
def get_pyproject(
|
|
55
|
+
self,
|
|
56
|
+
project_name: str,
|
|
57
|
+
) -> str:
|
|
58
|
+
return get_pyproject(project_name)
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Author : Rendy Zou
|
|
5
|
+
Module : VSCode Extensions Template
|
|
6
|
+
==================================================
|
|
7
|
+
|
|
8
|
+
Deskripsi:
|
|
9
|
+
- Merekomendasikan extension Visual Studio Code.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build() -> str:
|
|
16
|
+
|
|
17
|
+
extensions = {
|
|
18
|
+
"recommendations": [
|
|
19
|
+
"ms-python.python",
|
|
20
|
+
"ms-python.debugpy",
|
|
21
|
+
"ms-python.vscode-pylance",
|
|
22
|
+
"ms-python.black-formatter",
|
|
23
|
+
"charliermarsh.ruff",
|
|
24
|
+
"eamodio.gitlens"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return json.dumps(
|
|
29
|
+
extensions,
|
|
30
|
+
indent=4,
|
|
31
|
+
ensure_ascii=False,
|
|
32
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Author : Rendy Zou
|
|
5
|
+
Module : VSCode Launch Template
|
|
6
|
+
==================================================
|
|
7
|
+
|
|
8
|
+
Deskripsi:
|
|
9
|
+
- Template launch.json untuk Visual Studio Code.
|
|
10
|
+
- Menambahkan konfigurasi F5 hanya jika template memiliki entry point.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build(entry_point: str | None = "app.py") -> str:
|
|
17
|
+
|
|
18
|
+
configurations = []
|
|
19
|
+
|
|
20
|
+
if entry_point is not None:
|
|
21
|
+
configurations.append(
|
|
22
|
+
{
|
|
23
|
+
"name": f"Python: {entry_point}",
|
|
24
|
+
"type": "debugpy",
|
|
25
|
+
"request": "launch",
|
|
26
|
+
"program": f"${{workspaceFolder}}/{entry_point}",
|
|
27
|
+
"console": "integratedTerminal",
|
|
28
|
+
"justMyCode": True,
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
launch = {
|
|
33
|
+
"version": "0.2.0",
|
|
34
|
+
"configurations": configurations,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return json.dumps(
|
|
38
|
+
launch,
|
|
39
|
+
indent=4,
|
|
40
|
+
ensure_ascii=False,
|
|
41
|
+
)
|