Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/github_scaffolding_generator/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
import os
import sys

if sys.platform == "win32":
os.system("") # enables ANSI/VT100 sequences on Windows terminal
Expand All @@ -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")

Expand Down
22 changes: 12 additions & 10 deletions src/github_scaffolding_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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))

Expand Down Expand Up @@ -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)
Expand All @@ -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":
Expand All @@ -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",
Expand All @@ -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"),
Expand All @@ -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",
Expand Down
14 changes: 7 additions & 7 deletions src/github_scaffolding_generator/validator.py
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down Expand Up @@ -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()
Expand All @@ -45,15 +45,15 @@ 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:
raise ValidationError(f"Invalid visibility. Must be one of: {', '.join(VALID_VISIBILITIES)}")
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(",")]
Expand All @@ -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),
Expand Down
15 changes: 7 additions & 8 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand 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
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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


# ---------------------------------------------------------------------------
Expand Down