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
-
-
- 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