#!/usr/bin/env pwsh # cmbuyer Windows 统一安装、验证与启动入口。 # 这个入口只处理开发环境和离线门禁,不连接设备,也不触发采购、下单或付款动作。 $ErrorActionPreference = "Stop" Set-Location -Path $PSScriptRoot # Go 工具链固定用本机版本,避免自动下载造成不可复现的环境漂移。 $env:GOTOOLCHAIN = "local" $AdminDir = Join-Path $PSScriptRoot "admin" $ClientDir = Join-Path $PSScriptRoot "client" $ClientVenv = Join-Path $ClientDir ".venv" $ClientPython = Join-Path $ClientVenv "Scripts\python.exe" function Fail([string]$Message) { throw $Message } function Require-Directory([string]$Path, [string]$Name) { if (-not (Test-Path -LiteralPath $Path -PathType Container)) { Fail "$Name 目录不存在:$Path。请先完成对应初始化任务。" } } function Require-File([string]$Path, [string]$Name) { if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { Fail "$Name 不存在:$Path。请先完成对应初始化任务。" } } function Require-Command([string]$Name, [string]$Hint) { if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { Fail "缺少 $Name。$Hint" } } function Invoke-Step([string]$Description, [scriptblock]$Action) { Write-Host "==> $Description" & $Action if ($LASTEXITCODE -ne 0) { Fail "$Description 失败,退出码:$LASTEXITCODE" } } function Confirm-Python311OrNewer([string]$PythonExecutable, [string]$Context) { & $PythonExecutable -c "import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)" if ($LASTEXITCODE -ne 0) { Fail "$Context 必须使用 Python 3.11+。不会自动覆盖既有虚拟环境;请手工删除 client/.venv 后重试。" } } function Select-PythonLauncherSelector() { $launcherLines = @(& py -0p) if ($LASTEXITCODE -ne 0) { Fail "无法从 Python Launcher 枚举已安装版本。请安装 Python 3.11+ 并确认 py -0p 可运行。" } $minimumVersion = [version]::new(3, 11) $candidates = @() foreach ($line in $launcherLines) { $selectorMatch = [regex]::Match([string]$line, '^\s*-V:(?\S+)(?:\s+\*)?\s+(?.+?)\s*$') if (-not $selectorMatch.Success) { continue } $selector = $selectorMatch.Groups['selector'].Value $pythonPath = $selectorMatch.Groups['path'].Value $versionMatch = [regex]::Match($selector, '(?\d+)\.(?\d+)(?:\.(?\d+))?') if (-not $versionMatch.Success) { continue } $patch = if ($versionMatch.Groups['patch'].Success) { [int]$versionMatch.Groups['patch'].Value } else { 0 } $version = [version]::new( [int]$versionMatch.Groups['major'].Value, [int]$versionMatch.Groups['minor'].Value, $patch ) if ($version -ge $minimumVersion) { $candidates += [PSCustomObject]@{ Selector = $selector; Version = $version; Path = $pythonPath } } } if ($candidates.Count -eq 0) { Fail "Python Launcher 未发现 Python 3.11+。当前默认 python 不会作为回退;请安装合规版本后重试。" } $selected = $candidates | Sort-Object @{ Expression = 'Version'; Descending = $true }, @{ Expression = 'Selector'; Descending = $false } | Select-Object -First 1 Write-Host "==> 选择 Python Launcher 解释器:-V:$($selected.Selector)($($selected.Version))" return $selected } Write-Host "==> 当前目录: $($PWD.Path)" Require-Directory $AdminDir "采购服务 admin" Require-File (Join-Path $AdminDir "go.mod") "采购服务哨兵 admin/go.mod" Require-Directory $ClientDir "采购工具 client" Require-File (Join-Path $ClientDir "requirements.txt") "采购工具哨兵 client/requirements.txt" Require-File (Join-Path $ClientDir "pyproject.toml") "采购工具打包配置 client/pyproject.toml" Require-Command "go" "请安装 Go 1.23+ 并重新打开 PowerShell。" if (Test-Path -LiteralPath $ClientVenv) { if (-not (Test-Path -LiteralPath $ClientVenv -PathType Container)) { Fail "client/.venv 不是目录。不会覆盖该路径,请人工处理后重试。" } Require-File $ClientPython "既有 client/.venv Python" Confirm-Python311OrNewer $ClientPython "既有 client/.venv" } else { # 默认 python 在本机可能仍指向 3.10;仅在需要新建 venv 时,从 Launcher 中确定性选择最高的合规版本。 Require-Command "py" "请安装 Python Launcher 和 Python 3.11+。" $PythonRuntime = Select-PythonLauncherSelector Require-File $PythonRuntime.Path "Python Launcher 选择的解释器" Invoke-Step "验证所选 Python Launcher 解释器" { & $PythonRuntime.Path -c "import sys; print(sys.version); raise SystemExit(0 if sys.version_info >= (3, 11) else 1)" } Invoke-Step "创建所选 Python 3.11+ 采购工具虚拟环境" { & $PythonRuntime.Path -m venv $ClientVenv } Require-File $ClientPython "新建 client/.venv Python" Confirm-Python311OrNewer $ClientPython "新建 client/.venv" } # 后续所有采购工具检查与仓库 validator 均使用同一个合规 venv 解释器,避免混用宿主 Python。 Invoke-Step "验证采购工具虚拟环境 Python" { & $ClientPython -c "import sys; print(sys.version); raise SystemExit(0 if sys.version_info >= (3, 11) else 1)" } Push-Location $AdminDir try { Invoke-Step "采购服务:admin/ 同步依赖" { go mod download } Invoke-Step "采购服务:admin/ 测试" { go test ./... } Invoke-Step "采购服务:admin/ 静态检查" { go vet ./... } Invoke-Step "采购服务:admin/ 构建" { go build ./... } } finally { Pop-Location } Push-Location $ClientDir try { Invoke-Step "采购工具:以 editable 方式安装" { & $ClientPython -m pip install -e . } Invoke-Step "采购工具:验证已安装包" { & $ClientPython -c "import cmbuyer_client; print(cmbuyer_client.__version__)" } Invoke-Step "采购工具:测试" { & $ClientPython -m unittest discover -s tests -t . } Invoke-Step "采购工具:语法检查" { & $ClientPython -m compileall -q src tests scripts } } finally { Pop-Location } Invoke-Step "校验 agent 上下文清单" { & $ClientPython scripts/validate_agent_context.py } Write-Host "" Write-Host "==> 基线验证通过。启动命令:" Write-Host " 采购服务:cd admin; go run ./cmd/server" Write-Host " 采购工具:cd client; .\.venv\Scripts\python.exe -m cmbuyer_client" Write-Host "" Write-Host "真机连接与设备验收只能由人工完成;本入口不执行任何采购、下单或付款动作。"