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.
Files changed (137) hide show
  1. {newstack-0.1.0 → newstack-0.2.0}/CHANGELOG.md +19 -0
  2. {newstack-0.1.0 → newstack-0.2.0}/PKG-INFO +19 -12
  3. {newstack-0.1.0 → newstack-0.2.0}/README.md +18 -11
  4. {newstack-0.1.0 → newstack-0.2.0}/pyproject.toml +1 -1
  5. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/__init__.py +1 -1
  6. newstack-0.2.0/src/newstack/data/stacks/django/stack.json +59 -0
  7. newstack-0.2.0/src/newstack/data/stacks/django/template/.github/workflows/ci.yml +31 -0
  8. newstack-0.2.0/src/newstack/data/stacks/django/template/.gitignore +7 -0
  9. newstack-0.2.0/src/newstack/data/stacks/django/template/README.md +67 -0
  10. newstack-0.2.0/src/newstack/data/stacks/django/template/config/__init__.py +0 -0
  11. newstack-0.2.0/src/newstack/data/stacks/django/template/config/asgi.py +9 -0
  12. newstack-0.2.0/src/newstack/data/stacks/django/template/config/settings.py +82 -0
  13. newstack-0.2.0/src/newstack/data/stacks/django/template/config/urls.py +9 -0
  14. newstack-0.2.0/src/newstack/data/stacks/django/template/config/wsgi.py +9 -0
  15. newstack-0.2.0/src/newstack/data/stacks/django/template/core/__init__.py +0 -0
  16. newstack-0.2.0/src/newstack/data/stacks/django/template/core/apps.py +6 -0
  17. newstack-0.2.0/src/newstack/data/stacks/django/template/core/migrations/0001_initial.py +32 -0
  18. newstack-0.2.0/src/newstack/data/stacks/django/template/core/migrations/__init__.py +0 -0
  19. newstack-0.2.0/src/newstack/data/stacks/django/template/core/models.py +12 -0
  20. newstack-0.2.0/src/newstack/data/stacks/django/template/core/urls.py +8 -0
  21. newstack-0.2.0/src/newstack/data/stacks/django/template/core/views.py +22 -0
  22. newstack-0.2.0/src/newstack/data/stacks/django/template/manage.py +23 -0
  23. newstack-0.2.0/src/newstack/data/stacks/django/template/pyproject.toml +43 -0
  24. newstack-0.2.0/src/newstack/data/stacks/django/template/tests/test_models.py +20 -0
  25. newstack-0.2.0/src/newstack/data/stacks/django/template/tests/test_views.py +30 -0
  26. newstack-0.2.0/src/newstack/data/stacks/go-cli/stack.json +42 -0
  27. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/.github/workflows/ci.yml +21 -0
  28. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/.gitignore +15 -0
  29. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/README.md +55 -0
  30. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/go.mod +3 -0
  31. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/internal/app/app.go +69 -0
  32. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/internal/app/app_test.go +72 -0
  33. newstack-0.2.0/src/newstack/data/stacks/go-cli/template/main.go +12 -0
  34. newstack-0.2.0/src/newstack/data/stacks/react-vite-ts/template/LICENSE +21 -0
  35. newstack-0.2.0/src/newstack/data/stacks/rust-cli/stack.json +50 -0
  36. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/.github/workflows/ci.yml +32 -0
  37. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/.gitignore +1 -0
  38. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/Cargo.toml +17 -0
  39. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/LICENSE +21 -0
  40. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/README.md +46 -0
  41. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/src/lib.rs +120 -0
  42. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/src/main.rs +20 -0
  43. newstack-0.2.0/src/newstack/data/stacks/rust-cli/template/tests/cli.rs +46 -0
  44. newstack-0.2.0/src/newstack/data/stacks/static-site/template/LICENSE +21 -0
  45. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/stack.json +15 -0
  46. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.github/workflows/ci.yml +31 -0
  47. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.gitignore +23 -0
  48. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/.npmrc +1 -0
  49. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/LICENSE +21 -0
  50. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/README.md +71 -0
  51. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/package.json +34 -0
  52. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/app.d.ts +13 -0
  53. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/app.html +13 -0
  54. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/Counter.svelte +7 -0
  55. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/Counter.test.ts +17 -0
  56. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/assets/favicon.svg +1 -0
  57. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/lib/index.ts +1 -0
  58. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+layout.svelte +12 -0
  59. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+layout.ts +1 -0
  60. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/routes/+page.svelte +35 -0
  61. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/src/vitest-setup.ts +5 -0
  62. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/static/robots.txt +3 -0
  63. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/tsconfig.json +7 -0
  64. newstack-0.2.0/src/newstack/data/stacks/svelte-kit-ts/template/vite.config.ts +26 -0
  65. newstack-0.2.0/tests/test_docs.py +38 -0
  66. {newstack-0.1.0 → newstack-0.2.0}/.gitignore +0 -0
  67. {newstack-0.1.0 → newstack-0.2.0}/LICENSE +0 -0
  68. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/__main__.py +0 -0
  69. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/cli.py +0 -0
  70. {newstack-0.1.0/src/newstack/data/stacks/fastapi → newstack-0.2.0/src/newstack/data/stacks/django}/template/LICENSE +0 -0
  71. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/stack.json +0 -0
  72. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/.github/workflows/ci.yml +0 -0
  73. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/.gitignore +0 -0
  74. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/Dockerfile +0 -0
  75. {newstack-0.1.0/src/newstack/data/stacks/node-express-ts → newstack-0.2.0/src/newstack/data/stacks/fastapi}/template/LICENSE +0 -0
  76. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/README.md +0 -0
  77. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/pyproject.toml +0 -0
  78. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/src/{{package_name}}/__init__.py +0 -0
  79. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/src/{{package_name}}/main.py +0 -0
  80. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/fastapi/template/tests/test_api.py +0 -0
  81. {newstack-0.1.0/src/newstack/data/stacks/python-cli → newstack-0.2.0/src/newstack/data/stacks/go-cli}/template/LICENSE +0 -0
  82. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/stack.json +0 -0
  83. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/.github/workflows/ci.yml +0 -0
  84. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/.gitignore +0 -0
  85. {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
  86. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/README.md +0 -0
  87. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/package.json +0 -0
  88. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/src/app.ts +0 -0
  89. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/src/server.ts +0 -0
  90. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/tests/app.test.ts +0 -0
  91. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/node-express-ts/template/tsconfig.json +0 -0
  92. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/stack.json +0 -0
  93. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/.github/workflows/ci.yml +0 -0
  94. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/.gitignore +0 -0
  95. {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
  96. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/README.md +0 -0
  97. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/pyproject.toml +0 -0
  98. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/__init__.py +0 -0
  99. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/__main__.py +0 -0
  100. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/src/{{package_name}}/cli.py +0 -0
  101. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-cli/template/tests/test_cli.py +0 -0
  102. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/stack.json +0 -0
  103. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/.github/workflows/ci.yml +0 -0
  104. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/.gitignore +0 -0
  105. {newstack-0.1.0/src/newstack/data/stacks/static-site → newstack-0.2.0/src/newstack/data/stacks/python-lib}/template/LICENSE +0 -0
  106. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/README.md +0 -0
  107. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/pyproject.toml +0 -0
  108. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/src/{{package_name}}/__init__.py +0 -0
  109. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/src/{{package_name}}/core.py +0 -0
  110. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/python-lib/template/tests/test_core.py +0 -0
  111. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/stack.json +0 -0
  112. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/.github/workflows/ci.yml +0 -0
  113. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/.gitignore +0 -0
  114. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/README.md +0 -0
  115. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/index.html +0 -0
  116. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/package.json +0 -0
  117. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/App.test.tsx +0 -0
  118. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/App.tsx +0 -0
  119. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/main.tsx +0 -0
  120. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/src/styles.css +0 -0
  121. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/tsconfig.json +0 -0
  122. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/react-vite-ts/template/vite.config.ts +0 -0
  123. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/stack.json +0 -0
  124. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/.github/workflows/deploy-pages.yml +0 -0
  125. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/.gitignore +0 -0
  126. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/README.md +0 -0
  127. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/index.html +0 -0
  128. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/main.js +0 -0
  129. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/styles.css +0 -0
  130. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/data/stacks/static-site/template/tests/site.test.mjs +0 -0
  131. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/registry.py +0 -0
  132. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/render.py +0 -0
  133. {newstack-0.1.0 → newstack-0.2.0}/src/newstack/verify.py +0 -0
  134. {newstack-0.1.0 → newstack-0.2.0}/tests/test_cli.py +0 -0
  135. {newstack-0.1.0 → newstack-0.2.0}/tests/test_registry.py +0 -0
  136. {newstack-0.1.0 → newstack-0.2.0}/tests/test_render.py +0 -0
  137. {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.1.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
  [![CI](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml/badge.svg)](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml)
28
+ [![PyPI](https://img.shields.io/pypi/v/newstack)](https://pypi.org/project/newstack/)
28
29
  ![python](https://img.shields.io/badge/python-3.10%2B-blue)
29
30
  ![dependencies](https://img.shields.io/badge/runtime%20dependencies-0-brightgreen)
30
31
  [![license](https://img.shields.io/badge/license-MIT-blue)](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
- uv tool install git+https://github.com/DawnofGenX/newstack
68
- # or, without installing:
69
- uvx --from git+https://github.com/DawnofGenX/newstack newstack list
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
- | `static-site` | HTML/CSS/JS page with a GitHub Pages deploy workflow | `node` |
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
- fastapi PASS 1.0s 3 step(s)
119
- node-express-ts PASS 2.2s 3 step(s)
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.4s 3 step(s)
122
- react-vite-ts PASS 3.2s 4 step(s)
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: Go service, Rust CLI, Django, Vue, SvelteKit.
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
  [![CI](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml/badge.svg)](https://github.com/DawnofGenX/newstack/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/newstack)](https://pypi.org/project/newstack/)
4
5
  ![python](https://img.shields.io/badge/python-3.10%2B-blue)
5
6
  ![dependencies](https://img.shields.io/badge/runtime%20dependencies-0-brightgreen)
6
7
  [![license](https://img.shields.io/badge/license-MIT-blue)](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
- uv tool install git+https://github.com/DawnofGenX/newstack
44
- # or, without installing:
45
- uvx --from git+https://github.com/DawnofGenX/newstack newstack list
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
- | `static-site` | HTML/CSS/JS page with a GitHub Pages deploy workflow | `node` |
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
- fastapi PASS 1.0s 3 step(s)
95
- node-express-ts PASS 2.2s 3 step(s)
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.4s 3 step(s)
98
- react-vite-ts PASS 3.2s 4 step(s)
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: Go service, Rust CLI, Django, Vue, SvelteKit.
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.1.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"
@@ -1,4 +1,4 @@
1
1
  """newstack — scaffold a new project on any popular stack, proven green in CI."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
4
4
  __all__ = ["__version__"]
@@ -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,7 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ db.sqlite3
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .DS_Store
@@ -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).
@@ -0,0 +1,9 @@
1
+ """ASGI config for {{ title }}."""
2
+
3
+ import os
4
+
5
+ from django.core.asgi import get_asgi_application
6
+
7
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
8
+
9
+ application = get_asgi_application()
@@ -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"
@@ -0,0 +1,9 @@
1
+ """URL configuration for {{ title }}."""
2
+
3
+ from django.contrib import admin
4
+ from django.urls import include, path
5
+
6
+ urlpatterns = [
7
+ path("admin/", admin.site.urls),
8
+ path("", include("core.urls")),
9
+ ]
@@ -0,0 +1,9 @@
1
+ """WSGI config for {{ title }}."""
2
+
3
+ import os
4
+
5
+ from django.core.wsgi import get_wsgi_application
6
+
7
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
8
+
9
+ application = get_wsgi_application()
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class CoreConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "core"
@@ -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
+ ]
@@ -0,0 +1,12 @@
1
+ from django.db import models
2
+
3
+
4
+ class Item(models.Model):
5
+ name = models.CharField(max_length=100)
6
+ created = models.DateTimeField(auto_now_add=True)
7
+
8
+ class Meta:
9
+ ordering = ("-created",)
10
+
11
+ def __str__(self):
12
+ return self.name
@@ -0,0 +1,8 @@
1
+ from django.urls import path
2
+
3
+ from . import views
4
+
5
+ urlpatterns = [
6
+ path("health", views.health, name="health"),
7
+ path("items", views.items, name="items"),
8
+ ]
@@ -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"]