From 9d9e52d2ddd3fbe9bec7847cac242e559cb85130 Mon Sep 17 00:00:00 2001 From: Dave O'Connor Date: Thu, 5 Mar 2026 17:33:43 -0800 Subject: [PATCH 1/5] Django template adjustments Add IDE debug configurations for PyCharm and VS Code Updated readme --- README.md | 73 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 3a52074..52be9fa 100644 --- a/README.md +++ b/README.md @@ -2,51 +2,63 @@

-**template-starter** is a generic Scaf base for creating new Scaf templates. +**django-template** is a Scaf template for creating production-ready Django web applications with modern tooling and best practices. -## What This Starter Provides +## What This Template Provides -Use this starter to generate new Scaf templates with validated Copier prompts, -GitHub/GitLab CI templates, optional semantic-release wiring (GitHub token or -GitHub App), optional gitleaks scanning, and generated `make`/`task`/`just` -`init`/`check` commands. +Generate Django projects with Docker Compose development environment, ASGI support, authentication, GraphQL API, optional Celery task queue, optional Sentry error tracking, and full CI/CD configuration for GitHub or GitLab. ## Features -- Copier prompts with input validation and conditional logic -- CI scaffolding for GitHub or GitLab -- Optional semantic-release setup and config -- Optional secret scanning in CI -- Template correctness CI (Copier render checks) -- Choice of local task runner (`make`, `task`, or `just`) with `init`/`check` -- Generated project docs for usage and upgrade flow -- Apache-2.0 license by default (license selection via Copier option planned) +- **Django 6.0** with ASGI support via Daphne +- **Docker Compose** development environment with PostgreSQL, Redis, and Mailpit +- **Authentication** via django-allauth with email verification +- **GraphQL API** with Strawberry and DataLoader +- **Optional Celery** for distributed task processing with beat scheduler +- **Optional Sentry** integration for error tracking +- **CI/CD** scaffolding for GitHub Actions or GitLab CI +- **Semantic Release** automation for versioning and changelogs +- **Secret Scanning** with gitleaks in CI +- **PyCharm & VS Code** debug configurations included +- **Nix** development environment with direnv integration +- **1Password CLI** integration for secrets management +- **Choice of task runner** (`make`, `task`, or `just`) +- Comprehensive documentation for development, debugging, and upgrades ## Copier Options -- `copier__ci_provider`: `github` or `gitlab` (only asked when repo setup is disabled; otherwise inferred from `copier__repo_provider`) -- `copier__enable_semantic_release`: include release automation -- `copier__github_semantic_release_auth`: `github_token` or `github_app` (GitHub + semantic-release only) -- `copier__enable_secret_scanning`: include gitleaks CI -- `copier__task_runner`: `make`, `task`, or `just` -- `copier__configure_repo`: enable repository remote setup +- `copier__project_name`: Human-readable project name +- `copier__project_slug`: Python identifier-style slug +- `copier__description`: Short project description +- `copier__author_name`: Author name +- `copier__domain_name`: Project domain +- `copier__email`: Author email +- `copier__timezone`: Default timezone (e.g., `America/New_York`) +- `copier__use_celery`: Enable Celery for task management +- `copier__use_sentry`: Enable Sentry error tracking +- `copier__task_runner`: Choose `make`, `task`, or `just` +- `copier__configure_repo`: Enable repository remote setup - `copier__repo_provider`: `github` or `gitlab` -- `copier__repo_org`: organization/group name -- `copier__repo_name`: repository name -- `copier__create_repo`: create provider repo automatically when missing (`gh` for GitHub, `glab` for GitLab) -- `copier__repo_visibility`: `private` or `public` for automatic repo creation +- `copier__repo_org`: Organization/group name +- `copier__repo_name`: Repository name +- `copier__create_repo`: Auto-create repository if missing +- `copier__repo_visibility`: `private` or `public` +- `copier__ci_provider`: CI provider (inferred from repo provider) +- `copier__enable_semantic_release`: Include semantic-release automation +- `copier__github_semantic_release_auth`: `github_token` or `github_app` +- `copier__enable_secret_scanning`: Include gitleaks secret scanning ## Quick Start ```bash # Local path -scaf my-template ./template-starter +scaf my-django-project ./django-template # Git URL -scaf my-template https://github.com/getscaf/template-starter.git +scaf my-django-project https://github.com/getscaf/django-template.git ``` -## Testing This Starter Template +## Testing This Template Run local render tests: @@ -60,8 +72,11 @@ CI runs the same command in `.github/workflows/template-render-tests.yaml`. Each generated project includes: -- `docs/using-template.md` -- `docs/upgrading.md` +- `docs/development.md` - Local development setup and workflows +- `docs/debug.md` - Debugging with PyCharm and VS Code +- `docs/project-overview.md` - Architecture and technology stack +- `docs/using-template.md` - Day-to-day template usage +- `docs/upgrading.md` - Copier update workflow - `docs/semantic-release-github.md` (when GitHub + semantic-release is enabled) - `docs/semantic-release-gitlab.md` (when GitLab + semantic-release is enabled) From ea8644904c15cb3cb02753e53172215c4ce0b75d Mon Sep 17 00:00:00 2001 From: Dave O'Connor Date: Tue, 10 Mar 2026 18:57:57 -0700 Subject: [PATCH 2/5] Add back gitignore for .idea and .vscode --- .idea/workspace.xml | 94 --------------------------------------------- 1 file changed, 94 deletions(-) delete mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index a76c0f1..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - { - "associatedIndex": 5 -} - - - - - - - - - - - - - - - - - - - 1772667700507 - - - - - - From 8f757abd8c104cc86b2c6aa54ff8dc090520e696 Mon Sep 17 00:00:00 2001 From: Dave O'Connor Date: Wed, 18 Mar 2026 14:49:03 -0700 Subject: [PATCH 3/5] feat: auto-update Python version on copier generation --- copier.yml | 1 + flake.nix | 19 +++++++------ template/backend/Dockerfile | 4 +-- template/docs/development.md | 2 +- template/docs/project-overview.md | 2 +- template/flake.nix | 12 ++++---- template/scripts/update-python-version.sh | 34 +++++++++++++++++++++++ 7 files changed, 55 insertions(+), 19 deletions(-) create mode 100755 template/scripts/update-python-version.sh diff --git a/copier.yml b/copier.yml index 2c7f973..557d073 100644 --- a/copier.yml +++ b/copier.yml @@ -6,6 +6,7 @@ _exclude: _tasks: - python tasks.py + - bash scripts/update-python-version.sh copier__project_name_raw: type: str diff --git a/flake.nix b/flake.nix index 40af9a0..3112d34 100644 --- a/flake.nix +++ b/flake.nix @@ -15,15 +15,16 @@ in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ + jq pre-commit - python313 # matches Dockerfile, due for upgrade? - python313.pkgs.black - python313.pkgs.boto3 - python313.pkgs.botocore - python313.pkgs.copier - python313.pkgs.isort - python313.pkgs.pip-tools - python313.pkgs.psycopg2 + python314 # matches Dockerfile, due for upgrade? + python314.pkgs.black + python314.pkgs.boto3 + python314.pkgs.botocore + python314.pkgs.copier + python314.pkgs.isort + python314.pkgs.pip-tools + python314.pkgs.psycopg2 uv ]; shellHook = '' @@ -32,7 +33,7 @@ pre-commit install fi if [ ! -d .venv ]; then - uv venv .venv --python python3.13 + uv venv .venv --python python3.14 . .venv/bin/activate # uv pip install -r requirements.txt -r requirements-dev.txt else diff --git a/template/backend/Dockerfile b/template/backend/Dockerfile index eba3639..1c79efb 100644 --- a/template/backend/Dockerfile +++ b/template/backend/Dockerfile @@ -1,5 +1,5 @@ # Build all of the dependencies then we will build the actual application image -FROM python:3.13-slim AS build +FROM python:__PYTHON_FULL_VERSION__-slim AS build ENV PYTHONUNBUFFERED=1 ENV PYTHONDONTWRITEBYTECODE=1 @@ -54,7 +54,7 @@ RUN set -x \ -r /tmp/requirements/tests.txt; fi # Now we can build the application image -FROM python:3.13-slim +FROM python:__PYTHON_FULL_VERSION__-slim ENV PYTHONUNBUFFERED=1 ENV PYTHONPATH="/app" diff --git a/template/docs/development.md b/template/docs/development.md index 1dbe7a7..0482573 100644 --- a/template/docs/development.md +++ b/template/docs/development.md @@ -8,7 +8,7 @@ To work with this project, you need to install: * [Docker Compose](https://docs.docker.com/compose/install/) - Multi-container orchestration (usually included with Docker Desktop) * [direnv](https://direnv.net/) - Environment variable management (recommended) * [1Password CLI](https://developer.1password.com/docs/cli/) - For secrets management (or configure alternative in `.envrc.local`) -* [Python 3.13+](https://www.python.org/downloads/) - For local development and dependency management +* [Python __PYTHON_VERSION__+](https://www.python.org/downloads/) - For local development and dependency management * [uv](https://github.com/astral-sh/uv) - Fast Python package installer ## Setup your environment diff --git a/template/docs/project-overview.md b/template/docs/project-overview.md index 3dc62e8..01cdd87 100644 --- a/template/docs/project-overview.md +++ b/template/docs/project-overview.md @@ -125,7 +125,7 @@ For more details, see the [Development Documentation](./development.md). ### Backend Features - **Django 6.0** - Latest stable Django framework -- **Python 3.13** - Modern Python runtime +- **Python __PYTHON_VERSION__** - Modern Python runtime - **PostgreSQL 16** - Robust relational database - **Django Allauth** - Complete authentication system - **Daphne ASGI** - Async-capable application server diff --git a/template/flake.nix b/template/flake.nix index 97ce34e..0317782 100644 --- a/template/flake.nix +++ b/template/flake.nix @@ -17,11 +17,11 @@ devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ pre-commit - python313 # matches Dockerfile, due for upgrade? - python313.pkgs.black - python313.pkgs.copier - python313.pkgs.isort - python313.pkgs.pip-tools + __PYTHON_NIX_ATTR__ + __PYTHON_NIX_ATTR__.pkgs.black + __PYTHON_NIX_ATTR__.pkgs.copier + __PYTHON_NIX_ATTR__.pkgs.isort + __PYTHON_NIX_ATTR__.pkgs.pip-tools uv ]; shellHook = '' @@ -32,7 +32,7 @@ fi if [ ! -d .venv ]; then - uv venv .venv --python python3.13 + uv venv .venv --python python__PYTHON_VERSION__ . .venv/bin/activate # uv pip install -r requirements.txt -r requirements-dev.txt else diff --git a/template/scripts/update-python-version.sh b/template/scripts/update-python-version.sh new file mode 100755 index 0000000..0217450 --- /dev/null +++ b/template/scripts/update-python-version.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Kept as a single script so the Docker Hub API is called once and the resolved +# version is shared across all file updates without needing a temp file. + +tags=$(curl -fsSL "https://registry.hub.docker.com/v2/repositories/library/python/tags?page_size=100") + +minor=$(echo "$tags" | jq -r ' + [.results[].name | select(test("^[0-9]+\\.[0-9]+-slim$")) | rtrimstr("-slim")] + | map(split(".") | map(tonumber)) + | max_by(.[0] * 1000 + .[1]) + | join(".") +') + +full=$(echo "$tags" | jq -r --arg minor "$minor" ' + [.results[].name | select(test("^" + $minor + "\\.[0-9]+-slim$")) | rtrimstr("-slim")] + | map(split(".") | map(tonumber)) + | if length > 0 then max_by(.[2]) | join(".") else $minor end +') + +nix_attr="python${minor//.}" + +echo "Updating Python version to ${minor} (Docker: ${full})" + +update() { if [ -f "$1" ]; then sed -i "${@:2}" "$1"; fi; } + +update backend/Dockerfile "s|__PYTHON_FULL_VERSION__|${full}|g" +update flake.nix "s|__PYTHON_NIX_ATTR__|${nix_attr}|g" +update flake.nix "s|python__PYTHON_VERSION__|python${minor}|g" +update docs/development.md "s|Python __PYTHON_VERSION__+|Python ${minor}+|g" +update docs/project-overview.md "s|\*\*Python __PYTHON_VERSION__\*\*|**Python ${minor}**|g" +update .gitlab-ci.yml "s|image: python:__PYTHON_VERSION__|image: python:${minor}|g" +update .github/workflows/template-correctness.yaml "s|python-version: '__PYTHON_VERSION__'|python-version: '${minor}'|g" From 52593260a9c6ebf5493cb60cfe78de4b479689ad Mon Sep 17 00:00:00 2001 From: Dave O'Connor Date: Wed, 18 Mar 2026 14:49:10 -0700 Subject: [PATCH 4/5] fix: use source_env_if_exists and nix instead of nix-shell --- .envrc | 8 +++----- template/.envrc | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.envrc b/.envrc index 0160f4a..dc7a4d4 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1,3 @@ -#!/use/bin/env bash - -if command -v nix-shell >/dev/null 2>&1; then - use flake -fi +#!/usr/bin/env bash +source_env_if_exists .envrc.local +command -v nix >/dev/null 2>&1 && use flake diff --git a/template/.envrc b/template/.envrc index 4740445..c782212 100644 --- a/template/.envrc +++ b/template/.envrc @@ -1,10 +1,8 @@ #!/usr/bin/env bash set -euo pipefail - # Source personal .envrc.local if it exists (loaded first, can override everything) -[ -f .envrc.local ] && source .envrc.local - -command -v nix-shell >/dev/null 2>&1 && use flake +source_env_if_exists .envrc.local +command -v nix >/dev/null 2>&1 && use flake # Read secret from 1Password if not already set (returns 0 if set, 1 if failed) read_secret() { @@ -22,7 +20,7 @@ read_secret DJANGO_SECRET_KEY "op://{{ copier__project_name }}/DJANGO_SECRET_KEY {%- if copier__mail_service == 'Mailgun' %} read_secret MAILGUN_API_KEY "op://{{ copier__project_name }}/MAILGUN_API_KEY/token" || failed="${failed}MAILGUN_API_KEY " {%- endif %} -[ -z "$failed" ] || { echo "Error: Failed to load secrets from 1Password: $failed" >&2; echo "Tip: Start 1Password or set them in .envrc.local to bypass 1Password" >&2; exit 1; } +[ -z "$failed" ] || { echo "Error: Failed to load secrets from 1Password: $failed" >&2; echo "Tip: Start 1Password and ensure the key is there, or set them in .envrc.local to bypass 1Password" >&2; exit 1; } # Environment configuration export USE_DOCKER=yes From 8867c06e14e7a5f324e452feddf90c6b0e5a696f Mon Sep 17 00:00:00 2001 From: Dave O'Connor Date: Thu, 19 Mar 2026 18:54:37 -0700 Subject: [PATCH 5/5] fix: install uv in template render test workflow --- .github/workflows/template-render-tests.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/template-render-tests.yaml b/.github/workflows/template-render-tests.yaml index 402c2c6..3f047e0 100644 --- a/.github/workflows/template-render-tests.yaml +++ b/.github/workflows/template-render-tests.yaml @@ -18,8 +18,13 @@ jobs: with: python-version: '3.11' + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Install copier - run: pip install copier + run: uv tool install copier - name: Run render tests + env: + UV_SYSTEM_PYTHON: "1" run: make test-template-render