mirror of
https://github.com/kellymichels/zscripts-token-savers
synced 2026-10-07 07:18:18 +00:00
feat(zsetup): new command to create a project's venv + install deps
zstart only warns when a python project has no venv; zsetup is the
command that provisions one. For a python project it creates <root>/.venv
and installs deps; for vite/nextjs it runs npm install. Idempotent.
The pip install command comes from the project's optional "install"
config field (e.g. "-e backend" for deps in a subfolder, "-r reqs.txt"),
or is auto-detected from a root pyproject.toml/setup.py ("-e .") or
requirements.txt ("-r requirements.txt"). Bash + PowerShell + .cmd
wrapper, documented in the README and example configs.
This commit is contained in:
parent
4ebde3ebcf
commit
6b9a45ea09
@ -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"
|
||||||
|
"install": "-e .", // optional: pip args `zsetup` uses (auto-detects "-e ." / "-r requirements.txt")
|
||||||
"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
|
||||||
@ -98,6 +99,7 @@ The example config ships with sample projects named by their kind — `pyapp`, `
|
|||||||
|
|
||||||
Optional blocks do real work:
|
Optional blocks do real work:
|
||||||
|
|
||||||
|
- **`install`** — how `zsetup` installs a python project's dependencies into its `.venv`: the pip args, e.g. `"-e ."`, `"-e backend"` (deps in a subfolder), or `"-r requirements.txt"`. Omit it and `zsetup` auto-detects a root `pyproject.toml`/`setup.py` (`-e .`) or `requirements.txt` (`-r requirements.txt`). `zstart` never installs — run `zsetup <key>` once, then `zstart <key>`.
|
||||||
- **`start`** — pre-start steps for `zstart`: `gitPull: true` runs `git pull --ff-only` in the project root first (never starts a stale checkout), and `env` sets environment variables for the dev-server process (feature flags, reload switches).
|
- **`start`** — pre-start steps for `zstart`: `gitPull: true` runs `git pull --ff-only` in the project root first (never starts a stale checkout), and `env` sets environment variables for the dev-server process (feature flags, reload switches).
|
||||||
- **`db`** — deploys wait for `pg_isready` and `zbackup_ec2` pulls a `pg_dump`, both against the compose service named `db`. Omit it and those steps are skipped cleanly.
|
- **`db`** — deploys wait for `pg_isready` and `zbackup_ec2` pulls a `pg_dump`, both against the compose service named `db`. Omit it and those steps are skipped cleanly.
|
||||||
- **`deploy.gitPull`** — `git pull --ff-only` in the project root before zipping, so a merged PR actually ships. Since `zdeploy` zips your working tree, a checkout left behind `origin` would otherwise deploy stale code *and still bump the build number* — a silent no-op that looks like success. A failed pull (dirty tree that conflicts, diverged history) aborts the deploy rather than shipping uncertain code.
|
- **`deploy.gitPull`** — `git pull --ff-only` in the project root before zipping, so a merged PR actually ships. Since `zdeploy` zips your working tree, a checkout left behind `origin` would otherwise deploy stale code *and still bump the build number* — a silent no-op that looks like success. A failed pull (dirty tree that conflicts, diverged history) aborts the deploy rather than shipping uncertain code.
|
||||||
@ -113,6 +115,7 @@ The `.cmd` wrappers are the everyday interface. Every command takes one or more
|
|||||||
|
|
||||||
| Command | What it does |
|
| Command | What it does |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| `zsetup <key> ...` | Create the project's Python venv + install deps (or `npm install` for node) |
|
||||||
| `zstart <key> ...` | Start local dev server(s) |
|
| `zstart <key> ...` | Start local dev server(s) |
|
||||||
| `zstartd <key> ...` | Same, detached (new window, returns immediately) |
|
| `zstartd <key> ...` | Same, detached (new window, returns immediately) |
|
||||||
| `zkill <key> ...` | Kill local dev server(s) by port |
|
| `zkill <key> ...` | Kill local dev server(s) by port |
|
||||||
|
|||||||
@ -35,6 +35,7 @@ command argument:
|
|||||||
|
|
||||||
| Command | What it does |
|
| Command | What it does |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| `zsetup <p>` | create the project's python venv + install deps (or `npm install`) |
|
||||||
| `zstart <p> [--detached]` | start local dev server (python/vite/nextjs) |
|
| `zstart <p> [--detached]` | start local dev server (python/vite/nextjs) |
|
||||||
| `zkill <p>` | free the project's dev port |
|
| `zkill <p>` | free the project's dev port |
|
||||||
| `zrestart <p> [--detached]` | kill + start in one command |
|
| `zrestart <p> [--detached]` | kill + start in one command |
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"kind": "python",
|
"kind": "python",
|
||||||
"localRoot": "/home/youruser/code/pyapp",
|
"localRoot": "/home/youruser/code/pyapp",
|
||||||
"startModule": "pyapp.main",
|
"startModule": "pyapp.main",
|
||||||
|
"install": "-e .",
|
||||||
"ports": { "dev": 8080 },
|
"ports": { "dev": 8080 },
|
||||||
"domain": "pyapp.yourdomain.com",
|
"domain": "pyapp.yourdomain.com",
|
||||||
"start": {
|
"start": {
|
||||||
|
|||||||
95
bash/zsetup
Executable file
95
bash/zsetup
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Evomedia.net Token Savers — https://github.com/kellymichels/zscripts-token-savers
|
||||||
|
# Created by Kelly Michels · dev@evomedia.net
|
||||||
|
# Licensed under the MIT License. See LICENSE.
|
||||||
|
#
|
||||||
|
# zsetup — prepare a project for local dev: create its Python venv and install
|
||||||
|
# dependencies (python kind), or run `npm install` (vite/nextjs). Idempotent -
|
||||||
|
# safe to re-run to refresh deps. `zstart` only warns when a venv is missing;
|
||||||
|
# this is the command that creates it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# zsetup <project> [<project> ...]
|
||||||
|
# zsetup <project> --force # recreate the python venv from scratch
|
||||||
|
#
|
||||||
|
# Python install command (first match wins):
|
||||||
|
# 1. the project's "install" config field (e.g. "-e backend", "-r reqs.txt")
|
||||||
|
# 2. auto: pyproject.toml / setup.py -> "-e ." | requirements.txt -> "-r requirements.txt"
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
|
z_need_config
|
||||||
|
|
||||||
|
projects=(); force=0
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--force) force=1; shift ;;
|
||||||
|
-*) err "Unknown option: $1"; exit 1 ;;
|
||||||
|
*) projects+=("$1"); shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#projects[@]}" -eq 0 ]; then
|
||||||
|
printf '\n'; warn "Usage: zsetup <project> [<project> ...] [--force]"
|
||||||
|
dim " Projects in zconfig.json: $(zproj_csv)"
|
||||||
|
dim " Creates the python venv + installs deps (or runs npm install for vite/nextjs)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pip install args for a python project (one per line): config .install, else
|
||||||
|
# auto-detect a root pyproject.toml/setup.py or requirements.txt. Empty = unknown.
|
||||||
|
py_install_args() { # <key> <root>
|
||||||
|
local key="$1" root="$2" cfg
|
||||||
|
cfg="$(zproj "$key" .install)"
|
||||||
|
if [ -n "$cfg" ]; then printf '%s\n' $cfg; return 0; fi
|
||||||
|
if [ -f "$root/pyproject.toml" ] || [ -f "$root/setup.py" ]; then printf '%s\n' -e .; return 0; fi
|
||||||
|
if [ -f "$root/requirements.txt" ]; then printf '%s\n' -r requirements.txt; return 0; fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_python() { # <key> <root>
|
||||||
|
local key="$1" root="$2" venv="$2/.venv" args=() a
|
||||||
|
z_require python3
|
||||||
|
while IFS= read -r a; do [ -n "$a" ] && args+=("$a"); done < <(py_install_args "$key" "$root")
|
||||||
|
if [ "${#args[@]}" -eq 0 ]; then
|
||||||
|
err " Can't determine dependencies for '$key' (no pyproject.toml/setup.py/requirements.txt at $root)."
|
||||||
|
dim " Add an \"install\" field to the project, e.g. \"install\": \"-e backend\" or \"-r requirements.txt\"."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ "$force" -eq 1 ] && [ -d "$venv" ]; then dim " Removing existing venv (--force)..."; rm -rf "$venv"; fi
|
||||||
|
if [ ! -x "$venv/bin/python" ]; then
|
||||||
|
info " Creating venv: $venv"
|
||||||
|
python3 -m venv "$venv" || { err " venv creation failed"; return 1; }
|
||||||
|
else
|
||||||
|
dim " venv exists: $venv (installing/updating deps)"
|
||||||
|
fi
|
||||||
|
info " pip install ${args[*]}"
|
||||||
|
( cd "$root" && "$venv/bin/pip" install "${args[@]}" ) || { err " pip install failed"; return 1; }
|
||||||
|
ok " $key ready - zstart $key will use $venv/bin/python."
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_node() { # <key> <root>
|
||||||
|
local key="$1" root="$2"
|
||||||
|
[ -f "$root/package.json" ] || { err " No package.json in $root"; return 1; }
|
||||||
|
z_require npm
|
||||||
|
info " npm install (in $root)..."
|
||||||
|
( cd "$root" && npm install ) || { err " npm install failed"; return 1; }
|
||||||
|
ok " $key node_modules ready."
|
||||||
|
}
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
for key in "${projects[@]}"; do
|
||||||
|
zproj_require "$key"
|
||||||
|
kind="$(zproj "$key" .kind)"
|
||||||
|
root="$(zproj_root "$key")"
|
||||||
|
printf '\n'; info "=== zsetup ($(zproj "$key" .label)) ==="
|
||||||
|
[ -d "$root" ] || { err " Project root not found: $root"; rc=1; continue; }
|
||||||
|
case "$kind" in
|
||||||
|
python) setup_python "$key" "$root" || rc=1 ;;
|
||||||
|
vite|nextjs) setup_node "$key" "$root" || rc=1 ;;
|
||||||
|
*) dim " kind '$kind' - nothing to set up locally." ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
printf '\n'
|
||||||
|
exit "$rc"
|
||||||
@ -24,6 +24,7 @@
|
|||||||
"kind": "python",
|
"kind": "python",
|
||||||
"localRoot": "C:\\YourRoot\\pyapp",
|
"localRoot": "C:\\YourRoot\\pyapp",
|
||||||
"startModule": "pyapp.main",
|
"startModule": "pyapp.main",
|
||||||
|
"install": "-e .",
|
||||||
"ports": { "dev": 8080 },
|
"ports": { "dev": 8080 },
|
||||||
"domain": "pyapp.yourdomain.com",
|
"domain": "pyapp.yourdomain.com",
|
||||||
"start": {
|
"start": {
|
||||||
|
|||||||
6
zsetup.cmd
Normal file
6
zsetup.cmd
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
REM Evomedia.net Token Savers — https://github.com/kellymichels/zscripts-token-savers
|
||||||
|
REM Created by Kelly Michels · dev@evomedia.net
|
||||||
|
REM Licensed under the MIT License. See LICENSE.
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0zsetup.ps1" %*
|
||||||
106
zsetup.ps1
Normal file
106
zsetup.ps1
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Evomedia.net Token Savers — https://github.com/kellymichels/zscripts-token-savers
|
||||||
|
# Created by Kelly Michels · dev@evomedia.net
|
||||||
|
# Licensed under the MIT License. See LICENSE.
|
||||||
|
|
||||||
|
# zsetup.ps1 — prepare a project for local dev: create its Python venv and install
|
||||||
|
# dependencies (python kind), or run `npm install` (vite/nextjs). Idempotent -
|
||||||
|
# safe to re-run to refresh deps. `zstart` only warns when a venv is missing;
|
||||||
|
# this is the command that creates it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# zsetup <project> [<project> ...]
|
||||||
|
# zsetup <project> -Force # recreate the python venv from scratch
|
||||||
|
#
|
||||||
|
# Python install command (first match wins):
|
||||||
|
# 1. the project's "install" config field (e.g. "-e backend", "-r reqs.txt")
|
||||||
|
# 2. auto: pyproject.toml / setup.py -> "-e ." | requirements.txt -> "-r requirements.txt"
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
|
||||||
|
[string[]]$Projects = @(),
|
||||||
|
[switch]$Force
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
. (Join-Path $PSScriptRoot "ZHelpers.ps1")
|
||||||
|
Start-ZTracking
|
||||||
|
|
||||||
|
$Projects = @($Projects | ForEach-Object { $_.TrimStart('-') })
|
||||||
|
|
||||||
|
if ($Projects.Count -eq 0) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Usage: zsetup <project> [<project> ...] [-Force]" -ForegroundColor Yellow
|
||||||
|
Write-Host " Projects in zconfig.json: $((Get-ZProjectKeys) -join ', ')" -ForegroundColor Gray
|
||||||
|
Write-Host " Creates the python venv + installs deps (or runs npm install for vite/nextjs)." -ForegroundColor Gray
|
||||||
|
Stop-ZTracking; exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# pip install args for a python project: config .install, else auto-detect a
|
||||||
|
# root pyproject.toml/setup.py or requirements.txt. Empty array = unknown.
|
||||||
|
function Get-PyInstallArgs {
|
||||||
|
param([string]$Root, $Proj)
|
||||||
|
if ($Proj.install) { return @([string]$Proj.install -split '\s+' | Where-Object { $_ -ne '' }) }
|
||||||
|
if ((Test-Path (Join-Path $Root "pyproject.toml")) -or (Test-Path (Join-Path $Root "setup.py"))) { return @("-e", ".") }
|
||||||
|
if (Test-Path (Join-Path $Root "requirements.txt")) { return @("-r", "requirements.txt") }
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-PythonSetup {
|
||||||
|
param([string]$Key, [string]$Root, $Proj)
|
||||||
|
$installArgs = Get-PyInstallArgs -Root $Root -Proj $Proj
|
||||||
|
if ($installArgs.Count -eq 0) {
|
||||||
|
Write-Host " Can't determine dependencies for '$Key' (no pyproject.toml/setup.py/requirements.txt in $Root)." -ForegroundColor Red
|
||||||
|
Write-Host " Add an `"install`" field to the project, e.g. `"install`": `"-e backend`" or `"-r requirements.txt`"." -ForegroundColor DarkGray
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
$venv = Join-Path $Root ".venv"
|
||||||
|
$venvPython = Join-Path $venv "Scripts\python.exe"
|
||||||
|
if ($Force -and (Test-Path -LiteralPath $venv)) {
|
||||||
|
Write-Host " Removing existing venv (-Force)..." -ForegroundColor DarkGray
|
||||||
|
Remove-Item -LiteralPath $venv -Recurse -Force
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $venvPython)) {
|
||||||
|
Write-Host " Creating venv: $venv" -ForegroundColor Cyan
|
||||||
|
& python -m venv $venv
|
||||||
|
if ($LASTEXITCODE -ne 0) { Write-Host " venv creation failed" -ForegroundColor Red; return $false }
|
||||||
|
} else {
|
||||||
|
Write-Host " venv exists: $venv (installing/updating deps)" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
$pip = Join-Path $venv "Scripts\pip.exe"
|
||||||
|
Write-Host " pip install $($installArgs -join ' ')" -ForegroundColor Cyan
|
||||||
|
Push-Location -LiteralPath $Root
|
||||||
|
try { & $pip install @installArgs; $ok = ($LASTEXITCODE -eq 0) } finally { Pop-Location }
|
||||||
|
if (-not $ok) { Write-Host " pip install failed" -ForegroundColor Red; return $false }
|
||||||
|
Write-Host " $Key ready - zstart $Key will use $venvPython." -ForegroundColor Green
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-NodeSetup {
|
||||||
|
param([string]$Key, [string]$Root)
|
||||||
|
if (-not (Test-Path -LiteralPath (Join-Path $Root "package.json"))) {
|
||||||
|
Write-Host " No package.json in $Root" -ForegroundColor Red; return $false
|
||||||
|
}
|
||||||
|
Write-Host " npm install (in $Root)..." -ForegroundColor Cyan
|
||||||
|
Push-Location -LiteralPath $Root
|
||||||
|
try { & npm install; $ok = ($LASTEXITCODE -eq 0) } finally { Pop-Location }
|
||||||
|
if (-not $ok) { Write-Host " npm install failed" -ForegroundColor Red; return $false }
|
||||||
|
Write-Host " $Key node_modules ready." -ForegroundColor Green
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
$exitCode = 0
|
||||||
|
foreach ($key in $Projects) {
|
||||||
|
$proj = Get-ZProject -Key $key
|
||||||
|
$root = $proj.localRoot
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== zsetup ($($proj.label)) ===" -ForegroundColor Cyan
|
||||||
|
if (-not (Test-Path -LiteralPath $root)) { Write-Host " Project root not found: $root" -ForegroundColor Red; $exitCode = 1; continue }
|
||||||
|
switch ([string]$proj.kind) {
|
||||||
|
"python" { if (-not (Invoke-PythonSetup -Key $key -Root $root -Proj $proj)) { $exitCode = 1 } }
|
||||||
|
"vite" { if (-not (Invoke-NodeSetup -Key $key -Root $root)) { $exitCode = 1 } }
|
||||||
|
"nextjs" { if (-not (Invoke-NodeSetup -Key $key -Root $root)) { $exitCode = 1 } }
|
||||||
|
default { Write-Host " kind '$($proj.kind)' - nothing to set up locally." -ForegroundColor DarkGray }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Stop-ZTracking; exit $exitCode
|
||||||
Loading…
Reference in New Issue
Block a user