mirror of
https://github.com/kellymichels/zscripts-token-savers
synced 2026-10-07 07:18:18 +00:00
feat(zstart): support uvicorn/ASGI apps via a startApp config field
Python projects could only be started as `python -m <startModule>`, so FastAPI/ASGI apps that run under uvicorn (like evo-ai: `uvicorn app.main:app`) couldn't be started by zstart in either port. Add an optional `startApp` field. When set, zstart runs `uvicorn <startApp> --host <bind-host> --port <ports.dev> --reload` via the venv python's -m (no PATH juggling), integrating zstart's existing bind-host and dev-port handling. startApp takes precedence over startModule; a python project still needs one or the other. Applied to bash and PowerShell, documented in the README + example configs.
This commit is contained in:
parent
4ebde3ebcf
commit
fd53acc23d
@ -72,6 +72,7 @@ The example config ships with sample projects named by their kind — `pyapp`, `
|
|||||||
"kind": "python", // python | vite | nextjs | edge | docker
|
"kind": "python", // python | vite | nextjs | edge | docker
|
||||||
"localRoot": "C:\\dev\\myapp", // project folder on this machine
|
"localRoot": "C:\\dev\\myapp", // project folder on this machine
|
||||||
"startModule": "myapp.main", // python kind: runs "python -m myapp.main"
|
"startModule": "myapp.main", // python kind: runs "python -m myapp.main"
|
||||||
|
// "startApp": "app.main:app", // ...or, for ASGI/FastAPI: uvicorn app.main:app --port <dev> --reload
|
||||||
"ports": { "dev": 8080, "prod": 3000 }, // local dev port / direct server port
|
"ports": { "dev": 8080, "prod": 3000 }, // local dev port / direct server port
|
||||||
"domain": "www.myapp.com", // public domain (health checks + verification)
|
"domain": "www.myapp.com", // public domain (health checks + verification)
|
||||||
"start": { // optional zstart pre-steps
|
"start": { // optional zstart pre-steps
|
||||||
@ -135,7 +136,7 @@ The `.cmd` wrappers are the everyday interface. Every command takes one or more
|
|||||||
zstart <project> [<project> ...] [-Port N] [-BindHost <host>] [-Detached]
|
zstart <project> [<project> ...] [-Port N] [-BindHost <host>] [-Detached]
|
||||||
```
|
```
|
||||||
|
|
||||||
Starts each project's dev server using the handler for its `kind`: **python** runs `python -m <startModule>` (preferring the project's `.venv`), **vite** runs `npm run dev -- --host --port`, **nextjs** runs `npm run dev` with `PORT` set. Runs `npm install` automatically if `node_modules` is missing. A project's optional `start` config block runs first — `gitPull` fast-forwards the checkout and `env` sets process environment variables. Two more opt-in conveniences: if the project has a `motd/` folder of `.txt` files, one is shown (rotating) at startup; if it has `scripts/build_version_tool.py`, the build number is bumped on each start.
|
Starts each project's dev server using the handler for its `kind`: **python** runs `python -m <startModule>` — or, for an ASGI/FastAPI app, `uvicorn <startApp>` (e.g. `app.main:app`) with the dev port and `--reload` — preferring the project's `.venv`; **vite** runs `npm run dev -- --host --port`, **nextjs** runs `npm run dev` with `PORT` set. Runs `npm install` automatically if `node_modules` is missing. A project's optional `start` config block runs first — `gitPull` fast-forwards the checkout and `env` sets process environment variables. Two more opt-in conveniences: if the project has a `motd/` folder of `.txt` files, one is shown (rotating) at startup; if it has `scripts/build_version_tool.py`, the build number is bumped on each start.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
zstart viteapp # dev server on its configured port
|
zstart viteapp # dev server on its configured port
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"kind": "python",
|
"kind": "python",
|
||||||
"localRoot": "/home/youruser/code/pyapp",
|
"localRoot": "/home/youruser/code/pyapp",
|
||||||
"startModule": "pyapp.main",
|
"startModule": "pyapp.main",
|
||||||
|
"_startApp_note": "ASGI/FastAPI app? Use \"startApp\": \"app.main:app\" instead of startModule (runs uvicorn --port <dev> --reload).",
|
||||||
"ports": { "dev": 8080 },
|
"ports": { "dev": 8080 },
|
||||||
"domain": "pyapp.yourdomain.com",
|
"domain": "pyapp.yourdomain.com",
|
||||||
"start": {
|
"start": {
|
||||||
|
|||||||
27
bash/zstart
27
bash/zstart
@ -8,8 +8,9 @@
|
|||||||
# Usage:
|
# Usage:
|
||||||
# zstart <project> [<project> ...] [--port N] [--bind-host host] [--detached]
|
# zstart <project> [<project> ...] [--port N] [--bind-host host] [--detached]
|
||||||
#
|
#
|
||||||
# Handlers by kind: python (python -m <startModule>, prefers .venv),
|
# Handlers by kind: python (python -m <startModule>, or uvicorn <startApp> for
|
||||||
# vite (npm run dev -- --host --port), nextjs (npm run dev with PORT).
|
# ASGI/FastAPI apps; prefers .venv), vite (npm run dev -- --host --port),
|
||||||
|
# nextjs (npm run dev with PORT).
|
||||||
# Detached servers log to /tmp/zstart-<project>.log ; stop them with zkill.
|
# Detached servers log to /tmp/zstart-<project>.log ; stop them with zkill.
|
||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@ -67,11 +68,12 @@ start_prep() { # <key>
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_python() { # <key> <port>
|
start_python() { # <key> <port>
|
||||||
local key="$1" port="$2" root module exe bv=""
|
local key="$1" port="$2" root module app exe bv="" run_cmd=() what
|
||||||
root="$(zproj_root "$key")"
|
root="$(zproj_root "$key")"
|
||||||
module="$(zproj "$key" .startModule)"
|
module="$(zproj "$key" .startModule)"
|
||||||
|
app="$(zproj "$key" .startApp)"
|
||||||
[ -d "$root" ] || { err "Project root not found: $root"; return 1; }
|
[ -d "$root" ] || { err "Project root not found: $root"; return 1; }
|
||||||
[ -n "$module" ] || { err "Project '$key' (kind=python) needs 'startModule' in zconfig.json (e.g. \"startModule\": \"pyapp.main\")."; return 1; }
|
[ -n "$module" ] || [ -n "$app" ] || { err "Project '$key' (kind=python) needs 'startModule' (python -m ...) or 'startApp' (uvicorn app:app) in zconfig.json."; return 1; }
|
||||||
|
|
||||||
# Prefer the project venv. The Scripts/python.exe branch is a *Windows* venv
|
# Prefer the project venv. The Scripts/python.exe branch is a *Windows* venv
|
||||||
# layout — only runnable from a Windows-family shell (Git Bash/MSYS/Cygwin).
|
# layout — only runnable from a Windows-family shell (Git Bash/MSYS/Cygwin).
|
||||||
@ -88,8 +90,19 @@ start_python() { # <key> <port>
|
|||||||
[ -n "$bv" ] || bv="$(cd "$root" && "$exe" scripts/build_version_tool.py get 2>/dev/null | tail -1)"
|
[ -n "$bv" ] || bv="$(cd "$root" && "$exe" scripts/build_version_tool.py get 2>/dev/null | tail -1)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# startApp (uvicorn ASGI target, e.g. app.main:app) takes precedence over
|
||||||
|
# startModule (python -m ...). uvicorn is run via the venv python's -m so no
|
||||||
|
# PATH juggling is needed, and it gets zstart's bind-host + dev port.
|
||||||
|
if [ -n "$app" ]; then
|
||||||
|
run_cmd=("$exe" -m uvicorn "$app" --host "$bind_host" --port "$port" --reload)
|
||||||
|
what="uvicorn $app"
|
||||||
|
else
|
||||||
|
run_cmd=("$exe" -m "$module")
|
||||||
|
what="python -m $module"
|
||||||
|
fi
|
||||||
|
|
||||||
printf '\n'; info "=== zstart ($(zproj "$key" .label)) ==="
|
printf '\n'; info "=== zstart ($(zproj "$key" .label)) ==="
|
||||||
info "Starting python -m $module on port $port..."
|
info "Starting $what on port $port..."
|
||||||
[ -n "$bv" ] && note "Build Version: $bv"
|
[ -n "$bv" ] && note "Build Version: $bv"
|
||||||
show_project_motd "$root"
|
show_project_motd "$root"
|
||||||
dim "Press Ctrl+C to stop the server"
|
dim "Press Ctrl+C to stop the server"
|
||||||
@ -97,9 +110,9 @@ start_python() { # <key> <port>
|
|||||||
printf '\n'
|
printf '\n'
|
||||||
|
|
||||||
if [ "$detached" -eq 1 ]; then
|
if [ "$detached" -eq 1 ]; then
|
||||||
detach "$key" "$root" "$exe" -m "$module"
|
detach "$key" "$root" "${run_cmd[@]}"
|
||||||
else
|
else
|
||||||
( cd "$root" && exec "$exe" -m "$module" )
|
( cd "$root" && exec "${run_cmd[@]}" )
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
"kind": "python",
|
"kind": "python",
|
||||||
"localRoot": "C:\\YourRoot\\pyapp",
|
"localRoot": "C:\\YourRoot\\pyapp",
|
||||||
"startModule": "pyapp.main",
|
"startModule": "pyapp.main",
|
||||||
|
"_startApp_note": "ASGI/FastAPI app? Use \"startApp\": \"app.main:app\" instead of startModule (runs uvicorn --port <dev> --reload).",
|
||||||
"ports": { "dev": 8080 },
|
"ports": { "dev": 8080 },
|
||||||
"domain": "pyapp.yourdomain.com",
|
"domain": "pyapp.yourdomain.com",
|
||||||
"start": {
|
"start": {
|
||||||
|
|||||||
31
zstart.ps1
31
zstart.ps1
@ -12,8 +12,9 @@
|
|||||||
# zstart viteapp -Port 3000
|
# zstart viteapp -Port 3000
|
||||||
# zstart pyapp nextapp -Detached
|
# zstart pyapp nextapp -Detached
|
||||||
#
|
#
|
||||||
# Handlers by kind: python (python -m <startModule>, prefers .venv),
|
# Handlers by kind: python (python -m <startModule>, or uvicorn <startApp> for
|
||||||
# vite (npm run dev -- --host --port), nextjs (npm run dev with PORT).
|
# ASGI/FastAPI apps; prefers .venv), vite (npm run dev -- --host --port),
|
||||||
|
# nextjs (npm run dev with PORT).
|
||||||
#
|
#
|
||||||
param(
|
param(
|
||||||
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
|
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
|
||||||
@ -45,12 +46,13 @@ function Test-PortNeedsAdmin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Start-PythonProject {
|
function Start-PythonProject {
|
||||||
param([string]$Key, $Proj, [int]$ListenPort, [bool]$RunDetached)
|
param([string]$Key, $Proj, [int]$ListenPort, [bool]$RunDetached, [string]$HostBind = "127.0.0.1")
|
||||||
$root = $Proj.localRoot
|
$root = $Proj.localRoot
|
||||||
if (-not (Test-Path -LiteralPath $root)) { throw "Project root not found: $root" }
|
if (-not (Test-Path -LiteralPath $root)) { throw "Project root not found: $root" }
|
||||||
$module = $Proj.startModule
|
$module = $Proj.startModule
|
||||||
if (-not $module) {
|
$app = $Proj.startApp
|
||||||
throw "Project '$Key' (kind=python) needs 'startModule' in zconfig.json (e.g. `"startModule`": `"pyapp.main`" runs 'python -m pyapp.main')."
|
if (-not $module -and -not $app) {
|
||||||
|
throw "Project '$Key' (kind=python) needs 'startModule' (python -m ...) or 'startApp' (uvicorn app:app) in zconfig.json."
|
||||||
}
|
}
|
||||||
Set-Location -LiteralPath $root
|
Set-Location -LiteralPath $root
|
||||||
$venvPython = Join-Path $root ".venv\Scripts\python.exe"
|
$venvPython = Join-Path $root ".venv\Scripts\python.exe"
|
||||||
@ -77,9 +79,20 @@ function Start-PythonProject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# startApp (uvicorn ASGI target, e.g. app.main:app) takes precedence over
|
||||||
|
# startModule (python -m ...). uvicorn runs via the venv python's -m, and
|
||||||
|
# gets zstart's bind-host + dev port.
|
||||||
|
if ($app) {
|
||||||
|
$runArgs = @("-m", "uvicorn", $app, "--host", $HostBind, "--port", "$ListenPort", "--reload")
|
||||||
|
$what = "uvicorn $app"
|
||||||
|
} else {
|
||||||
|
$runArgs = @("-m", $module)
|
||||||
|
$what = "python -m $module"
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "=== zstart ($($Proj.label)) ===" -ForegroundColor Cyan
|
Write-Host "=== zstart ($($Proj.label)) ===" -ForegroundColor Cyan
|
||||||
Write-Host "Starting python -m $module on port $ListenPort..." -ForegroundColor Cyan
|
Write-Host "Starting $what on port $ListenPort..." -ForegroundColor Cyan
|
||||||
if (-not [string]::IsNullOrWhiteSpace($buildVersion)) {
|
if (-not [string]::IsNullOrWhiteSpace($buildVersion)) {
|
||||||
Write-Host "Build Version: $buildVersion" -ForegroundColor Magenta
|
Write-Host "Build Version: $buildVersion" -ForegroundColor Magenta
|
||||||
}
|
}
|
||||||
@ -91,11 +104,11 @@ function Start-PythonProject {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
if ($RunDetached) {
|
if ($RunDetached) {
|
||||||
Start-Process -FilePath $exe -ArgumentList "-m", $module -WorkingDirectory $root | Out-Null
|
Start-Process -FilePath $exe -ArgumentList $runArgs -WorkingDirectory $root | Out-Null
|
||||||
Write-Host "Started in detached mode." -ForegroundColor Green
|
Write-Host "Started in detached mode." -ForegroundColor Green
|
||||||
Write-Host "Use zkill $Key to stop it." -ForegroundColor DarkGray
|
Write-Host "Use zkill $Key to stop it." -ForegroundColor DarkGray
|
||||||
} else {
|
} else {
|
||||||
& $exe -m $module
|
& $exe @runArgs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +214,7 @@ foreach ($key in $Projects) {
|
|||||||
Invoke-ProjectStartPrep -Proj $proj
|
Invoke-ProjectStartPrep -Proj $proj
|
||||||
|
|
||||||
switch ([string]$proj.kind) {
|
switch ([string]$proj.kind) {
|
||||||
"python" { Start-PythonProject -Key $key -Proj $proj -ListenPort $devPort -RunDetached $Detached.IsPresent }
|
"python" { Start-PythonProject -Key $key -Proj $proj -ListenPort $devPort -HostBind $BindHost -RunDetached $Detached.IsPresent }
|
||||||
"vite" { Start-ViteProject -Key $key -Proj $proj -ListenPort $devPort -HostBind $BindHost -RunDetached $Detached.IsPresent }
|
"vite" { Start-ViteProject -Key $key -Proj $proj -ListenPort $devPort -HostBind $BindHost -RunDetached $Detached.IsPresent }
|
||||||
"nextjs" { Start-NextProject -Key $key -Proj $proj -ListenPort $devPort -RunDetached $Detached.IsPresent }
|
"nextjs" { Start-NextProject -Key $key -Proj $proj -ListenPort $devPort -RunDetached $Detached.IsPresent }
|
||||||
default {
|
default {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user