newstack 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {newstack-0.1.0 → newstack-0.2.0}/CHANGELOG.md +19 -0
- {newstack-0.1.0 → newstack-0.2.0}/PKG-INFO +19 -12
- {newstack-0.1.0 → newstack-0.2.0}/README.md +18 -11
- {newstack-0.1.0 → newstack-0.2.0}/pyproject.toml +1 -1
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/__init__.py +1 -1
- newstack-0.2.0/src/newstack/data/stacks/django/stack.json +59 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/.github/workflows/ci.yml +31 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/.gitignore +7 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/README.md +67 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/config/__init__.py +0 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/config/asgi.py +9 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/config/settings.py +82 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/config/urls.py +9 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/config/wsgi.py +9 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/__init__.py +0 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/apps.py +6 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/migrations/0001_initial.py +32 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/migrations/__init__.py +0 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/models.py +12 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/urls.py +8 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/core/views.py +22 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/manage.py +23 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/pyproject.toml +43 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/tests/test_models.py +20 -0
- newstack-0.2.0/src/newstack/data/stacks/django/template/tests/test_views.py +30 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/stack.json +42 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/.github/workflows/ci.yml +21 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/.gitignore +15 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/README.md +55 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/go.mod +3 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/internal/app/app.go +69 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/internal/app/app_test.go +72 -0
- newstack-0.2.0/src/newstack/data/stacks/go-cli/template/main.go +12 -0
- newstack-0.2.0/src/newstack/data/stacks/react-vite-ts/template/LICENSE +21 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/stack.json +50 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/.github/workflows/ci.yml +32 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/.gitignore +1 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/Cargo.toml +17 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/LICENSE +21 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/README.md +46 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/src/lib.rs +120 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/src/main.rs +20 -0
- newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/tests/cli.rs +46 -0
- newstack-0.2.0/src/newstack/data/stacks/static-site/template/LICENSE +21 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/stack.json +15 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.github/workflows/ci.yml +31 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.gitignore +23 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.npmrc +1 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/LICENSE +21 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/README.md +71 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/package.json +34 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/app.d.ts +13 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/app.html +13 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/Counter.svelte +7 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/Counter.test.ts +17 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/assets/favicon.svg +1 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/index.ts +1 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+layout.svelte +12 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+layout.ts +1 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+page.svelte +35 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/vitest-setup.ts +5 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/static/robots.txt +3 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/tsconfig.json +7 -0
- newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/vite.config.ts +26 -0
- newstack-0.2.0/tests/test_docs.py +38 -0
- {newstack-0.1.0 → newstack-0.2.0}/.gitignore +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/__main__.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/cli.py +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/fastapi → newstack-0.2.0/src/newstack/data/stacks/django}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/.github/workflows/ci.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/.gitignore +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/Dockerfile +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/node-express-ts → newstack-0.2.0/src/newstack/data/stacks/fastapi}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/pyproject.toml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/src/{{package_name}}/__init__.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/src/{{package_name}}/main.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/tests/test_api.py +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/python-cli → newstack-0.2.0/src/newstack/data/stacks/go-cli}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/.github/workflows/ci.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/.gitignore +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/python-lib → newstack-0.2.0/src/newstack/data/stacks/node-express-ts}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/package.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/src/app.ts +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/src/server.ts +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/tests/app.test.ts +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/tsconfig.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/.github/workflows/ci.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/.gitignore +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/react-vite-ts → newstack-0.2.0/src/newstack/data/stacks/python-cli}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/pyproject.toml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/__init__.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/__main__.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/cli.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/tests/test_cli.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/.github/workflows/ci.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/.gitignore +0 -0
- {newstack-0.1.0/src/newstack/data/stacks/static-site → newstack-0.2.0/src/newstack/data/stacks/python-lib}/template/LICENSE +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/pyproject.toml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/src/{{package_name}}/__init__.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/src/{{package_name}}/core.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/tests/test_core.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/.github/workflows/ci.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/.gitignore +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/index.html +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/package.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/App.test.tsx +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/App.tsx +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/main.tsx +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/styles.css +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/tsconfig.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/vite.config.ts +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/stack.json +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/.github/workflows/deploy-pages.yml +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/.gitignore +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/README.md +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/index.html +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/main.js +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/styles.css +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/tests/site.test.mjs +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/registry.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/render.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/src/newstack/verify.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/tests/test_cli.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/tests/test_registry.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/tests/test_render.py +0 -0
- {newstack-0.1.0 → newstack-0.2.0}/tests/test_stacks.py +0 -0
|
@@ -6,6 +6,25 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.0] - 2026-11-02
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Four new stacks, all verified the same way as the original six:
|
|
14
|
+
- `go-cli` — stdlib `flag` parsing, table tests, vet/test/build gates.
|
|
15
|
+
- `rust-cli` — std-only parsing, lib+bin split, fmt/clippy/test/release gates.
|
|
16
|
+
- `django` — Django 6.1, config/core split, SQLite, pytest-django, and a
|
|
17
|
+
migration-drift step that fails when a model changes without a migration.
|
|
18
|
+
- `svelte-kit-ts` — SvelteKit 3 with Svelte 5 runes, Vitest, static adapter build.
|
|
19
|
+
- CI runs one job per stack (ten in total), with Go and Rust toolchains installed only
|
|
20
|
+
for the jobs that need them.
|
|
21
|
+
- Docs drift tests: the README table and the CI matrix must both list every registered
|
|
22
|
+
stack, and the README may not hardcode a stack count.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Install instructions now point at PyPI (`uvx newstack`, `uv tool install newstack`).
|
|
27
|
+
|
|
9
28
|
## [0.1.0] - 2026-11-02
|
|
10
29
|
|
|
11
30
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: newstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Scaffold a new project on any popular stack — every generated starter is proven green in CI.
|
|
5
5
|
Project-URL: Homepage, https://github.com/DawnofGenX/newstack
|
|
6
6
|
Project-URL: Repository, https://github.com/DawnofGenX/newstack
|
|
@@ -25,6 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
# newstack
|
|
26
26
|
|
|
27
27
|
[](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml)
|
|
28
|
+
[](https://pypi.org/project/newstack/)
|
|
28
29
|

|
|
29
30
|

|
|
30
31
|
[](LICENSE)
|
|
@@ -61,12 +62,10 @@ first day of work.
|
|
|
61
62
|
|
|
62
63
|
## Install
|
|
63
64
|
|
|
64
|
-
Not on PyPI yet — install straight from the repository:
|
|
65
|
-
|
|
66
65
|
```bash
|
|
67
|
-
|
|
68
|
-
# or
|
|
69
|
-
|
|
66
|
+
uvx newstack list # run without installing
|
|
67
|
+
uv tool install newstack # or put it on your PATH
|
|
68
|
+
pipx install newstack # any of the usual runners works
|
|
70
69
|
```
|
|
71
70
|
|
|
72
71
|
From a clone:
|
|
@@ -101,9 +100,13 @@ directory to CI-ready project.
|
|
|
101
100
|
| `python-lib` | src layout, hatchling build, pytest, ruff, 4-version CI matrix | `uv` |
|
|
102
101
|
| `python-cli` | console entry point on argparse, zero runtime deps, tested CLI | `uv` |
|
|
103
102
|
| `fastapi` | typed JSON API, pydantic models, TestClient tests, Dockerfile | `uv` |
|
|
103
|
+
| `django` | Django 6.1 app: config/core split, SQLite, pytest-django, migration drift check | `uv` |
|
|
104
104
|
| `react-vite-ts` | React 19 + Vite + TS, Vitest + Testing Library, typecheck + build | `node`, `npm` |
|
|
105
105
|
| `node-express-ts` | Express 5 API, typed handlers, supertest tests | `node`, `npm` |
|
|
106
|
-
| `
|
|
106
|
+
| `svelte-kit-ts` | SvelteKit + TS, Svelte 5 runes component, Vitest, static adapter build | `node`, `npm` |
|
|
107
|
+
| `go-cli` | stdlib-only Go CLI: `flag` parsing, table tests, vet/test/build gates | `go` |
|
|
108
|
+
| `rust-cli` | std-only Rust CLI: lib+bin split, fmt/clippy/test/release-build gates | `cargo` |
|
|
109
|
+
| `static-site` | HTML/CSS/JS page, `node:test` suite, GitHub Pages deploy workflow | `node` |
|
|
107
110
|
|
|
108
111
|
Each generated project ships its own `.github/workflows/` that is ready to run, plus a
|
|
109
112
|
README, an MIT licence and a `.gitignore`.
|
|
@@ -115,12 +118,16 @@ verification as a first-class command:
|
|
|
115
118
|
|
|
116
119
|
```console
|
|
117
120
|
$ newstack check --all
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
django PASS 1.5s 5 step(s)
|
|
122
|
+
fastapi PASS 1.4s 3 step(s)
|
|
123
|
+
go-cli PASS 0.3s 3 step(s)
|
|
124
|
+
node-express-ts PASS 6.1s 3 step(s)
|
|
120
125
|
python-cli PASS 0.5s 4 step(s)
|
|
121
|
-
python-lib PASS 0.
|
|
122
|
-
react-vite-ts PASS
|
|
126
|
+
python-lib PASS 0.5s 3 step(s)
|
|
127
|
+
react-vite-ts PASS 5.5s 4 step(s)
|
|
128
|
+
rust-cli PASS 0.5s 4 step(s)
|
|
123
129
|
static-site PASS 0.1s 3 step(s)
|
|
130
|
+
svelte-kit-ts PASS 7.1s 4 step(s)
|
|
124
131
|
```
|
|
125
132
|
|
|
126
133
|
(Real timings from a warm package cache — a cold run takes about a minute because
|
|
@@ -189,7 +196,7 @@ manifest format, no fork required.
|
|
|
189
196
|
## Roadmap
|
|
190
197
|
|
|
191
198
|
- `newstack update` — re-apply upstream template changes to an existing project.
|
|
192
|
-
- More stacks:
|
|
199
|
+
- More stacks: Vue, Next.js, Flask.
|
|
193
200
|
- `--license apache-2.0|bsd-3` and other variable choices beyond the current defaults.
|
|
194
201
|
|
|
195
202
|
## Licence
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# newstack
|
|
2
2
|
|
|
3
3
|
[](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/newstack/)
|
|
4
5
|

|
|
5
6
|

|
|
6
7
|
[](LICENSE)
|
|
@@ -37,12 +38,10 @@ first day of work.
|
|
|
37
38
|
|
|
38
39
|
## Install
|
|
39
40
|
|
|
40
|
-
Not on PyPI yet — install straight from the repository:
|
|
41
|
-
|
|
42
41
|
```bash
|
|
43
|
-
|
|
44
|
-
# or
|
|
45
|
-
|
|
42
|
+
uvx newstack list # run without installing
|
|
43
|
+
uv tool install newstack # or put it on your PATH
|
|
44
|
+
pipx install newstack # any of the usual runners works
|
|
46
45
|
```
|
|
47
46
|
|
|
48
47
|
From a clone:
|
|
@@ -77,9 +76,13 @@ directory to CI-ready project.
|
|
|
77
76
|
| `python-lib` | src layout, hatchling build, pytest, ruff, 4-version CI matrix | `uv` |
|
|
78
77
|
| `python-cli` | console entry point on argparse, zero runtime deps, tested CLI | `uv` |
|
|
79
78
|
| `fastapi` | typed JSON API, pydantic models, TestClient tests, Dockerfile | `uv` |
|
|
79
|
+
| `django` | Django 6.1 app: config/core split, SQLite, pytest-django, migration drift check | `uv` |
|
|
80
80
|
| `react-vite-ts` | React 19 + Vite + TS, Vitest + Testing Library, typecheck + build | `node`, `npm` |
|
|
81
81
|
| `node-express-ts` | Express 5 API, typed handlers, supertest tests | `node`, `npm` |
|
|
82
|
-
| `
|
|
82
|
+
| `svelte-kit-ts` | SvelteKit + TS, Svelte 5 runes component, Vitest, static adapter build | `node`, `npm` |
|
|
83
|
+
| `go-cli` | stdlib-only Go CLI: `flag` parsing, table tests, vet/test/build gates | `go` |
|
|
84
|
+
| `rust-cli` | std-only Rust CLI: lib+bin split, fmt/clippy/test/release-build gates | `cargo` |
|
|
85
|
+
| `static-site` | HTML/CSS/JS page, `node:test` suite, GitHub Pages deploy workflow | `node` |
|
|
83
86
|
|
|
84
87
|
Each generated project ships its own `.github/workflows/` that is ready to run, plus a
|
|
85
88
|
README, an MIT licence and a `.gitignore`.
|
|
@@ -91,12 +94,16 @@ verification as a first-class command:
|
|
|
91
94
|
|
|
92
95
|
```console
|
|
93
96
|
$ newstack check --all
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
django PASS 1.5s 5 step(s)
|
|
98
|
+
fastapi PASS 1.4s 3 step(s)
|
|
99
|
+
go-cli PASS 0.3s 3 step(s)
|
|
100
|
+
node-express-ts PASS 6.1s 3 step(s)
|
|
96
101
|
python-cli PASS 0.5s 4 step(s)
|
|
97
|
-
python-lib PASS 0.
|
|
98
|
-
react-vite-ts PASS
|
|
102
|
+
python-lib PASS 0.5s 3 step(s)
|
|
103
|
+
react-vite-ts PASS 5.5s 4 step(s)
|
|
104
|
+
rust-cli PASS 0.5s 4 step(s)
|
|
99
105
|
static-site PASS 0.1s 3 step(s)
|
|
106
|
+
svelte-kit-ts PASS 7.1s 4 step(s)
|
|
100
107
|
```
|
|
101
108
|
|
|
102
109
|
(Real timings from a warm package cache — a cold run takes about a minute because
|
|
@@ -165,7 +172,7 @@ manifest format, no fork required.
|
|
|
165
172
|
## Roadmap
|
|
166
173
|
|
|
167
174
|
- `newstack update` — re-apply upstream template changes to an existing project.
|
|
168
|
-
- More stacks:
|
|
175
|
+
- More stacks: Vue, Next.js, Flask.
|
|
169
176
|
- `--license apache-2.0|bsd-3` and other variable choices beyond the current defaults.
|
|
170
177
|
|
|
171
178
|
## Licence
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "newstack"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Scaffold a new project on any popular stack — every generated starter is proven green in CI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "django",
|
|
3
|
+
"title": "Django app",
|
|
4
|
+
"language": "python",
|
|
5
|
+
"blurb": "Django 6 JSON API: core app, SQLite, pytest-django, ruff, CI matrix",
|
|
6
|
+
"requires": [
|
|
7
|
+
"uv",
|
|
8
|
+
"git"
|
|
9
|
+
],
|
|
10
|
+
"default_description": "A Django web application.",
|
|
11
|
+
"next_steps": [
|
|
12
|
+
"cd {path}",
|
|
13
|
+
"uv sync",
|
|
14
|
+
"uv run python manage.py migrate",
|
|
15
|
+
"uv run python manage.py runserver"
|
|
16
|
+
],
|
|
17
|
+
"verify": [
|
|
18
|
+
{
|
|
19
|
+
"name": "install",
|
|
20
|
+
"cmd": "uv venv -q .venv && uv pip install -q --python .venv/bin/python -e . pytest pytest-django ruff",
|
|
21
|
+
"tools": [
|
|
22
|
+
"uv"
|
|
23
|
+
],
|
|
24
|
+
"timeout": 900
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "check",
|
|
28
|
+
"cmd": ".venv/bin/python manage.py check",
|
|
29
|
+
"tools": [
|
|
30
|
+
"uv"
|
|
31
|
+
],
|
|
32
|
+
"timeout": 300
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "migrations",
|
|
36
|
+
"cmd": ".venv/bin/python manage.py makemigrations --check --dry-run",
|
|
37
|
+
"tools": [
|
|
38
|
+
"uv"
|
|
39
|
+
],
|
|
40
|
+
"timeout": 300
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "ruff",
|
|
44
|
+
"cmd": ".venv/bin/ruff check .",
|
|
45
|
+
"tools": [
|
|
46
|
+
"uv"
|
|
47
|
+
],
|
|
48
|
+
"timeout": 300
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "pytest",
|
|
52
|
+
"cmd": ".venv/bin/python -m pytest -q",
|
|
53
|
+
"tools": [
|
|
54
|
+
"uv"
|
|
55
|
+
],
|
|
56
|
+
"timeout": 600
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.12", "3.13"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync --group dev
|
|
26
|
+
- name: Lint
|
|
27
|
+
run: uv run ruff check .
|
|
28
|
+
- name: Check for missing migrations
|
|
29
|
+
run: uv run python manage.py makemigrations --check --dry-run
|
|
30
|
+
- name: Test
|
|
31
|
+
run: uv run pytest
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# {{ title }}
|
|
2
|
+
|
|
3
|
+
{{ description }}
|
|
4
|
+
|
|
5
|
+
A [Django](https://www.djangoproject.com/) 6 application with a JSON API app (`core`), SQLite
|
|
6
|
+
storage, pytest-django tests and ruff linting.
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Python 3.12+
|
|
11
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
uv run python manage.py migrate
|
|
18
|
+
uv run python manage.py runserver
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The server listens on http://127.0.0.1:8000/.
|
|
22
|
+
|
|
23
|
+
## Endpoints
|
|
24
|
+
|
|
25
|
+
| Method | Path | Response |
|
|
26
|
+
| ------ | --------- | ---------------------------------------------------- |
|
|
27
|
+
| GET | `/health` | `{"status": "ok"}` |
|
|
28
|
+
| GET | `/items` | JSON array of `{"id", "name", "created"}` records |
|
|
29
|
+
| GET | `/admin/` | Django admin (create a superuser with `createsuperuser`) |
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
Settings live in `config/settings.py` and read two environment variables:
|
|
34
|
+
|
|
35
|
+
| Variable | Default | Notes |
|
|
36
|
+
| ------------------- | -------------------------------------- | ------------------------------ |
|
|
37
|
+
| `DJANGO_SECRET_KEY` | dev-only insecure key | **required** in production |
|
|
38
|
+
| `DJANGO_DEBUG` | `1` | set to `0` for production |
|
|
39
|
+
|
|
40
|
+
`ALLOWED_HOSTS` is `["*"]` for local development; tighten it before deploying.
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv sync
|
|
46
|
+
uv run python manage.py makemigrations # after changing core/models.py
|
|
47
|
+
uv run pytest
|
|
48
|
+
uv run ruff check .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Project layout
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
config/ settings, root URLconf, WSGI/ASGI entry points
|
|
55
|
+
core/ app: Item model, JSON views, migrations
|
|
56
|
+
tests/ pytest-django tests
|
|
57
|
+
manage.py Django management entry point
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## CI
|
|
61
|
+
|
|
62
|
+
`.github/workflows/ci.yml` runs lint, a migration-drift check and the test suite on Python 3.12
|
|
63
|
+
and 3.13 for every push to `main` and every pull request.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT — see [LICENSE](LICENSE).
|
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Django settings for {{ title }}.
|
|
2
|
+
|
|
3
|
+
Development defaults are used when the environment variables are not set.
|
|
4
|
+
Never ship the fallback SECRET_KEY to production — set DJANGO_SECRET_KEY instead.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
11
|
+
|
|
12
|
+
SECRET_KEY = os.environ.get(
|
|
13
|
+
"DJANGO_SECRET_KEY",
|
|
14
|
+
"django-insecure-dev-only-key-do-not-use-in-production",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
DEBUG = os.environ.get("DJANGO_DEBUG", "1") == "1"
|
|
18
|
+
|
|
19
|
+
ALLOWED_HOSTS = ["*"]
|
|
20
|
+
|
|
21
|
+
INSTALLED_APPS = [
|
|
22
|
+
"django.contrib.admin",
|
|
23
|
+
"django.contrib.auth",
|
|
24
|
+
"django.contrib.contenttypes",
|
|
25
|
+
"django.contrib.sessions",
|
|
26
|
+
"django.contrib.messages",
|
|
27
|
+
"django.contrib.staticfiles",
|
|
28
|
+
"core",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
MIDDLEWARE = [
|
|
32
|
+
"django.middleware.security.SecurityMiddleware",
|
|
33
|
+
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
34
|
+
"django.middleware.common.CommonMiddleware",
|
|
35
|
+
"django.middleware.csrf.CsrfViewMiddleware",
|
|
36
|
+
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
37
|
+
"django.contrib.messages.middleware.MessageMiddleware",
|
|
38
|
+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
ROOT_URLCONF = "config.urls"
|
|
42
|
+
|
|
43
|
+
TEMPLATES = [
|
|
44
|
+
{
|
|
45
|
+
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
46
|
+
"DIRS": [],
|
|
47
|
+
"APP_DIRS": True,
|
|
48
|
+
"OPTIONS": {
|
|
49
|
+
"context_processors": [
|
|
50
|
+
"django.template.context_processors.request",
|
|
51
|
+
"django.contrib.auth.context_processors.auth",
|
|
52
|
+
"django.contrib.messages.context_processors.messages",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
WSGI_APPLICATION = "config.wsgi.application"
|
|
59
|
+
ASGI_APPLICATION = "config.asgi.application"
|
|
60
|
+
|
|
61
|
+
DATABASES = {
|
|
62
|
+
"default": {
|
|
63
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
64
|
+
"NAME": BASE_DIR / "db.sqlite3",
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
AUTH_PASSWORD_VALIDATORS = [
|
|
69
|
+
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
|
70
|
+
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
|
71
|
+
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|
72
|
+
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
LANGUAGE_CODE = "en-us"
|
|
76
|
+
TIME_ZONE = "UTC"
|
|
77
|
+
USE_I18N = True
|
|
78
|
+
USE_TZ = True
|
|
79
|
+
|
|
80
|
+
STATIC_URL = "static/"
|
|
81
|
+
|
|
82
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Generated by Django 6.1.2 on 2026-11-02 02:15
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
initial = True
|
|
9
|
+
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.CreateModel(
|
|
14
|
+
name="Item",
|
|
15
|
+
fields=[
|
|
16
|
+
(
|
|
17
|
+
"id",
|
|
18
|
+
models.BigAutoField(
|
|
19
|
+
auto_created=True,
|
|
20
|
+
primary_key=True,
|
|
21
|
+
serialize=False,
|
|
22
|
+
verbose_name="ID",
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
("name", models.CharField(max_length=100)),
|
|
26
|
+
("created", models.DateTimeField(auto_now_add=True)),
|
|
27
|
+
],
|
|
28
|
+
options={
|
|
29
|
+
"ordering": ("-created",),
|
|
30
|
+
},
|
|
31
|
+
),
|
|
32
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from django.http import JsonResponse
|
|
2
|
+
from django.views.decorators.http import require_GET
|
|
3
|
+
|
|
4
|
+
from .models import Item
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@require_GET
|
|
8
|
+
def health(request):
|
|
9
|
+
return JsonResponse({"status": "ok"})
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@require_GET
|
|
13
|
+
def items(request):
|
|
14
|
+
payload = [
|
|
15
|
+
{
|
|
16
|
+
"id": item.pk,
|
|
17
|
+
"name": item.name,
|
|
18
|
+
"created": item.created.isoformat(),
|
|
19
|
+
}
|
|
20
|
+
for item in Item.objects.all()
|
|
21
|
+
]
|
|
22
|
+
return JsonResponse(payload, safe=False)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Django's command-line utility for administrative tasks."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
"""Run administrative tasks."""
|
|
10
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
11
|
+
try:
|
|
12
|
+
from django.core.management import execute_from_command_line
|
|
13
|
+
except ImportError as exc:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
16
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
17
|
+
"forget to activate a virtual environment?"
|
|
18
|
+
) from exc
|
|
19
|
+
execute_from_command_line(sys.argv)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ npm_name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "{{ description }}"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "{{ author }}", email = "{{ email }}" }]
|
|
14
|
+
dependencies = ["django>=6.1"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Framework :: Django",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = ["pytest>=8", "pytest-django>=4.14", "ruff>=0.6"]
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["config", "core"]
|
|
27
|
+
|
|
28
|
+
[tool.pytest.ini_options]
|
|
29
|
+
testpaths = ["tests"]
|
|
30
|
+
pythonpath = ["."]
|
|
31
|
+
DJANGO_SETTINGS_MODULE = "config.settings"
|
|
32
|
+
addopts = "-q"
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 100
|
|
36
|
+
target-version = "py312"
|
|
37
|
+
# migrations are generated code: their module-level lists trip RUF012
|
|
38
|
+
extend-exclude = ["*/migrations/*"]
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
# Pin the rule set: ruff's defaults shift between releases, which makes the same
|
|
42
|
+
# template lint clean in one environment and dirty in another.
|
|
43
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from datetime import timedelta
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
|
|
6
|
+
from core.models import Item
|
|
7
|
+
|
|
8
|
+
pytestmark = pytest.mark.django_db
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_str_returns_name():
|
|
12
|
+
assert str(Item(name="Widget")) == "Widget"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_default_ordering_is_newest_first():
|
|
16
|
+
older = Item.objects.create(name="older")
|
|
17
|
+
Item.objects.create(name="newer")
|
|
18
|
+
Item.objects.filter(pk=older.pk).update(created=timezone.now() - timedelta(days=1))
|
|
19
|
+
|
|
20
|
+
assert [item.name for item in Item.objects.all()] == ["newer", "older"]
|