feat: add node wrapper script and update Makefile for frontend tooling
This commit is contained in:
41
Makefile
41
Makefile
@@ -6,6 +6,8 @@ SHELL := /usr/bin/env bash
|
|||||||
BACKEND_DIR := backend
|
BACKEND_DIR := backend
|
||||||
FRONTEND_DIR := frontend
|
FRONTEND_DIR := frontend
|
||||||
|
|
||||||
|
NODE_WRAP := bash scripts/with_node.sh
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help: ## Show available targets
|
help: ## Show available targets
|
||||||
@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " %-26s %s\n", $$1, $$2}'
|
@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*## "}; {printf " %-26s %s\n", $$1, $$2}'
|
||||||
@@ -13,13 +15,20 @@ help: ## Show available targets
|
|||||||
.PHONY: setup
|
.PHONY: setup
|
||||||
setup: backend-sync frontend-sync ## Install/sync backend + frontend deps
|
setup: backend-sync frontend-sync ## Install/sync backend + frontend deps
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: setup format check ## Run everything (deps + format + CI-equivalent checks)
|
||||||
|
|
||||||
.PHONY: backend-sync
|
.PHONY: backend-sync
|
||||||
backend-sync: ## uv sync backend deps (includes dev extra)
|
backend-sync: ## uv sync backend deps (includes dev extra)
|
||||||
cd $(BACKEND_DIR) && uv sync --extra dev
|
cd $(BACKEND_DIR) && uv sync --extra dev
|
||||||
|
|
||||||
|
.PHONY: frontend-tooling
|
||||||
|
frontend-tooling: ## Verify frontend toolchain (node + npm)
|
||||||
|
@$(NODE_WRAP) --check
|
||||||
|
|
||||||
.PHONY: frontend-sync
|
.PHONY: frontend-sync
|
||||||
frontend-sync: ## npm install frontend deps
|
frontend-sync: frontend-tooling ## npm install frontend deps
|
||||||
cd $(FRONTEND_DIR) && npm install
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npm install
|
||||||
|
|
||||||
.PHONY: format
|
.PHONY: format
|
||||||
format: backend-format frontend-format ## Format backend + frontend
|
format: backend-format frontend-format ## Format backend + frontend
|
||||||
@@ -30,8 +39,8 @@ backend-format: ## Format backend (isort + black)
|
|||||||
cd $(BACKEND_DIR) && uv run black .
|
cd $(BACKEND_DIR) && uv run black .
|
||||||
|
|
||||||
.PHONY: frontend-format
|
.PHONY: frontend-format
|
||||||
frontend-format: ## Format frontend (prettier)
|
frontend-format: frontend-tooling ## Format frontend (prettier)
|
||||||
cd $(FRONTEND_DIR) && npx prettier --write "src/**/*.{ts,tsx,js,jsx,json,css,md}" "*.{ts,js,json,md,mdx}"
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npx prettier --write "src/**/*.{ts,tsx,js,jsx,json,css,md}" "*.{ts,js,json,md,mdx}"
|
||||||
|
|
||||||
.PHONY: format-check
|
.PHONY: format-check
|
||||||
format-check: backend-format-check frontend-format-check ## Check formatting (no changes)
|
format-check: backend-format-check frontend-format-check ## Check formatting (no changes)
|
||||||
@@ -42,8 +51,8 @@ backend-format-check: ## Check backend formatting (isort + black)
|
|||||||
cd $(BACKEND_DIR) && uv run black . --check --diff
|
cd $(BACKEND_DIR) && uv run black . --check --diff
|
||||||
|
|
||||||
.PHONY: frontend-format-check
|
.PHONY: frontend-format-check
|
||||||
frontend-format-check: ## Check frontend formatting (prettier)
|
frontend-format-check: frontend-tooling ## Check frontend formatting (prettier)
|
||||||
cd $(FRONTEND_DIR) && npx prettier --check "src/**/*.{ts,tsx,js,jsx,json,css,md}" "*.{ts,js,json,md,mdx}"
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npx prettier --check "src/**/*.{ts,tsx,js,jsx,json,css,md}" "*.{ts,js,json,md,mdx}"
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: backend-lint frontend-lint ## Lint backend + frontend
|
lint: backend-lint frontend-lint ## Lint backend + frontend
|
||||||
@@ -53,8 +62,8 @@ backend-lint: ## Lint backend (flake8)
|
|||||||
cd $(BACKEND_DIR) && uv run flake8 --config .flake8
|
cd $(BACKEND_DIR) && uv run flake8 --config .flake8
|
||||||
|
|
||||||
.PHONY: frontend-lint
|
.PHONY: frontend-lint
|
||||||
frontend-lint: ## Lint frontend (eslint)
|
frontend-lint: frontend-tooling ## Lint frontend (eslint)
|
||||||
cd $(FRONTEND_DIR) && npm run lint
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npm run lint
|
||||||
|
|
||||||
.PHONY: typecheck
|
.PHONY: typecheck
|
||||||
typecheck: backend-typecheck frontend-typecheck ## Typecheck backend + frontend
|
typecheck: backend-typecheck frontend-typecheck ## Typecheck backend + frontend
|
||||||
@@ -64,8 +73,8 @@ backend-typecheck: ## Typecheck backend (mypy --strict)
|
|||||||
cd $(BACKEND_DIR) && uv run mypy
|
cd $(BACKEND_DIR) && uv run mypy
|
||||||
|
|
||||||
.PHONY: frontend-typecheck
|
.PHONY: frontend-typecheck
|
||||||
frontend-typecheck: ## Typecheck frontend (tsc)
|
frontend-typecheck: frontend-tooling ## Typecheck frontend (tsc)
|
||||||
cd $(FRONTEND_DIR) && npx tsc -p tsconfig.json --noEmit
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npx tsc -p tsconfig.json --noEmit
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: backend-test frontend-test ## Run tests
|
test: backend-test frontend-test ## Run tests
|
||||||
@@ -88,8 +97,8 @@ backend-coverage: ## Backend tests with coverage gate (scoped 100% stmt+branch o
|
|||||||
--cov-fail-under=100
|
--cov-fail-under=100
|
||||||
|
|
||||||
.PHONY: frontend-test
|
.PHONY: frontend-test
|
||||||
frontend-test: ## Frontend tests (vitest)
|
frontend-test: frontend-tooling ## Frontend tests (vitest)
|
||||||
cd $(FRONTEND_DIR) && npm run test
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npm run test
|
||||||
|
|
||||||
.PHONY: backend-migrate
|
.PHONY: backend-migrate
|
||||||
backend-migrate: ## Apply backend DB migrations (alembic upgrade head)
|
backend-migrate: ## Apply backend DB migrations (alembic upgrade head)
|
||||||
@@ -99,12 +108,12 @@ backend-migrate: ## Apply backend DB migrations (alembic upgrade head)
|
|||||||
build: frontend-build ## Build artifacts
|
build: frontend-build ## Build artifacts
|
||||||
|
|
||||||
.PHONY: frontend-build
|
.PHONY: frontend-build
|
||||||
frontend-build: ## Build frontend (next build)
|
frontend-build: frontend-tooling ## Build frontend (next build)
|
||||||
cd $(FRONTEND_DIR) && npm run build
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npm run build
|
||||||
|
|
||||||
.PHONY: api-gen
|
.PHONY: api-gen
|
||||||
api-gen: ## Regenerate TS API client (requires backend running at 127.0.0.1:8000)
|
api-gen: frontend-tooling ## Regenerate TS API client (requires backend running at 127.0.0.1:8000)
|
||||||
cd $(FRONTEND_DIR) && npm run api:gen
|
$(NODE_WRAP) --cwd $(FRONTEND_DIR) npm run api:gen
|
||||||
|
|
||||||
.PHONY: backend-templates-sync
|
.PHONY: backend-templates-sync
|
||||||
backend-templates-sync: ## Sync templates to existing gateway agents (usage: make backend-templates-sync GATEWAY_ID=<uuid> SYNC_ARGS="--reset-sessions")
|
backend-templates-sync: ## Sync templates to existing gateway agents (usage: make backend-templates-sync GATEWAY_ID=<uuid> SYNC_ARGS="--reset-sessions")
|
||||||
|
|||||||
155
scripts/with_node.sh
Normal file
155
scripts/with_node.sh
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
with_node.sh [--check] [--cwd DIR] [--] <command> [args...]
|
||||||
|
|
||||||
|
Ensures node/npm/npx are available (optionally via nvm) before running a command.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--check Only verify node/npm/npx are available (no command required).
|
||||||
|
--cwd DIR Change to DIR before running.
|
||||||
|
-h, --help Show help.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_ONLY="false"
|
||||||
|
CWD=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--check)
|
||||||
|
CHECK_ONLY="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--cwd)
|
||||||
|
CWD="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$CWD" ]]; then
|
||||||
|
: # handled after we resolve repo root from this script's location
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||||
|
REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
|
if [[ -n "$CWD" ]]; then
|
||||||
|
cd "$CWD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read_nvmrc() {
|
||||||
|
local path="$1"
|
||||||
|
if [[ -f "$path" ]]; then
|
||||||
|
command tr -d ' \t\r\n' <"$path" || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
version_greater() {
|
||||||
|
# Returns 0 (true) if $1 > $2 for simple semver-ish values like "v22.21.1".
|
||||||
|
local v1="${1#v}"
|
||||||
|
local v2="${2#v}"
|
||||||
|
local a1 b1 c1 a2 b2 c2
|
||||||
|
IFS=. read -r a1 b1 c1 <<<"$v1"
|
||||||
|
IFS=. read -r a2 b2 c2 <<<"$v2"
|
||||||
|
a1="${a1:-0}"; b1="${b1:-0}"; c1="${c1:-0}"
|
||||||
|
a2="${a2:-0}"; b2="${b2:-0}"; c2="${c2:-0}"
|
||||||
|
if ((a1 != a2)); then ((a1 > a2)); return; fi
|
||||||
|
if ((b1 != b2)); then ((b1 > b2)); return; fi
|
||||||
|
((c1 > c2))
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap_nvm_if_needed() {
|
||||||
|
if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1 && command -v npx >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local nvm_dir="${NVM_DIR:-$HOME/.nvm}"
|
||||||
|
if [[ ! -s "$nvm_dir/nvm.sh" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# nvm is not guaranteed to be safe under `set -u`.
|
||||||
|
set +u
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
. "$nvm_dir/nvm.sh"
|
||||||
|
|
||||||
|
local version=""
|
||||||
|
version="$(read_nvmrc "$REPO_ROOT/.nvmrc")"
|
||||||
|
if [[ -z "$version" ]]; then
|
||||||
|
version="$(read_nvmrc "$REPO_ROOT/frontend/.nvmrc")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$version" ]]; then
|
||||||
|
nvm use --silent "$version" >/dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
# Prefer a user-defined nvm default, otherwise pick the newest installed version.
|
||||||
|
nvm use --silent default >/dev/null 2>&1 || true
|
||||||
|
if ! command -v node >/dev/null 2>&1; then
|
||||||
|
local versions_dir="$nvm_dir/versions/node"
|
||||||
|
if [[ -d "$versions_dir" ]]; then
|
||||||
|
local latest=""
|
||||||
|
local candidate=""
|
||||||
|
for candidate in "$versions_dir"/*; do
|
||||||
|
[[ -d "$candidate" ]] || continue
|
||||||
|
candidate="$(basename "$candidate")"
|
||||||
|
[[ "$candidate" =~ ^v?[0-9]+(\\.[0-9]+){0,2}$ ]] || continue
|
||||||
|
if [[ -z "$latest" ]] || version_greater "$candidate" "$latest"; then
|
||||||
|
latest="$candidate"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ -n "$latest" ]] && nvm use --silent "$latest" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -u
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap_nvm_if_needed
|
||||||
|
|
||||||
|
if ! command -v node >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: node is required to run frontend targets." >&2
|
||||||
|
echo "Install Node.js or make it available via nvm (set NVM_DIR and ensure \$NVM_DIR/nvm.sh exists)." >&2
|
||||||
|
echo "Tip: add a project .nvmrc or set an nvm default alias (e.g. 'nvm alias default <version>')." >&2
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: npm is required to run frontend targets." >&2
|
||||||
|
echo "Install Node.js (includes npm/npx) or ensure your nvm-selected Node provides npm." >&2
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v npx >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: npx is required to run frontend targets (usually installed with npm)." >&2
|
||||||
|
echo "Install Node.js (includes npm/npx) or ensure your npm install includes npx." >&2
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$CHECK_ONLY" == "true" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
Reference in New Issue
Block a user