From f8b5765c15643955aa4b2fd255ca0629ecf6ae8d Mon Sep 17 00:00:00 2001 From: h Date: Tue, 8 Sep 2026 19:15:15 +0200 Subject: [PATCH] Initial template --- .gitignore | 2 ++ README.md | 21 ++++++++++++++++ newproject | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 newproject diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4befed3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..601f99b --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# newproject + +One command that 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. + +```sh +newproject ~/projects/my-thing +``` + +Templates are read from `$TEMPLATES_DIR` (default `~/projects/templates`). Every part updates independently: + +```sh +copier update -a .copier-answers.backend.yml +``` + +Shapes it produces: + +- infrastructure only: root `docker-compose.yml`, `Makefile`, `.env`, optional `caddy/` +- backend with one or many services, each `python -m ` from one image +- backend and frontend as separate containers behind Caddy +- backend serving the frontend's static build from the api +- frontend only diff --git a/newproject b/newproject new file mode 100755 index 0000000..b8cf032 --- /dev/null +++ b/newproject @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +TEMPLATES="${TEMPLATES_DIR:-$HOME/projects/templates}" +dest="${1:?usage: newproject [copier options]}" +shift +copier_args=("$@") + +ask() { + local prompt="$1" default="$2" answer + read -r -p "$prompt [$default]: " answer + printf '%s' "${answer:-$default}" +} + +yes_no() { + local answer + answer=$(ask "$1" "$2") + [[ "$answer" =~ ^[Yy] ]] +} + +copy() { + uvx copier@latest copy --trust "${copier_args[@]}" "$@" +} + +project_name=$(ask "Project name" "$(basename "$dest")") +default_slug=$(printf '%s' "$project_name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/_/g') +project_slug=$(ask "Slug" "$default_slug") +author_name=$(git config user.name || true) +author_email=$(git config user.email || true) + +parts=() +yes_no "Backend" y && parts+=(backend) +yes_no "Frontend" y && parts+=(frontend) +with_caddy=false +yes_no "Caddy" y && with_caddy=true + +serve=bun +serve_spa=false +if [[ " ${parts[*]} " == *" frontend "* ]]; then + serve=$(ask "Frontend served by (bun | static | api)" "bun") + [[ "$serve" == api ]] && serve_spa=true +fi + +routes=() +[[ " ${parts[*]} " == *" backend "* ]] && routes+=(api) +[[ " ${parts[*]} " == *" frontend "* && "$serve" != api ]] && routes+=(frontend) + +json_list() { + local out="[" sep="" + for item in "$@"; do out+="$sep\"$item\""; sep=","; done + printf '%s]' "$out" +} + +mkdir -p "$dest" +copy -d "parts=$(json_list "${parts[@]}")" "$TEMPLATES/infra" "$dest" + +if [[ " ${parts[*]} " == *" backend "* ]]; then + copy -d "project_name=$project_name" -d "project_slug=$project_slug" \ + -d "author_name=$author_name" -d "author_email=$author_email" \ + -d "serve_spa=$serve_spa" "$TEMPLATES/backend-python" "$dest" +fi + +if [[ " ${parts[*]} " == *" frontend "* ]]; then + copy -d "project_name=$project_name" -d "project_slug=$project_slug" \ + -d "serve=$serve" "$TEMPLATES/frontend-svelte" "$dest" +fi + +if $with_caddy; then + copy -d "project_slug=$project_slug" -d "routes=$(json_list "${routes[@]}")" "$TEMPLATES/caddy" "$dest" +fi + +(cd "$dest" && make env)