From 048f29f21e36153f82bd5481b67030d6f2d95c93 Mon Sep 17 00:00:00 2001 From: h Date: Sat, 19 Sep 2026 22:03:02 +0200 Subject: [PATCH] feat(new): other backend or frontend from the blank template --- README.md | 4 +++- templates.py | 24 ++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3de5f7c..b49c02f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # newproject -Composes a project from the templates in this organization: `infra` at the root, then `backend-python`, `frontend-svelte` and `caddy` on top, each with its own answers file in `.copier/`. +Composes a project from the templates in this organization: `infra` at the root, then `backend-python`, `frontend-svelte`, `blank` and `caddy` on top, each with its own answers file in `.copier/`. ```sh source ~/projects/templates/newproject/shell.sh @@ -15,6 +15,8 @@ updateproject backend # one part newproject ~/projects/my-thing --parts backend,frontend,caddy --backend python --frontend svelte --serve bun --defaults ``` +`other` as the backend or the frontend renders the `blank` template: the part's compose files, Makefile and `.env.example` with no code, for a stack that has no template of its own, next to any other part. + One part alone is rendered at the root of the project; two parts live in `backend/` and `frontend/`. After rendering, `make env` creates `.env` and every `docker-compose.override.yml` from their examples. `updateproject` needs a clean working tree and commits each part's update separately, so the next part starts from a clean tree again; conflicts stay in those commits for you to resolve. Templates are cloned from `https://git.kotikot.com/templates` unless `TEMPLATES_DIR` points at a directory with local checkouts; `TEMPLATES_REF=HEAD` renders their working trees instead of the latest tag. ## Renovate diff --git a/templates.py b/templates.py index ea957d8..8b0bbf8 100644 --- a/templates.py +++ b/templates.py @@ -14,8 +14,9 @@ from copier import run_copy, run_update SOURCE = os.environ.get("TEMPLATES_DIR", "https://git.kotikot.com/templates") REF = os.environ.get("TEMPLATES_REF") -BACKENDS = {"python": "backend-python"} -FRONTENDS = {"svelte": "frontend-svelte"} +BLANK = "blank" +BACKENDS = {"python": "backend-python", "other": BLANK} +FRONTENDS = {"svelte": "frontend-svelte", "other": BLANK} SERVE = ["bun", "static", "api"] @@ -65,25 +66,32 @@ def new(args: argparse.Namespace) -> None: if "frontend" in parts: frontend = args.frontend or ask_select("Frontend", list(FRONTENDS), "svelte") serve = "bun" - if frontend: + if frontend and FRONTENDS[frontend] != BLANK: options = SERVE if backend else SERVE[:-1] serve = args.serve or ask_select("Frontend served by", options, "bun") stack = [p for p in ("backend", "frontend") if p in parts] single = len(stack) == 1 routes = [r for r, ok in (("api", backend), ("frontend", frontend and serve != "api")) if ok] - def copy(name: str, data: dict) -> None: - run_copy(template(name), dest, data=data, unsafe=True, defaults=args.defaults, vcs_ref=REF) + def copy(name: str, data: dict, answers_file: str | None = None) -> None: + run_copy(template(name), dest, data=data, answers_file=answers_file, unsafe=True, + defaults=args.defaults, vcs_ref=REF) + + def copy_part(part: str, name: str, data: dict) -> None: + if name == BLANK: + copy(name, {**identity, "layout": layout, "part": part}, f".copier/{part}.yml") + else: + copy(name, {**identity, "layout": layout, **data}) dest.mkdir(parents=True, exist_ok=True) identity = {"project_name": name, "project_slug": slug} copy("infra", {"project_name": name, "layout": "single" if single else "parts", "parts": [] if single else stack}) layout = "root" if single else "part" if backend: - copy(BACKENDS[backend], {**identity, "layout": layout, "serve_spa": serve == "api", - "author_name": git_config("user.name"), "author_email": git_config("user.email")}) + copy_part("backend", BACKENDS[backend], {"serve_spa": serve == "api", "author_name": git_config("user.name"), + "author_email": git_config("user.email")}) if frontend: - copy(FRONTENDS[frontend], {**identity, "layout": layout, "serve": serve}) + copy_part("frontend", FRONTENDS[frontend], {"serve": serve}) if "caddy" in parts: copy("caddy", {"project_slug": slug, "routes": routes}) if (dest / "Makefile").is_file():