build: establish unified project entry

This commit is contained in:
QiuSW
2026-08-03 18:55:32 +08:00
parent 637c341b95
commit 4f4a95a55e
6 changed files with 276 additions and 181 deletions
+129 -64
View File
@@ -1,84 +1,149 @@
#!/usr/bin/env pwsh
# cmbuyer 标准启动与验证入口(Windows PowerShell 版),与 init.sh 等价,二选一:
# - Windows 原生 PowerShell:./init.ps1
# - WSL / Git Bash:./init.sh
#
# 本项目是双产品结构:admin/(采购服务,Go)+ client/(采购工具,Python)。脚本按
# admin/go.mod 与 client/requirements.txt 两个初始化哨兵分别处理;未初始化的一端会跳过并
# 提示对应任务,不会把空目录误判为可运行项目,也不会静默通过。
#
# T-001 / T-002 / T-003 落地后,把下面的命令补全,并同步:
# docs/03-tech-stack.md、docs/00-ai-start-here.md、docs/current-state.md
# cmbuyer Windows 统一安装、验证与启动入口。
# 这个入口只处理开发环境和离线门禁,不连接设备,也不触发采购、下单或付款动作。
$ErrorActionPreference = "Stop"
Set-Location -Path $PSScriptRoot
# Go 工具链固定用本机版本,避免自动下载
# Go 工具链固定用本机版本,避免自动下载造成不可复现的环境漂移。
$env:GOTOOLCHAIN = "local"
$AdminDir = Join-Path $PSScriptRoot "admin"
$AdminDir = Join-Path $PSScriptRoot "admin"
$ClientDir = Join-Path $PSScriptRoot "client"
$Pending = @()
$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:(?<selector>\S+)(?:\s+\*)?\s+(?<path>.+?)\s*$')
if (-not $selectorMatch.Success) {
continue
}
$selector = $selectorMatch.Groups['selector'].Value
$pythonPath = $selectorMatch.Groups['path'].Value
$versionMatch = [regex]::Match($selector, '(?<major>\d+)\.(?<minor>\d+)(?:\.(?<patch>\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)"
# ------------------------------------------------------- 采购服务(admin/,Go)
if (Test-Path (Join-Path $AdminDir "go.mod") -PathType Leaf) {
Write-Host "==> 采购服务:admin/ 同步依赖"
Push-Location $AdminDir
try {
go mod download
Write-Host "==> 采购服务:admin/ 静态检查"
go vet ./...
Write-Host "==> 采购服务:admin/ 测试"
go test ./...
} finally {
Pop-Location
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 {
Write-Host "==> 采购服务:admin/go.mod 不存在,跳过。先做 T-001(初始化 admin/ Go 骨架)。"
$Pending += "T-001 初始化采购服务 admin/ Go 骨架"
# 默认 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"
}
# ---------------------------------------------------- 采购工具(client/,Python)
if (Test-Path (Join-Path $ClientDir "requirements.txt") -PathType Leaf) {
Write-Host "==> 采购工具:client/ 同步依赖"
Push-Location $ClientDir
try {
if (-not (Test-Path ".venv")) {
python -m venv .venv
}
& ".venv\Scripts\python.exe" -m pip install -q -r requirements.txt
Write-Host "==> 采购工具:client/ 语法检查"
& ".venv\Scripts\python.exe" -m compileall -q src tests
Write-Host "==> 采购工具:client/ 测试"
& ".venv\Scripts\python.exe" -m unittest discover -s tests -t .
} finally {
Pop-Location
}
} else {
Write-Host "==> 采购工具:client/requirements.txt 不存在,跳过。先做 T-002(初始化 client/ Python 骨架)。"
$Pending += "T-002 初始化采购工具 client/ Python 骨架"
# 后续所有采购工具检查与仓库 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
}
# ------------------------------------------------------------------ 文档自检
Write-Host "==> 校验 agent 上下文清单"
python scripts/validate_agent_context.py
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 ""
if ($Pending.Count -gt 0) {
Write-Host "==> 尚未初始化的部分:"
foreach ($item in $Pending) { Write-Host " - $item" }
Write-Host ""
Write-Host "当前仓库只有文档。按 docs/06-tasks.md 领取上述任务后再运行本脚本。"
exit 3
}
Write-Host "==> 启动命令"
Write-Host " 采购服务:cd admin ; go run ./cmd/server"
Write-Host " 采购工具:cd client ; .venv\Scripts\python.exe src/main.py"
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 "基础验证失败时先修基线,不要在坏的起点上继续叠新功能。"
Write-Host "真机连接与设备验收只能由人工完成,agent 不得据此把任务标为 DONE。"
Write-Host "真机连接与设备验收只能由人工完成;本入口不执行任何采购、下单或付款动作。"