From 82c3dbbc6bb6c7a8e5349144ffd77125d22618b2 Mon Sep 17 00:00:00 2001 From: Admire <64821731+LittleChenLiya@users.noreply.github.com> Date: Wed, 1 Apr 2026 23:13:00 +0800 Subject: [PATCH] Fix Windows startup and dependency checks (#1709) * windows check and dev fixes * fix windows startup scripts * fix windows startup scripts --------- Co-authored-by: Willem Jiang --- Makefile | 11 +++++++- README.md | 1 + README_zh.md | 1 + scripts/check.py | 55 +++++++++++++++++++++++++-------------- scripts/config-upgrade.sh | 25 ++++++++++++------ scripts/serve.sh | 11 +++++++- scripts/start-daemon.sh | 1 + 7 files changed, 76 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 550e7cd41..e74a02db3 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,14 @@ .PHONY: help config config-upgrade check install dev dev-daemon start stop up down clean docker-init docker-start docker-stop docker-logs docker-logs-frontend docker-logs-gateway -PYTHON ?= python BASH ?= bash # Detect OS for Windows compatibility ifeq ($(OS),Windows_NT) SHELL := cmd.exe + PYTHON ?= python +else + PYTHON ?= python3 endif help: @@ -96,6 +98,7 @@ setup-sandbox: # Start all services in development mode (with hot-reloading) dev: + @$(PYTHON) ./scripts/check.py ifeq ($(OS),Windows_NT) @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --dev else @@ -104,6 +107,7 @@ endif # Start all services in production mode (with optimizations) start: + @$(PYTHON) ./scripts/check.py ifeq ($(OS),Windows_NT) @call scripts\run-with-git-bash.cmd ./scripts/serve.sh --prod else @@ -112,7 +116,12 @@ endif # Start all services in daemon mode (background) dev-daemon: + @$(PYTHON) ./scripts/check.py +ifeq ($(OS),Windows_NT) + @call scripts\run-with-git-bash.cmd ./scripts/start-daemon.sh +else @./scripts/start-daemon.sh +endif # Stop all services stop: diff --git a/README.md b/README.md index 317a43f29..a527acd7c 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed Docker development guide. If you prefer running services locally: Prerequisite: complete the "Configuration" steps above first (`make config` and model API keys). `make dev` requires a valid configuration file (defaults to `config.yaml` in the project root; can be overridden via `DEER_FLOW_CONFIG_PATH`). +On Windows, run the local development flow from Git Bash. Native `cmd.exe` and PowerShell shells are not supported for the bash-based service scripts, and WSL is not guaranteed because some scripts rely on Git for Windows utilities such as `cygpath`. 1. **Check prerequisites**: ```bash diff --git a/README_zh.md b/README_zh.md index 3a838e72c..cbb3b5601 100644 --- a/README_zh.md +++ b/README_zh.md @@ -180,6 +180,7 @@ make down # 停止并移除容器 如果你更希望直接在本地启动各个服务: 前提:先完成上面的“配置”步骤(`make config` 和模型 API key 配置)。`make dev` 需要有效配置文件,默认读取项目根目录下的 `config.yaml`,也可以通过 `DEER_FLOW_CONFIG_PATH` 覆盖。 +在 Windows 上,请使用 Git Bash 运行本地开发流程。基于 bash 的服务脚本不支持直接在原生 `cmd.exe` 或 PowerShell 中执行,且 WSL 也不保证可用,因为部分脚本依赖 Git for Windows 的 `cygpath` 等工具。 1. **检查依赖环境**: ```bash diff --git a/scripts/check.py b/scripts/check.py index 650932376..d358e3086 100644 --- a/scripts/check.py +++ b/scripts/check.py @@ -29,6 +29,18 @@ def run_command(command: list[str]) -> Optional[str]: return result.stdout.strip() or result.stderr.strip() +def find_pnpm_command() -> Optional[list[str]]: + """Return a pnpm-compatible command that exists on this machine.""" + candidates = [["pnpm"], ["pnpm.cmd"]] + if shutil.which("corepack"): + candidates.append(["corepack", "pnpm"]) + + for command in candidates: + if shutil.which(command[0]): + return command + return None + + def parse_node_major(version_text: str) -> Optional[int]: version = version_text.strip() if version.startswith("v"): @@ -55,35 +67,39 @@ def main() -> int: if node_version: major = parse_node_major(node_version) if major is not None and major >= 22: - print(f" ✓ Node.js {node_version.lstrip('v')} (>= 22 required)") + print(f" OK Node.js {node_version.lstrip('v')} (>= 22 required)") else: print( - f" ✗ Node.js {node_version.lstrip('v')} found, but version 22+ is required" + f" FAIL Node.js {node_version.lstrip('v')} found, but version 22+ is required" ) print(" Install from: https://nodejs.org/") failed = True else: - print(" ✗ Unable to determine Node.js version") + print(" INFO Unable to determine Node.js version") print(" Install from: https://nodejs.org/") failed = True else: - print(" ✗ Node.js not found (version 22+ required)") + print(" FAIL Node.js not found (version 22+ required)") print(" Install from: https://nodejs.org/") failed = True print() print("Checking pnpm...") - pnpm_executable = shutil.which("pnpm.cmd") or shutil.which("pnpm") - if pnpm_executable: - pnpm_version = run_command([pnpm_executable, "-v"]) + pnpm_command = find_pnpm_command() + if pnpm_command: + pnpm_version = run_command([*pnpm_command, "-v"]) if pnpm_version: - print(f" ✓ pnpm {pnpm_version}") + if pnpm_command[0] == "corepack": + print(f" OK pnpm {pnpm_version} (via Corepack)") + else: + print(f" OK pnpm {pnpm_version}") else: - print(" ✗ Unable to determine pnpm version") + print(" INFO Unable to determine pnpm version") failed = True else: - print(" ✗ pnpm not found") + print(" FAIL pnpm not found") print(" Install: npm install -g pnpm") + print(" Or enable Corepack: corepack enable") print(" Or visit: https://pnpm.io/installation") failed = True @@ -92,13 +108,14 @@ def main() -> int: if shutil.which("uv"): uv_version_text = run_command(["uv", "--version"]) if uv_version_text: - uv_version = uv_version_text.split()[-1] - print(f" ✓ uv {uv_version}") + uv_version_parts = uv_version_text.split() + uv_version = uv_version_parts[1] if len(uv_version_parts) > 1 else uv_version_text + print(f" OK uv {uv_version}") else: - print(" ✗ Unable to determine uv version") + print(" INFO Unable to determine uv version") failed = True else: - print(" ✗ uv not found") + print(" FAIL uv not found") print(" Visit the official installation guide for your platform:") print(" https://docs.astral.sh/uv/getting-started/installation/") failed = True @@ -109,11 +126,11 @@ def main() -> int: nginx_version_text = run_command(["nginx", "-v"]) if nginx_version_text and "/" in nginx_version_text: nginx_version = nginx_version_text.split("/", 1)[1] - print(f" ✓ nginx {nginx_version}") + print(f" OK nginx {nginx_version}") else: - print(" ✓ nginx (version unknown)") + print(" INFO nginx (version unknown)") else: - print(" ✗ nginx not found") + print(" FAIL nginx not found") print(" macOS: brew install nginx") print(" Ubuntu: sudo apt install nginx") print(" Windows: use WSL for local mode or use Docker mode") @@ -123,7 +140,7 @@ def main() -> int: print() if not failed: print("==========================================") - print(" ✓ All dependencies are installed!") + print(" OK All dependencies are installed!") print("==========================================") print() print("You can now run:") @@ -134,7 +151,7 @@ def main() -> int: return 0 print("==========================================") - print(" ✗ Some dependencies are missing") + print(" FAIL Some dependencies are missing") print("==========================================") print() print("Please install the missing tools and run 'make check' again.") diff --git a/scripts/config-upgrade.sh b/scripts/config-upgrade.sh index 02d592608..96b0ceaf9 100755 --- a/scripts/config-upgrade.sh +++ b/scripts/config-upgrade.sh @@ -30,19 +30,28 @@ fi if [ -z "$CONFIG" ]; then echo "No config.yaml found — creating from example..." cp "$EXAMPLE" "$REPO_ROOT/config.yaml" - echo "✓ config.yaml created. Please review and set your API keys." + echo "OK config.yaml created. Please review and set your API keys." exit 0 fi # Use inline Python to do migrations + recursive merge with PyYAML -cd "$REPO_ROOT/backend" && uv run python3 -c " +if command -v cygpath >/dev/null 2>&1; then + CONFIG_WIN="$(cygpath -w "$CONFIG")" + EXAMPLE_WIN="$(cygpath -w "$EXAMPLE")" +else + CONFIG_WIN="$CONFIG" + EXAMPLE_WIN="$EXAMPLE" +fi + +cd "$REPO_ROOT/backend" && CONFIG_WIN_PATH="$CONFIG_WIN" EXAMPLE_WIN_PATH="$EXAMPLE_WIN" uv run python -c " +import os import sys, shutil, copy, re from pathlib import Path import yaml -config_path = Path('$CONFIG') -example_path = Path('$EXAMPLE') +config_path = Path(os.environ['CONFIG_WIN_PATH']) +example_path = Path(os.environ['EXAMPLE_WIN_PATH']) with open(config_path, encoding='utf-8') as f: raw_text = f.read() @@ -55,10 +64,10 @@ user_version = user.get('config_version', 0) example_version = example.get('config_version', 0) if user_version >= example_version: - print(f'✓ config.yaml is already up to date (version {user_version}).') + print(f'OK config.yaml is already up to date (version {user_version}).') sys.exit(0) -print(f'Upgrading config.yaml: version {user_version} → {example_version}') +print(f'Upgrading config.yaml: version {user_version} -> {example_version}') print() # ── Migrations ─────────────────────────────────────────────────────────── @@ -93,7 +102,7 @@ for version in range(user_version + 1, example_version + 1): for old, new in migration.get('replacements', []): if old in raw_text: raw_text = raw_text.replace(old, new) - migrated.append(f'{old} → {new}') + migrated.append(f'{old} -> {new}') # Re-parse after text migrations user = yaml.safe_load(raw_text) or {} @@ -141,6 +150,6 @@ if not migrated and not added: print('No changes needed (version bumped only).') print() -print(f'✓ config.yaml upgraded to version {example_version}.') +print(f'OK config.yaml upgraded to version {example_version}.') print(' Please review the changes and set any new required values.') " diff --git a/scripts/serve.sh b/scripts/serve.sh index d5d3b42cf..ae1c153ac 100755 --- a/scripts/serve.sh +++ b/scripts/serve.sh @@ -30,7 +30,15 @@ done if $DEV_MODE; then FRONTEND_CMD="pnpm run dev" else - FRONTEND_CMD="env BETTER_AUTH_SECRET=$(python3 -c 'import secrets; print(secrets.token_hex(16))') pnpm run preview" + if command -v python3 >/dev/null 2>&1; then + PYTHON_BIN="python3" + elif command -v python >/dev/null 2>&1; then + PYTHON_BIN="python" + else + echo "Python is required to generate BETTER_AUTH_SECRET, but neither python3 nor python was found." + exit 1 + fi + FRONTEND_CMD="env BETTER_AUTH_SECRET=$($PYTHON_BIN -c 'import secrets; print(secrets.token_hex(16))') pnpm run preview" fi # ── Stop existing services ──────────────────────────────────────────────────── @@ -121,6 +129,7 @@ trap cleanup INT TERM # ── Start services ──────────────────────────────────────────────────────────── mkdir -p logs +mkdir -p temp/client_body_temp temp/proxy_temp temp/fastcgi_temp temp/uwsgi_temp temp/scgi_temp if $DEV_MODE; then LANGGRAPH_EXTRA_FLAGS="--no-reload" diff --git a/scripts/start-daemon.sh b/scripts/start-daemon.sh index a60653dbc..96ee788e1 100755 --- a/scripts/start-daemon.sh +++ b/scripts/start-daemon.sh @@ -71,6 +71,7 @@ trap cleanup_on_failure INT TERM # ── Start services ──────────────────────────────────────────────────────────── mkdir -p logs +mkdir -p temp/client_body_temp temp/proxy_temp temp/fastcgi_temp temp/uwsgi_temp temp/scgi_temp echo "Starting LangGraph server..." nohup sh -c 'cd backend && NO_COLOR=1 uv run langgraph dev --no-browser --allow-blocking --no-reload > ../logs/langgraph.log 2>&1' &