newstack 0.1.0__py3-none-any.whl
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/__init__.py +4 -0
- newstack/__main__.py +8 -0
- newstack/cli.py +387 -0
- newstack/data/stacks/fastapi/stack.json +42 -0
- newstack/data/stacks/fastapi/template/.github/workflows/ci.yml +30 -0
- newstack/data/stacks/fastapi/template/.gitignore +11 -0
- newstack/data/stacks/fastapi/template/Dockerfile +13 -0
- newstack/data/stacks/fastapi/template/LICENSE +21 -0
- newstack/data/stacks/fastapi/template/README.md +30 -0
- newstack/data/stacks/fastapi/template/pyproject.toml +31 -0
- newstack/data/stacks/fastapi/template/src/{{package_name}}/__init__.py +4 -0
- newstack/data/stacks/fastapi/template/src/{{package_name}}/main.py +50 -0
- newstack/data/stacks/fastapi/template/tests/test_api.py +41 -0
- newstack/data/stacks/node-express-ts/stack.json +14 -0
- newstack/data/stacks/node-express-ts/template/.github/workflows/ci.yml +28 -0
- newstack/data/stacks/node-express-ts/template/.gitignore +6 -0
- newstack/data/stacks/node-express-ts/template/LICENSE +21 -0
- newstack/data/stacks/node-express-ts/template/README.md +24 -0
- newstack/data/stacks/node-express-ts/template/package.json +26 -0
- newstack/data/stacks/node-express-ts/template/src/app.ts +35 -0
- newstack/data/stacks/node-express-ts/template/src/server.ts +7 -0
- newstack/data/stacks/node-express-ts/template/tests/app.test.ts +32 -0
- newstack/data/stacks/node-express-ts/template/tsconfig.json +14 -0
- newstack/data/stacks/python-cli/stack.json +50 -0
- newstack/data/stacks/python-cli/template/.github/workflows/ci.yml +32 -0
- newstack/data/stacks/python-cli/template/.gitignore +11 -0
- newstack/data/stacks/python-cli/template/LICENSE +21 -0
- newstack/data/stacks/python-cli/template/README.md +35 -0
- newstack/data/stacks/python-cli/template/pyproject.toml +32 -0
- newstack/data/stacks/python-cli/template/src/{{package_name}}/__init__.py +4 -0
- newstack/data/stacks/python-cli/template/src/{{package_name}}/__main__.py +6 -0
- newstack/data/stacks/python-cli/template/src/{{package_name}}/cli.py +30 -0
- newstack/data/stacks/python-cli/template/tests/test_cli.py +20 -0
- newstack/data/stacks/python-lib/stack.json +42 -0
- newstack/data/stacks/python-lib/template/.github/workflows/ci.yml +32 -0
- newstack/data/stacks/python-lib/template/.gitignore +11 -0
- newstack/data/stacks/python-lib/template/LICENSE +21 -0
- newstack/data/stacks/python-lib/template/README.md +28 -0
- newstack/data/stacks/python-lib/template/pyproject.toml +34 -0
- newstack/data/stacks/python-lib/template/src/{{package_name}}/__init__.py +6 -0
- newstack/data/stacks/python-lib/template/src/{{package_name}}/core.py +23 -0
- newstack/data/stacks/python-lib/template/tests/test_core.py +31 -0
- newstack/data/stacks/react-vite-ts/stack.json +15 -0
- newstack/data/stacks/react-vite-ts/template/.github/workflows/ci.yml +29 -0
- newstack/data/stacks/react-vite-ts/template/.gitignore +7 -0
- newstack/data/stacks/react-vite-ts/template/LICENSE +21 -0
- newstack/data/stacks/react-vite-ts/template/README.md +25 -0
- newstack/data/stacks/react-vite-ts/template/index.html +12 -0
- newstack/data/stacks/react-vite-ts/template/package.json +28 -0
- newstack/data/stacks/react-vite-ts/template/src/App.test.tsx +20 -0
- newstack/data/stacks/react-vite-ts/template/src/App.tsx +15 -0
- newstack/data/stacks/react-vite-ts/template/src/main.tsx +16 -0
- newstack/data/stacks/react-vite-ts/template/src/styles.css +26 -0
- newstack/data/stacks/react-vite-ts/template/tsconfig.json +16 -0
- newstack/data/stacks/react-vite-ts/template/vite.config.ts +10 -0
- newstack/data/stacks/static-site/stack.json +14 -0
- newstack/data/stacks/static-site/template/.github/workflows/deploy-pages.yml +30 -0
- newstack/data/stacks/static-site/template/.gitignore +2 -0
- newstack/data/stacks/static-site/template/LICENSE +21 -0
- newstack/data/stacks/static-site/template/README.md +21 -0
- newstack/data/stacks/static-site/template/index.html +34 -0
- newstack/data/stacks/static-site/template/main.js +15 -0
- newstack/data/stacks/static-site/template/styles.css +74 -0
- newstack/data/stacks/static-site/template/tests/site.test.mjs +22 -0
- newstack/registry.py +137 -0
- newstack/render.py +163 -0
- newstack/verify.py +133 -0
- newstack-0.1.0.dist-info/METADATA +197 -0
- newstack-0.1.0.dist-info/RECORD +72 -0
- newstack-0.1.0.dist-info/WHEEL +4 -0
- newstack-0.1.0.dist-info/entry_points.txt +2 -0
- newstack-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-express-ts",
|
|
3
|
+
"title": "Express + TypeScript API",
|
|
4
|
+
"language": "node",
|
|
5
|
+
"blurb": "HTTP API: Express 5, typed handlers, supertest tests, CI",
|
|
6
|
+
"requires": ["node", "npm", "git"],
|
|
7
|
+
"default_description": "An Express API.",
|
|
8
|
+
"next_steps": ["cd {path}", "npm install", "npm run dev"],
|
|
9
|
+
"verify": [
|
|
10
|
+
{"name": "install", "cmd": "npm install --no-audit --no-fund --silent", "tools": ["npm"], "timeout": 900},
|
|
11
|
+
{"name": "typecheck", "cmd": "npm run typecheck", "tools": ["npm"], "timeout": 300},
|
|
12
|
+
{"name": "test", "cmd": "npm test", "tools": ["npm"], "timeout": 300}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
node-version: ["22", "24"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- uses: actions/setup-node@v7
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node-version }}
|
|
23
|
+
cache: npm
|
|
24
|
+
# no lockfile is shipped yet: run `npm install` once locally and commit
|
|
25
|
+
# package-lock.json, then switch this to `npm ci` for reproducible installs
|
|
26
|
+
- run: npm install --no-audit --no-fund
|
|
27
|
+
- run: npm run typecheck
|
|
28
|
+
- run: npm test
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) {{ year }} {{ author }}
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# {{ title }}
|
|
2
|
+
|
|
3
|
+
{{ description }}
|
|
4
|
+
|
|
5
|
+
Express 5 on TypeScript: `GET /health`, `GET /items`, `POST /items`.
|
|
6
|
+
|
|
7
|
+
## Develop
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev # http://localhost:3000
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Check
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm run typecheck
|
|
18
|
+
npm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
After the first `npm install`, commit `package-lock.json` and switch CI's install step
|
|
22
|
+
to `npm ci` for reproducible installs.
|
|
23
|
+
|
|
24
|
+
Generated with [newstack](https://github.com/DawnofGenX/newstack).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{ npm_name }}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "{{ description }}",
|
|
7
|
+
"main": "src/server.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "tsx watch src/server.ts",
|
|
10
|
+
"start": "tsx src/server.ts",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"express": "^5.3.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/express": "^5.0.6",
|
|
19
|
+
"@types/node": "^26.6.6",
|
|
20
|
+
"@types/supertest": "^7.2.1",
|
|
21
|
+
"supertest": "^7.3.1",
|
|
22
|
+
"tsx": "^4.23.15",
|
|
23
|
+
"typescript": "^7.0.2",
|
|
24
|
+
"vitest": "^5.0.5"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import express, { type Request, type Response } from "express";
|
|
2
|
+
|
|
3
|
+
export interface Item {
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createApp() {
|
|
9
|
+
const app = express();
|
|
10
|
+
app.use(express.json());
|
|
11
|
+
|
|
12
|
+
const items: Item[] = [];
|
|
13
|
+
let nextId = 1;
|
|
14
|
+
|
|
15
|
+
app.get("/health", (_req: Request, res: Response) => {
|
|
16
|
+
res.json({ status: "ok" });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.get("/items", (_req: Request, res: Response) => {
|
|
20
|
+
res.json(items);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
app.post("/items", (req: Request, res: Response) => {
|
|
24
|
+
const name = typeof req.body?.name === "string" ? req.body.name.trim() : "";
|
|
25
|
+
if (!name) {
|
|
26
|
+
res.status(400).json({ error: "name is required" });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const item: Item = { id: nextId++, name };
|
|
30
|
+
items.push(item);
|
|
31
|
+
res.status(201).json(item);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return app;
|
|
35
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import request from "supertest";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { createApp } from "../src/app";
|
|
5
|
+
|
|
6
|
+
describe("api", () => {
|
|
7
|
+
it("answers the health check", async () => {
|
|
8
|
+
const response = await request(createApp()).get("/health");
|
|
9
|
+
expect(response.status).toBe(200);
|
|
10
|
+
expect(response.body).toEqual({ status: "ok" });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("starts with no items", async () => {
|
|
14
|
+
const response = await request(createApp()).get("/items");
|
|
15
|
+
expect(response.body).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("creates an item and lists it", async () => {
|
|
19
|
+
const app = createApp();
|
|
20
|
+
const created = await request(app).post("/items").send({ name: "pen" });
|
|
21
|
+
expect(created.status).toBe(201);
|
|
22
|
+
expect(created.body).toEqual({ id: 1, name: "pen" });
|
|
23
|
+
|
|
24
|
+
const listed = await request(app).get("/items");
|
|
25
|
+
expect(listed.body).toHaveLength(1);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("rejects an item without a name", async () => {
|
|
29
|
+
const response = await request(createApp()).post("/items").send({});
|
|
30
|
+
expect(response.status).toBe(400);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"types": ["node"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src", "tests"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "python-cli",
|
|
3
|
+
"title": "Python CLI",
|
|
4
|
+
"language": "python",
|
|
5
|
+
"blurb": "Console command on argparse: zero runtime deps, tested entry point",
|
|
6
|
+
"requires": [
|
|
7
|
+
"uv",
|
|
8
|
+
"git"
|
|
9
|
+
],
|
|
10
|
+
"default_description": "A command line tool.",
|
|
11
|
+
"next_steps": [
|
|
12
|
+
"cd {path}",
|
|
13
|
+
"uv sync",
|
|
14
|
+
"uv run {npm_name} greet --name you"
|
|
15
|
+
],
|
|
16
|
+
"verify": [
|
|
17
|
+
{
|
|
18
|
+
"name": "install",
|
|
19
|
+
"cmd": "uv venv -q .venv && uv pip install -q --python .venv/bin/python -e . pytest ruff",
|
|
20
|
+
"tools": [
|
|
21
|
+
"uv"
|
|
22
|
+
],
|
|
23
|
+
"timeout": 900
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "ruff",
|
|
27
|
+
"cmd": ".venv/bin/ruff check .",
|
|
28
|
+
"tools": [
|
|
29
|
+
"uv"
|
|
30
|
+
],
|
|
31
|
+
"timeout": 300
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "pytest",
|
|
35
|
+
"cmd": ".venv/bin/python -m pytest -q",
|
|
36
|
+
"tools": [
|
|
37
|
+
"uv"
|
|
38
|
+
],
|
|
39
|
+
"timeout": 300
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "run",
|
|
43
|
+
"cmd": ".venv/bin/{{ npm_name }} greet --name newstack",
|
|
44
|
+
"tools": [
|
|
45
|
+
"uv"
|
|
46
|
+
],
|
|
47
|
+
"timeout": 120
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- name: Set up uv
|
|
21
|
+
uses: astral-sh/setup-uv@v10.3.3
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
enable-cache: true
|
|
25
|
+
- name: Install
|
|
26
|
+
run: uv sync --group dev
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: uv run ruff check .
|
|
29
|
+
- name: Test
|
|
30
|
+
run: uv run pytest
|
|
31
|
+
- name: Smoke test the CLI
|
|
32
|
+
run: uv run {{ npm_name }} greet --name CI
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) {{ year }} {{ author }}
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{ title }}
|
|
2
|
+
|
|
3
|
+
{{ description }}
|
|
4
|
+
|
|
5
|
+
A single-command CLI built on the standard library — no runtime dependencies.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv sync
|
|
11
|
+
uv run {{ npm_name }} --help
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or install it on your PATH:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install .
|
|
18
|
+
{{ npm_name }} greet --name world
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
{{ npm_name }} greet --name <name> print a greeting
|
|
25
|
+
{{ npm_name }} --version print the version
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Develop
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv run pytest
|
|
32
|
+
uv run ruff check .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Generated with [newstack](https://github.com/DawnofGenX/newstack).
|
|
@@ -0,0 +1,32 @@
|
|
|
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.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "{{ author }}", email = "{{ email }}" }]
|
|
14
|
+
dependencies = []
|
|
15
|
+
classifiers = ["Environment :: Console", "Programming Language :: Python :: 3"]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
{{ npm_name }} = "{{ package_name }}.cli:main"
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = ["pytest>=8.0", "ruff>=0.6"]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/{{ package_name }}"]
|
|
25
|
+
|
|
26
|
+
[tool.ruff]
|
|
27
|
+
line-length = 100
|
|
28
|
+
target-version = "py310"
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
testpaths = ["tests"]
|
|
32
|
+
addopts = "-q"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Command line entry point for {{ title }}."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
13
|
+
parser = argparse.ArgumentParser(prog="{{ npm_name }}", description="{{ description }}")
|
|
14
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
15
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
16
|
+
|
|
17
|
+
greet = sub.add_parser("greet", help="print a greeting")
|
|
18
|
+
greet.add_argument("--name", default="world", help="who to greet")
|
|
19
|
+
return parser
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
23
|
+
args = build_parser().parse_args(argv)
|
|
24
|
+
if args.command == "greet":
|
|
25
|
+
print(f"Hello, {args.name}!")
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
sys.exit(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from {{ package_name }}.cli import main
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_greet_uses_default_name(capsys) -> None:
|
|
5
|
+
assert main(["greet"]) == 0
|
|
6
|
+
assert capsys.readouterr().out.strip() == "Hello, world!"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_greet_uses_given_name(capsys) -> None:
|
|
10
|
+
assert main(["greet", "--name", "Ada"]) == 0
|
|
11
|
+
assert "Ada" in capsys.readouterr().out
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_version_prints_and_exits(capsys) -> None:
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
with pytest.raises(SystemExit) as excinfo:
|
|
18
|
+
main(["--version"])
|
|
19
|
+
assert excinfo.value.code == 0
|
|
20
|
+
assert "0.1.0" in capsys.readouterr().out
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "python-lib",
|
|
3
|
+
"title": "Python library",
|
|
4
|
+
"language": "python",
|
|
5
|
+
"blurb": "Installable package: src layout, pytest, ruff, 4-version CI matrix",
|
|
6
|
+
"requires": [
|
|
7
|
+
"uv",
|
|
8
|
+
"git"
|
|
9
|
+
],
|
|
10
|
+
"default_description": "A Python library.",
|
|
11
|
+
"next_steps": [
|
|
12
|
+
"cd {path}",
|
|
13
|
+
"uv sync",
|
|
14
|
+
"uv run pytest"
|
|
15
|
+
],
|
|
16
|
+
"verify": [
|
|
17
|
+
{
|
|
18
|
+
"name": "install",
|
|
19
|
+
"cmd": "uv venv -q .venv && uv pip install -q --python .venv/bin/python -e . pytest ruff",
|
|
20
|
+
"tools": [
|
|
21
|
+
"uv"
|
|
22
|
+
],
|
|
23
|
+
"timeout": 900
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "ruff",
|
|
27
|
+
"cmd": ".venv/bin/ruff check .",
|
|
28
|
+
"tools": [
|
|
29
|
+
"uv"
|
|
30
|
+
],
|
|
31
|
+
"timeout": 300
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "pytest",
|
|
35
|
+
"cmd": ".venv/bin/python -m pytest -q",
|
|
36
|
+
"tools": [
|
|
37
|
+
"uv"
|
|
38
|
+
],
|
|
39
|
+
"timeout": 300
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- name: Set up uv
|
|
21
|
+
uses: astral-sh/setup-uv@v10.3.3
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
enable-cache: true
|
|
25
|
+
- name: Install
|
|
26
|
+
run: uv sync --group dev
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: uv run ruff check .
|
|
29
|
+
- name: Test
|
|
30
|
+
run: uv run pytest
|
|
31
|
+
- name: Build
|
|
32
|
+
run: uv build
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) {{ year }} {{ author }}
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# {{ title }}
|
|
2
|
+
|
|
3
|
+
{{ description }}
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv sync
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from {{ package_name }} import truncate, word_count
|
|
15
|
+
|
|
16
|
+
word_count("hello world") # 2
|
|
17
|
+
truncate("hello world", 8) # "hello w…"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Develop
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run pytest # tests
|
|
24
|
+
uv run ruff check . # lint
|
|
25
|
+
uv build # sdist + wheel
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Generated with [newstack](https://github.com/DawnofGenX/newstack).
|
|
@@ -0,0 +1,34 @@
|
|
|
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.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "{{ author }}", email = "{{ email }}" }]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = ["pytest>=8.0", "ruff>=0.6"]
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["src/{{ package_name }}"]
|
|
27
|
+
|
|
28
|
+
[tool.ruff]
|
|
29
|
+
line-length = 100
|
|
30
|
+
target-version = "py310"
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
34
|
+
addopts = "-q"
|