diff --git a/src/github_scaffolding_generator/cli.py b/src/github_scaffolding_generator/cli.py index ff69e3c..348cf0b 100644 --- a/src/github_scaffolding_generator/cli.py +++ b/src/github_scaffolding_generator/cli.py @@ -1,5 +1,5 @@ -import sys import os +import sys if sys.platform == "win32": os.system("") # enables ANSI/VT100 sequences on Windows terminal @@ -9,9 +9,10 @@ sys.stdin.reconfigure(encoding="utf-8") import typer -from .validator import validate_all, ValidationError + from .generator import Generator from .stacks import STACKS, get_stack_labels +from .validator import ValidationError, validate_all app = typer.Typer(name="github-scaffolding-generator", rich_markup_mode="markdown") diff --git a/src/github_scaffolding_generator/generator.py b/src/github_scaffolding_generator/generator.py index 1f6a6d5..de98f7b 100644 --- a/src/github_scaffolding_generator/generator.py +++ b/src/github_scaffolding_generator/generator.py @@ -11,10 +11,12 @@ import os import sys -from datetime import date +from datetime import datetime, timezone from pathlib import Path +from typing import ClassVar + from jinja2 import Environment, FileSystemLoader -from typing import Dict, List + from .stacks import STACK_BY_LABEL TEMPLATE_DIR = Path(__file__).parent / "templates" @@ -30,8 +32,8 @@ def __init__(self, output_dir: str = "output"): lstrip_blocks=True, ) - def generate(self, context: Dict) -> List[str]: - today = date.today() + def generate(self, context: dict) -> list[str]: + today = datetime.now(tz=timezone.utc).date() context.setdefault("today", today.isoformat()) context.setdefault("year", str(today.year)) @@ -67,7 +69,7 @@ def generate(self, context: Dict) -> List[str]: return generated_files - def _render_template_map(self, base_dir: Path, template_map: Dict[str, str], context: Dict) -> List[str]: + def _render_template_map(self, base_dir: Path, template_map: dict[str, str], context: dict) -> list[str]: files = [] for output_name, template_name in template_map.items(): content = self.env.get_template(template_name).render(**context) @@ -77,7 +79,7 @@ def _render_template_map(self, base_dir: Path, template_map: Dict[str, str], con files.append(str(out_path)) return files - def _generate_community_standards(self, project_dir: Path, context: Dict) -> List[str]: + def _generate_community_standards(self, project_dir: Path, context: dict) -> list[str]: project_type = context.get("project_type", "") if project_type == "powershell-script": @@ -97,7 +99,7 @@ def _generate_community_standards(self, project_dir: Path, context: Dict) -> Lis } return self._render_template_map(project_dir, template_map, context) - def _generate_github_files(self, project_dir: Path, context: Dict) -> List[str]: + def _generate_github_files(self, project_dir: Path, context: dict) -> list[str]: template_map = { ".github/CODEOWNERS": "github/CODEOWNERS.j2", ".github/dependabot.yml": "github/dependabot.yml.j2", @@ -107,13 +109,13 @@ def _generate_github_files(self, project_dir: Path, context: Dict) -> List[str]: } return self._render_template_map(project_dir, template_map, context) - def _generate_ci(self, project_dir: Path, context: Dict) -> List[str]: + def _generate_ci(self, project_dir: Path, context: dict) -> list[str]: template_map = { ".github/workflows/ci.yml": "ci/ci.yml.j2", } return self._render_template_map(project_dir, template_map, context) - _MANIFEST_TEMPLATES: Dict[str, tuple] = { + _MANIFEST_TEMPLATES: ClassVar[dict[str, tuple]] = { "Python": ("pyproject.toml", "pyproject.toml.j2"), "Node": ("package.json", "package.json.j2"), "Go": ("go.mod", "go.mod.j2"), @@ -125,7 +127,7 @@ def _generate_ci(self, project_dir: Path, context: Dict) -> List[str]: "PowerShell": ("{project_name}.psd1", "module.psd1.j2"), } - def _generate_project_files(self, project_dir: Path, context: Dict) -> List[str]: + def _generate_project_files(self, project_dir: Path, context: dict) -> list[str]: template_map = { ".gitignore": "gitignore.j2", ".gitattributes": "gitattributes.j2", diff --git a/src/github_scaffolding_generator/validator.py b/src/github_scaffolding_generator/validator.py index e835ed0..7dde8a2 100644 --- a/src/github_scaffolding_generator/validator.py +++ b/src/github_scaffolding_generator/validator.py @@ -1,6 +1,6 @@ """Validation module for project inputs.""" -from typing import Optional + from .stacks import get_all_valid_labels VALID_PROJECT_TYPES = ["cli", "library", "webapp", "github-action", "docs", "monorepo", "powershell-script", "shell-script"] @@ -35,7 +35,7 @@ def validate_stack(stack: str) -> str: return stack -def validate_license(license_name: Optional[str]) -> str: +def validate_license(license_name: str | None) -> str: if license_name is None: return "MIT" normalized = license_name.lower() @@ -45,7 +45,7 @@ def validate_license(license_name: Optional[str]) -> str: raise ValidationError(f"Invalid license. Must be one of: {', '.join(VALID_LICENSES)}") -def validate_visibility(visibility: Optional[str]) -> str: +def validate_visibility(visibility: str | None) -> str: if visibility is None: return "public" if visibility not in VALID_VISIBILITIES: @@ -53,7 +53,7 @@ def validate_visibility(visibility: Optional[str]) -> str: return visibility -def validate_ci_targets(ci_targets: Optional[str]) -> list: +def validate_ci_targets(ci_targets: str | None) -> list: if ci_targets is None: return ["lint", "test"] targets = [t.strip() for t in ci_targets.split(",")] @@ -67,9 +67,9 @@ def validate_all( project_name: str, project_type: str, stack: str, - license_name: Optional[str] = None, - visibility: Optional[str] = None, - ci_targets: Optional[str] = None, + license_name: str | None = None, + visibility: str | None = None, + ci_targets: str | None = None, ) -> dict: return { "project_name": validate_project_name(project_name), diff --git a/tests/test_cli.py b/tests/test_cli.py index cc97354..4efa452 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,6 +2,8 @@ import pytest +from github_scaffolding_generator.cli import ACTIVITY_MAPPING, CI_MAP, LICENSE_MAP +from github_scaffolding_generator.generator import Generator from github_scaffolding_generator.validator import ( ValidationError, validate_all, @@ -12,9 +14,6 @@ validate_stack, validate_visibility, ) -from github_scaffolding_generator.generator import Generator -from github_scaffolding_generator.cli import ACTIVITY_MAPPING, LICENSE_MAP, CI_MAP - # --------------------------------------------------------------------------- # validate_project_name @@ -333,12 +332,12 @@ def test_generator_injects_today_and_year(): with tempfile.TemporaryDirectory() as tmpdir: gen = Generator(output_dir=tmpdir) gen.generate(_make_context()) + from datetime import datetime, timezone from pathlib import Path - from datetime import date changelog = Path(tmpdir) / "test-project" / "CHANGELOG.md" content = changelog.read_text(encoding="utf-8") - assert date.today().isoformat() in content - assert str(date.today().year) in content + assert datetime.now(tz=timezone.utc).date().isoformat() in content + assert str(datetime.now(tz=timezone.utc).date().year) in content def test_generator_injects_project_name_in_templates(): @@ -471,11 +470,11 @@ def test_generator_readme_contains_today_date(): with tempfile.TemporaryDirectory() as tmpdir: gen = Generator(output_dir=tmpdir) gen.generate(_make_context()) + from datetime import datetime, timezone from pathlib import Path - from datetime import date readme = Path(tmpdir) / "test-project" / "README.md" content = readme.read_text(encoding="utf-8") - assert date.today().isoformat() in content + assert datetime.now(tz=timezone.utc).date().isoformat() in content # ---------------------------------------------------------------------------