rapidkit 1.0.0-beta.4

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.
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env python3
2
+ """Professional CLI commands for {{ project_name }} - Poetry Scripts Integration
3
+
4
+ A modern CLI inspired by Next.js developer experience for FastAPI projects.
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Any, Dict
12
+
13
+
14
+ def _print_banner(emoji: str, message: str, color_code: str = "36") -> None:
15
+ """Print colored banner message."""
16
+ print(f"\033[{color_code}m{emoji} {message}\033[0m")
17
+
18
+
19
+ def _print_error(message: str) -> None:
20
+ """Print error message in red."""
21
+ print(f"\033[91mโŒ {message}\033[0m")
22
+
23
+
24
+ def _print_success(message: str) -> None:
25
+ """Print success message in green."""
26
+ print(f"\033[92mโœ… {message}\033[0m")
27
+
28
+
29
+ def _get_env_var(key: str, default: str) -> str:
30
+ """Get environment variable with default."""
31
+ return os.getenv(key, default)
32
+
33
+
34
+ def _parse_args() -> dict:
35
+ """Parse command line arguments."""
36
+ args = {"port": "8000", "host": "0.0.0.0", "workers": "1", "env": "development"}
37
+
38
+ i = 0
39
+ while i < len(sys.argv):
40
+ if sys.argv[i] in ["--port", "-p"] and i + 1 < len(sys.argv):
41
+ args["port"] = sys.argv[i + 1]
42
+ i += 2
43
+ elif sys.argv[i] in ["--host", "-h"] and i + 1 < len(sys.argv):
44
+ args["host"] = sys.argv[i + 1]
45
+ i += 2
46
+ elif sys.argv[i] in ["--workers", "-w"] and i + 1 < len(sys.argv):
47
+ args["workers"] = sys.argv[i + 1]
48
+ i += 2
49
+ elif sys.argv[i] in ["--env", "-e"] and i + 1 < len(sys.argv):
50
+ args["env"] = sys.argv[i + 1]
51
+ i += 2
52
+ else:
53
+ i += 1
54
+
55
+ return args
56
+
57
+ def _run_uvicorn(module: str, reload: bool = False, **kwargs: Any) -> None:
58
+ """Run uvicorn server with proper error handling."""
59
+ args: Dict[str, Any] = _parse_args()
60
+
61
+ cmd: list[str] = [
62
+ sys.executable, "-m", "uvicorn",
63
+ module,
64
+ "--host", str(args.get("host", "0.0.0.0")),
65
+ "--port", str(args.get("port", 8000))
66
+ ]
67
+
68
+ if reload:
69
+ cmd.append("--reload")
70
+ cmd.extend(["--reload-dir", "src"])
71
+
72
+ env: str = args.get("env", "development")
73
+ if env == "production" and "workers" in args:
74
+ cmd.extend(["--workers", str(args["workers"])])
75
+ cmd.append("--no-access-log")
76
+
77
+ extra_args: list[str] = kwargs.get("extra_args", [])
78
+ cmd.extend(extra_args)
79
+
80
+ env_vars: Dict[str, str] = os.environ.copy()
81
+ env_vars.update({
82
+ "PYTHONPATH": str(Path.cwd()),
83
+ "ENVIRONMENT": env
84
+ })
85
+
86
+ try:
87
+ subprocess.run(cmd, check=False, env=env_vars)
88
+ except KeyboardInterrupt:
89
+ _print_banner("๐Ÿ›‘", "Server stopped by user")
90
+ except Exception as e:
91
+ _print_error(f"Failed to start server: {e}")
92
+ sys.exit(1)
93
+
94
+ def dev() -> None:
95
+ """๐Ÿš€ Start development server with hot reload
96
+
97
+ Usage:
98
+ poetry run dev [options]
99
+
100
+ Options:
101
+ --port, -p PORT Server port (default: 8000)
102
+ --host, -h HOST Server host (default: 0.0.0.0)
103
+ --env, -e ENV Environment (default: development)
104
+
105
+ Examples:
106
+ poetry run dev
107
+ poetry run dev --port 3000
108
+ poetry run dev --host 127.0.0.1 --port 8080
109
+ """
110
+ _print_banner("๐Ÿš€", "Starting development server with hot reload...")
111
+ _print_banner("๐Ÿ“", f"Working directory: {Path.cwd()}")
112
+
113
+ args = _parse_args()
114
+ _print_banner("๐ŸŒ", f"Server will be available at: http://{args['host']}:{args['port']}")
115
+
116
+ _run_uvicorn("src.main:app", reload=True)
117
+
118
+
119
+ def start() -> None:
120
+ """โšก Start production server
121
+
122
+ Usage:
123
+ poetry run start [options]
124
+
125
+ Options:
126
+ --port, -p PORT Server port (default: 8000)
127
+ --host, -h HOST Server host (default: 0.0.0.0)
128
+ --workers, -w NUM Number of workers (default: 1)
129
+
130
+ Examples:
131
+ poetry run start
132
+ poetry run start --workers 4 --port 80
133
+ """
134
+ _print_banner("โšก", "Starting production server...")
135
+
136
+ args = _parse_args()
137
+ _print_banner("๐Ÿ‘ฅ", f"Workers: {args['workers']}")
138
+ _print_banner("๐ŸŒ", f"Server will be available at: http://{args['host']}:{args['port']}")
139
+
140
+ _run_uvicorn("src.main:app", reload=False, env="production")
141
+
142
+
143
+ def build() -> None:
144
+ """๐Ÿ“ฆ Build project for production
145
+
146
+ This command prepares the project for production deployment:
147
+ - Validates dependencies
148
+ - Runs type checking
149
+ - Builds distribution packages
150
+ """
151
+ _print_banner("๐Ÿ“ฆ", "Building project for production...")
152
+
153
+ try:
154
+ # Type checking
155
+ _print_banner("๐Ÿ”", "Running type checks...")
156
+ result = subprocess.run([
157
+ sys.executable, "-m", "mypy", "src", "--ignore-missing-imports"
158
+ ], capture_output=True, text=True)
159
+
160
+ if result.returncode != 0:
161
+ _print_error("Type checking failed!")
162
+ print(result.stdout)
163
+ print(result.stderr)
164
+ else:
165
+ _print_success("Type checking passed!")
166
+
167
+ # Build wheel
168
+ _print_banner("๐Ÿ—๏ธ", "Building distribution packages...")
169
+ subprocess.run([sys.executable, "-m", "build"], check=True)
170
+
171
+ _print_success("Build completed successfully!")
172
+ _print_banner("๐Ÿ“", "Distribution files created in 'dist/' directory")
173
+
174
+ except subprocess.CalledProcessError as e:
175
+ _print_error(f"Build failed: {e}")
176
+ sys.exit(1)
177
+ except FileNotFoundError:
178
+ _print_error("Build tools not found. Install with: poetry install --group dev")
179
+ sys.exit(1)
180
+
181
+
182
+ def test() -> None:
183
+ """๐Ÿงช Run tests with coverage
184
+
185
+ Usage:
186
+ poetry run test [pytest-options]
187
+
188
+ Examples:
189
+ poetry run test
190
+ poetry run test -v
191
+ poetry run test tests/test_api.py
192
+ poetry run test --cov-report=html
193
+ """
194
+ _print_banner("๐Ÿงช", "Running tests with coverage...")
195
+
196
+ # Get additional pytest args
197
+ pytest_args = [arg for arg in sys.argv[2:] if not arg.startswith("-")]
198
+
199
+ cmd = [
200
+ sys.executable, "-m", "pytest",
201
+ "--cov=src",
202
+ "--cov-report=term-missing",
203
+ "--cov-report=xml",
204
+ "-v"
205
+ ] + pytest_args
206
+
207
+ try:
208
+ result = subprocess.run(cmd, check=False)
209
+
210
+ if result.returncode == 0:
211
+ _print_success("All tests passed!")
212
+ else:
213
+ _print_error("Some tests failed!")
214
+ sys.exit(result.returncode)
215
+
216
+ except FileNotFoundError:
217
+ _print_error("pytest not found. Install with: poetry install --group dev")
218
+ sys.exit(1)
219
+
220
+
221
+ def lint() -> None:
222
+ """๏ฟฝ Run code linting and formatting
223
+
224
+ This command runs:
225
+ - ruff for linting
226
+ - black for formatting
227
+ - isort for import sorting
228
+ """
229
+ _print_banner("๏ฟฝ", "Running linting and formatting...")
230
+
231
+ try:
232
+ # Ruff linting
233
+ _print_banner("๐Ÿ“‹", "Running ruff linter...")
234
+ subprocess.run([sys.executable, "-m", "ruff", "check", "src"], check=True)
235
+
236
+ # Black formatting
237
+ _print_banner("๐ŸŽจ", "Running black formatter...")
238
+ subprocess.run([sys.executable, "-m", "black", "src", "--check"], check=True)
239
+
240
+ # isort import sorting
241
+ _print_banner("๐Ÿ“ฆ", "Checking import sorting...")
242
+ subprocess.run([sys.executable, "-m", "isort", "src", "--check-only"], check=True)
243
+
244
+ _print_success("All linting checks passed!")
245
+
246
+ except subprocess.CalledProcessError:
247
+ _print_error("Linting failed! Run 'poetry run format' to fix issues.")
248
+ sys.exit(1)
249
+ except FileNotFoundError:
250
+ _print_error("Linting tools not found. Install with: poetry install --group dev")
251
+ sys.exit(1)
252
+
253
+
254
+ def format() -> None:
255
+ """โœจ Format code automatically
256
+
257
+ This command auto-fixes:
258
+ - Code formatting with black
259
+ - Import sorting with isort
260
+ - Auto-fixable linting issues with ruff
261
+ """
262
+ _print_banner("โœจ", "Formatting code...")
263
+
264
+ try:
265
+ # Ruff auto-fix
266
+ _print_banner("๐Ÿ”ง", "Auto-fixing with ruff...")
267
+ subprocess.run([sys.executable, "-m", "ruff", "check", "src", "--fix"], check=True)
268
+
269
+ # Black formatting
270
+ _print_banner("๐ŸŽจ", "Formatting with black...")
271
+ subprocess.run([sys.executable, "-m", "black", "src"], check=True)
272
+
273
+ # isort import sorting
274
+ _print_banner("๐Ÿ“ฆ", "Sorting imports with isort...")
275
+ subprocess.run([sys.executable, "-m", "isort", "src"], check=True)
276
+
277
+ _print_success("Code formatting completed!")
278
+
279
+ except subprocess.CalledProcessError as e:
280
+ _print_error(f"Formatting failed: {e}")
281
+ sys.exit(1)
282
+ except FileNotFoundError:
283
+ _print_error("Formatting tools not found. Install with: poetry install --group dev")
284
+ sys.exit(1)
285
+
286
+
287
+ def help_cmd() -> None:
288
+ """๐Ÿ“š Show detailed help information"""
289
+ _print_banner("๐Ÿ“š", "{{ project_name }} - Available Commands")
290
+
291
+ commands = {
292
+ "dev": "๐Ÿš€ Start development server with hot reload",
293
+ "start": "โšก Start production server",
294
+ "build": "๐Ÿ“ฆ Build project for production",
295
+ "test": "๐Ÿงช Run tests with coverage",
296
+ "lint": "๐Ÿ”ง Run code linting and formatting checks",
297
+ "format": "โœจ Format code automatically",
298
+ "help": "๐Ÿ“š Show this help message"
299
+ }
300
+
301
+ print("\nCommands:")
302
+ for cmd, desc in commands.items():
303
+ print(f" poetry run {cmd:<10} {desc}")
304
+
305
+ print("\nFor detailed command help:")
306
+ print(" poetry run <command> --help")
307
+
308
+ print("\nProject: {{ project_name }}")
309
+ print("Framework: FastAPI + RapidKit")
310
+
311
+
312
+ if __name__ == "__main__":
313
+ if len(sys.argv) < 2:
314
+ help_cmd()
315
+ sys.exit(0)
316
+
317
+ command = sys.argv[1]
318
+ commands = {
319
+ "dev": dev,
320
+ "start": start,
321
+ "build": build,
322
+ "test": test,
323
+ "lint": lint,
324
+ "format": format,
325
+ "help": help_cmd,
326
+ }
327
+
328
+ if command in commands:
329
+ commands[command]()
330
+ else:
331
+ _print_error(f"Unknown command: {command}")
332
+ print("\nRun 'poetry run help' to see available commands.")
333
+ sys.exit(1)
@@ -0,0 +1,41 @@
1
+ """{{ project_name }} application entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+
8
+ # <<<inject:imports>>>
9
+
10
+ from .routing import api_router
11
+
12
+ app = FastAPI(
13
+ title="{{ project_name }}",
14
+ description="{{ description }}",
15
+ version="{{ app_version }}",
16
+ docs_url="/docs",
17
+ redoc_url="/redoc",
18
+ )
19
+
20
+ app.add_middleware(
21
+ CORSMiddleware,
22
+ allow_origins=["*"],
23
+ allow_credentials=True,
24
+ allow_methods=["*"],
25
+ allow_headers=["*"],
26
+ )
27
+
28
+ app.include_router(api_router, prefix="/api")
29
+ # <<<inject:routes>>>
30
+
31
+
32
+ @app.on_event("startup")
33
+ async def _on_startup() -> None:
34
+ """Execute startup hooks provided by modules."""
35
+ # <<<inject:startup>>>
36
+
37
+
38
+ @app.on_event("shutdown")
39
+ async def _on_shutdown() -> None:
40
+ """Execute shutdown hooks provided by modules."""
41
+ # <<<inject:shutdown>>>
@@ -0,0 +1,3 @@
1
+ """Module bootstrap namespace."""
2
+
3
+ # <<<inject:module-init>>>
@@ -0,0 +1,13 @@
1
+ """API router assembly."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter
6
+
7
+ from .health import router as health_router
8
+ # <<<inject:router-imports>>>
9
+
10
+ api_router = APIRouter()
11
+
12
+ api_router.include_router(health_router, prefix="/health", tags=["health"])
13
+ # <<<inject:router-mount>>>
@@ -0,0 +1,13 @@
1
+ """Health endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter
6
+
7
+ router = APIRouter()
8
+
9
+
10
+ @router.get("/", summary="Health check")
11
+ async def heartbeat() -> dict[str, str]:
12
+ """Return basic service heartbeat."""
13
+ return {"status": "ok"}
@@ -0,0 +1 @@
1
+ """Test package placeholder."""