Skip to content

Commit e57b644

Browse files
committed
release: v0.16.0 — shimkit framework django
Third framework recipe under the existing `framework` parent. Modelled on Laravel (v0.7.0) + Symfony (v0.14.0); adjusted for Django's conventions: Aspect Laravel Symfony Django ────────────────────────────────────────────────────────────────── Writable tree storage + b/c var media + staticfiles App secret APP_KEY base64 APP_SECRET hex SECRET_KEY 50-char alphabet Env file .env .env.local .env (django-environ) Console artisan bin/console manage.py Console runtime php php python Scheduler kit cron-install — migrate (sugar) Surface (one fewer command than Laravel — no cron-install since Django has no built-in scheduler, same as Symfony): shimkit framework django perms PATH [--group G] (MODERATE) shimkit framework django env PATH [--name N] [--debug/--no-debug] [--db D] (MODERATE) shimkit framework django migrate PATH shimkit framework django manage --project PATH [--in-container [--stack NAME]] -- <args> `migrate` is sugar for `manage migrate --no-input` — the most- common manage.py invocation in any CI / deploy script. `env` writes django-environ / python-decouple compatible `.env` with SECRET_KEY + DEBUG + ALLOWED_HOSTS + DATABASE_URL + EMAIL_BACKEND + a commented REDIS_URL hint pointing at `shimkit db redis up` (v0.15.0). SECRET_KEY is hand-rolled to match Django's documented alphabet (`[a-zA-Z0-9!@#$%^&*(-_=+)]`, 50 chars). shimkit doesn't depend on Django being installed to scaffold the file. Default DB is **postgres** (Django's most-shipped pairing — unlike Laravel's mysql default). Implementation: - New `tools/framework/django/` sub-tree (manager + commands + __init__) mirroring laravel/ and symfony/. - `framework_app` registers `django_app` alongside `laravel_app` + `symfony_app`. Sub-app surface unchanged for existing users. - `FrameworkDjangoConfig` pydantic model + corresponding defaults.json block with `web_group`, `file_mode`, `dir_mode`, `writable_dirs`, `default_debug`. Tests: 30 new in test_tools_framework_django.py (1086 → 1116 total). _generate_secret_key (length / Django alphabet / uniqueness across 100 calls), platform gating, perms (path refusal / dry-run / media + staticfiles targeting / chgrp skip on missing group / missing-writable warning / failed-step JSON), env (overwrite refusal / SECRET_KEY shape + DATABASE_URL postgres default / DEBUG True default / --no-debug / DATABASE_URL mysql + mariadb / ALLOWED_HOSTS + EMAIL_BACKEND present / Redis hint commented-out / dry-run no-write / uniqueness across two scaffolds), manage (host happy path / missing-python exits 69 / no-args refusal / missing-manage.py refusal / --in-container delegates to StackManager), migrate (wraps manage migrate --no-input), command surface (framework --help lists all three; django --help lists all four subcommands). Gates: pytest 1116 passed, ruff clean, mypy strict clean. No new optional dependency extras.
1 parent 0a23e18 commit e57b644

13 files changed

Lines changed: 1472 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,56 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
66

77
## [Unreleased]
88

9+
## [0.16.0] — 2026-05-16
10+
11+
### Added
12+
13+
- **`shimkit framework django`** — third framework recipe.
14+
Modelled on Laravel + Symfony with Django-specific
15+
conventions:
16+
- `perms PATH [--group G]` (MODERATE) — fixes `media/` +
17+
`staticfiles/` permissions.
18+
- `env PATH [--name N] [--debug/--no-debug] [--db D]` (MODERATE)
19+
— scaffolds `.env` with `SECRET_KEY` (Django's 50-char
20+
alphabet) + `DATABASE_URL` (dj-database-url / django-environ
21+
convention). Refuses to overwrite. Default DB is **postgres**
22+
(Django's most common pairing); mysql + mariadb also
23+
supported.
24+
- `migrate PATH` — sugar for `manage migrate --no-input`.
25+
- `manage -- <args>` — passthrough to `python manage.py`.
26+
Host or `--in-container` via `shimkit stack lemp`.
27+
- `tools.framework.django` config block with `web_group`,
28+
`file_mode`, `dir_mode`, `writable_dirs`, and `default_debug`.
29+
30+
### Tests
31+
32+
- 30 new tests in `tests/test_tools_framework_django.py` (1086 →
33+
1116 total). _generate_secret_key (length / Django alphabet /
34+
uniqueness across 100 calls), platform gating, perms (path
35+
refusal / dry-run / media + staticfiles targeting / chgrp skip
36+
on missing group / missing-writable warning / failed-step JSON),
37+
env (overwrite refusal / SECRET_KEY shape + DATABASE_URL
38+
postgres default / DEBUG True default / --no-debug flag /
39+
DATABASE_URL mysql + mariadb / ALLOWED_HOSTS + EMAIL_BACKEND
40+
present / Redis hint commented-out / dry-run no-write /
41+
uniqueness across two scaffolds), manage (host happy path /
42+
missing-python exits 69 / no-args refusal / missing-manage.py
43+
refusal / --in-container delegates to StackManager), migrate
44+
(wraps manage migrate --no-input), command surface (framework
45+
--help lists django; django --help lists all four
46+
subcommands).
47+
48+
### Notes
49+
50+
Third framework recipe. Same shape pattern as Laravel + Symfony:
51+
perms / env / passthrough + one framework-specific shortcut
52+
(`migrate` for Django, `cache-clear` for Symfony,
53+
`cron-install` for Laravel). No cron-install for Django —
54+
same reason as Symfony, no built-in scheduler.
55+
56+
Gates: pytest 1116 passed, ruff clean, mypy strict clean. No new
57+
optional dependency extras.
58+
959
## [0.15.0] — 2026-05-16
1060

1161
### Added

docs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ shimkit is a collection. Each tool gets its own page:
6161
- **[`shimkit framework symfony`](tools/framework-symfony.md)**
6262
Symfony helpers: perms (`var/`), `.env.local` scaffold with
6363
`APP_SECRET`, cache-clear, `bin/console` passthrough.
64+
- **[`shimkit framework django`](tools/framework-django.md)**
65+
Django helpers: perms (`media/` + `staticfiles/`), `.env`
66+
scaffold with `SECRET_KEY` + django-environ-style
67+
`DATABASE_URL`, migrate, `manage.py` passthrough.
6468

6569
Top-level utilities (not tools):
6670

@@ -94,6 +98,8 @@ Top-level utilities (not tools):
9498

9599
Per-version, user-facing summaries (newest first):
96100

101+
- **[`v0.16.0`](release-notes/v0.16.0.md)** — `shimkit framework
102+
django` third framework recipe.
97103
- **[`v0.15.0`](release-notes/v0.15.0.md)**`shimkit db redis`
98104
sixth engine. `Engine.up_command()` for argv-passed config.
99105
- **[`v0.14.0`](release-notes/v0.14.0.md)** — `shimkit framework

docs/release-notes/v0.16.0.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# shimkit 0.16.0
2+
3+
`shimkit framework django` — third framework recipe under the
4+
existing `framework` parent. For the full machine-readable
5+
changelog, see [`CHANGELOG.md`](../../CHANGELOG.md).
6+
7+
---
8+
9+
## TL;DR
10+
11+
```
12+
shimkit framework django perms PATH [--group G] # MODERATE
13+
shimkit framework django env PATH [--name N] [--debug] [--db D] # MODERATE
14+
shimkit framework django migrate PATH
15+
shimkit framework django manage -- <args>
16+
```
17+
18+
Same shape pattern as Laravel + Symfony. Adjusted for Django's
19+
conventions: writable `media/` + `staticfiles/`, `SECRET_KEY`
20+
(Django alphabet), `manage.py` (root-level), Python runtime.
21+
22+
---
23+
24+
## Differences across the three recipes
25+
26+
| Aspect | Laravel | Symfony | Django |
27+
|--------|---------|---------|--------|
28+
| Writable tree | `storage/` + `bootstrap/cache/` | `var/` | `media/` + `staticfiles/` |
29+
| App secret | `APP_KEY=base64:...` | `APP_SECRET=...` (hex) | `SECRET_KEY=...` (50-char alphabet) |
30+
| Env file | `.env` | `.env.local` | `.env` (django-environ/decouple) |
31+
| Console | `artisan` | `bin/console` | `manage.py` |
32+
| Console runtime | `php` | `php` | `python` |
33+
| Scheduler shortcut | `cron-install` (every minute) || `migrate` (sugar for `manage migrate`) |
34+
35+
---
36+
37+
## `env` shape
38+
39+
`.env` is the standard for django-environ / python-decouple
40+
projects. shimkit writes:
41+
42+
```ini
43+
# Django local environment overrides.
44+
# Read by django-environ / python-decouple from .env at
45+
# project root. Production should override via real secrets
46+
# management.
47+
48+
SECRET_KEY=<50 chars from Django's documented alphabet>
49+
DEBUG=True
50+
ALLOWED_HOSTS="localhost,127.0.0.1"
51+
52+
DATABASE_URL="postgres://root:shimkit-dev@127.0.0.1:15432/<dirname>"
53+
54+
# Cache + queue (pair with `shimkit db redis up`).
55+
# REDIS_URL="redis://default:shimkit-dev@127.0.0.1:16379/0"
56+
57+
# Email — defaults to the console backend; override in prod.
58+
EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
59+
```
60+
61+
`SECRET_KEY` matches what
62+
`django.core.management.utils.get_random_secret_key()` would
63+
emit — 50 chars from `[a-zA-Z0-9!@#$%^&*(-_=+)]`. shimkit doesn't
64+
depend on Django being installed to scaffold the file.
65+
66+
Default DB is **postgres** (most-shipped pairing). Override:
67+
68+
```bash
69+
shimkit framework django env --yes --db mysql ./my-app
70+
shimkit framework django env --yes --db mariadb ./my-app
71+
```
72+
73+
The `# REDIS_URL=...` comment is a hint pointing at
74+
`shimkit db redis up` (v0.15.0). Uncomment to wire Redis.
75+
76+
---
77+
78+
## End-to-end example
79+
80+
```bash
81+
# 1. Database + optional cache
82+
shimkit db postgres up
83+
shimkit db redis up
84+
85+
# 2. Scaffold env + fix perms
86+
shimkit framework django env --yes --db postgres ./my-app
87+
shimkit framework django perms --yes ./my-app
88+
89+
# 3. Migrate + superuser
90+
shimkit framework django migrate ./my-app
91+
shimkit framework django manage --project ./my-app -- createsuperuser
92+
93+
# 4. Dev server
94+
shimkit framework django manage --project ./my-app -- runserver
95+
```
96+
97+
---
98+
99+
## Why no `cron-install`
100+
101+
Same reason as Symfony (v0.14.0): Django doesn't ship a built-in
102+
scheduler analogous to Laravel's `schedule:run`. Application-
103+
specific cron entries go via `shimkit cron add` directly:
104+
105+
```bash
106+
shimkit cron add --yes \
107+
--name django-nightly \
108+
--schedule "0 3 * * *" \
109+
--cmd "cd /var/www/my-app && python manage.py my-nightly-job >> /dev/null 2>&1"
110+
```
111+
112+
Django-Q / Celery / huey are the in-process alternatives — shimkit
113+
doesn't bind to any one of them.
114+
115+
---
116+
117+
## Stats
118+
119+
- Tests: 1086 → 1116 (+30)
120+
- Gates: pytest, ruff, mypy strict — all green
121+
- New optional extras: 0
122+
123+
---
124+
125+
## Upgrading
126+
127+
```bash
128+
uv tool upgrade shimkit
129+
pipx upgrade shimkit
130+
```
131+
132+
Laravel + Symfony users see no behavioural change. To start
133+
using the Django recipe, just invoke
134+
`shimkit framework django --help`.

docs/tools/framework-django.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# shimkit framework django
2+
3+
Django-specific helpers. Third recipe under the `framework`
4+
parent. Same shape as the Laravel + Symfony recipes; adjusted
5+
for Django's conventions.
6+
7+
## Commands
8+
9+
| Command | Purpose |
10+
|---------|---------|
11+
| `shimkit framework django` | Menu. |
12+
| `shimkit framework django perms PATH [--group G]` | MODERATE. Fix `media/` + `staticfiles/` permissions. |
13+
| `shimkit framework django env PATH [--name N] [--debug/--no-debug] [--db D]` | MODERATE. Scaffold `.env` with `SECRET_KEY` + `DATABASE_URL`. |
14+
| `shimkit framework django migrate PATH` | Wraps `python manage.py migrate --no-input`. |
15+
| `shimkit framework django manage -- <args>` | Passthrough to `python manage.py`. |
16+
17+
Universal flags before the subcommand (`--quiet`, `--verbose`,
18+
`--log-file`, `--no-color`, `--color`, `--no-input`); per-command
19+
flags after (`--json`, `--dry-run`, `--yes`, `--force`).
20+
21+
## Differences from Laravel + Symfony
22+
23+
| Aspect | Laravel | Symfony | Django |
24+
|--------|---------|---------|--------|
25+
| Writable tree | `storage/` + `bootstrap/cache/` | `var/` | `media/` + `staticfiles/` |
26+
| App secret | `APP_KEY=base64:...` | `APP_SECRET=...` (hex) | `SECRET_KEY=...` (Django alphabet) |
27+
| Env file | `.env` (gitignored) | `.env.local` (gitignored) | `.env` (django-environ / decouple convention) |
28+
| Console | `artisan` (root) | `bin/console` | `manage.py` (root) |
29+
| Console runtime | `php` | `php` | `python` |
30+
| Scheduler | `schedule:run` every minute | none — use `shimkit cron add` | none — use `shimkit cron add` |
31+
32+
## perms
33+
34+
`media/` (user uploads) and `staticfiles/` (collectstatic target)
35+
are the canonical writable trees in modern Django layouts.
36+
`staticfiles/` typically doesn't exist on a fresh project until
37+
the first `collectstatic` — shimkit warns about that and runs
38+
the global `chmod` passes regardless.
39+
40+
Group detection: `getent group <name>` on Linux, `dscl . -read
41+
/Groups/<name>` on macOS, `grp.getgrnam()` Python fallback.
42+
43+
```bash
44+
shimkit framework django perms --yes ./my-django-app
45+
shimkit framework django perms --yes --group staff ./my-django-app # macOS
46+
shimkit framework django perms --dry-run ./my-django-app
47+
```
48+
49+
## env
50+
51+
Writes `.env` with a generated `SECRET_KEY` + `DATABASE_URL` +
52+
sensible dev defaults. **Refuses to overwrite** an existing
53+
`.env`. The format is django-environ / python-decouple
54+
compatible: `KEY=value` lines. `DATABASE_URL` follows the
55+
Heroku / dj-database-url convention.
56+
57+
`SECRET_KEY` is 50 chars from Django's documented alphabet —
58+
matches what `django.core.management.utils.get_random_secret_key()`
59+
would emit, but shimkit doesn't depend on Django being installed
60+
to scaffold the file.
61+
62+
Default DB engine is **postgres** (Django's most-shipped pairing).
63+
Override with `--db mysql` or `--db mariadb`.
64+
65+
| `--db` | URL prefix | Port |
66+
|------------|------------|------|
67+
| `postgres` (default) | `postgres://` | 15432 |
68+
| `mysql` | `mysql://` | 13306 |
69+
| `mariadb` | `mysql://` | 13307 |
70+
71+
```bash
72+
shimkit db postgres up
73+
shimkit framework django env --yes --db postgres ./my-app
74+
75+
# DEBUG=False for a more prod-like local
76+
shimkit framework django env --yes --no-debug ./my-app
77+
```
78+
79+
The env file also lists a commented-out `REDIS_URL` line as a
80+
hint pointing the user at `shimkit db redis up` (v0.15.0+).
81+
82+
## migrate
83+
84+
Sugar for `manage migrate --no-input`:
85+
86+
```bash
87+
shimkit framework django migrate ./my-app
88+
shimkit framework django migrate --in-container --stack myapp ./my-app
89+
```
90+
91+
## manage
92+
93+
Generic passthrough to `python manage.py`. Host execution by
94+
default — preflights `python` via
95+
`shimkit.core.version.preflight`, exits 69 with the remediation
96+
hint when `python` is missing. `--in-container` routes through
97+
`shimkit stack lemp`'s php-fpm container (which has `python`
98+
installed via the upstream PHP-FPM image's base layers).
99+
100+
```bash
101+
shimkit framework django manage --project ./my-app -- runserver 0.0.0.0:8000
102+
shimkit framework django manage --project ./my-app -- createsuperuser
103+
shimkit framework django manage --project ./my-app -- collectstatic --no-input
104+
```
105+
106+
`--project` defaults to the current working directory.
107+
108+
## End-to-end example
109+
110+
```bash
111+
# 1. Database
112+
shimkit db postgres up
113+
114+
# 2. Optional: Redis cache / Channels
115+
shimkit db redis up
116+
117+
# 3. Scaffold env + fix perms
118+
shimkit framework django env --yes --db postgres ./my-app
119+
shimkit framework django perms --yes ./my-app
120+
121+
# 4. Migrate + create superuser
122+
shimkit framework django migrate ./my-app
123+
shimkit framework django manage --project ./my-app -- createsuperuser
124+
125+
# 5. Run the dev server
126+
shimkit framework django manage --project ./my-app -- runserver
127+
```
128+
129+
## Configuration
130+
131+
```json
132+
{
133+
"tools": {
134+
"framework": {
135+
"django": {
136+
"web_group": "www-data",
137+
"file_mode": "664",
138+
"dir_mode": "775",
139+
"writable_dirs": ["media", "staticfiles"],
140+
"default_debug": true
141+
}
142+
}
143+
}
144+
}
145+
```
146+
147+
Override `web_group` per host. Add `writable_dirs` entries if
148+
your app writes outside `media/` + `staticfiles/`.
149+
150+
## Exit codes
151+
152+
| Code | Meaning |
153+
|-----:|---------|
154+
| 0 | success |
155+
| 1 | bad path, missing `manage.py`, refusing overwrite, prompt cancelled |
156+
| 2 | Typer usage error |
157+
| 69 | `EX_UNAVAILABLE` — wrong platform or missing `python` for `manage`/`migrate` |
158+
| 130 | SIGINT |
159+
160+
## Platform support
161+
162+
| Platform | Status |
163+
|----------|--------|
164+
| macOS | full. Dev account typically owns the project, so `chgrp` is no-op'd unless `--group` is passed. |
165+
| Linux | full. Targets `www-data` by default; override with `--group`. |
166+
| WSL | works (Linux path). |
167+
| Windows | out of charter (use WSL). |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "shimkit"
7-
version = "0.15.0"
7+
version = "0.16.0"
88
description = "A toolkit of developer utilities — Java version manager, shell upgrader, and more. Python tools, shimmed by bash."
99
readme = "README.md"
1010
license = { file = "LICENSE" }

src/shimkit/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
Python tools, shimmed by bash.
44
"""
55

6-
__version__ = "0.15.0"
6+
__version__ = "0.16.0"
77
__all__ = ["__version__"]

0 commit comments

Comments
 (0)