Files
kompas3d-mcp/tools/dev/rebuild-mcp.ps1
T
mikhail cb43b84249 Dev-лаунчер: сервер запускается из теневой копии сборки
Пока MCP-сервер работал прямо из src\...\bin, он держал открытым kompas-mcp.exe
и Kompas.Mcp.Core.dll — dotnet build падал с MSB3027 от каждой живой сессии.
Лаунчер копирует сборку в %LOCALAPPDATA%\kompas-mcp\dev\<отпечаток> и запускает
её оттуда, поэтому bin свободен всегда.

- tools/dev/KompasMcpDev.psm1 — отпечаток каталога сборки, теневая копия через
  атомарный Directory.Move, уборка старых копий мимо занятых живым процессом,
  сессионный mutex на одновременный старт нескольких сессий;
- tools/dev/launch-dev-mcp.ps1 — точка входа для .mcp.json, инвариант «в stdout
  до старта сервера не уходит ни байта»;
- tools/dev/rebuild-mcp.ps1 — -Check компилирует мимо bin (~2 с при живом
  сервере), обычный режим отказывается заранее и называет блокирующие PID;
- tools/tests/DevLauncher.Tests.ps1 — Pester на все три файла.

Раздел «The rebuild cycle» в CLAUDE.md (2c56511) уже описывал эту схему.
2026-07-31 11:51:59 +03:00

103 lines
5.3 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#requires -Version 7.0
<#
.SYNOPSIS
Сборка MCP-сервера для dev-цикла: с диагностикой блокировки bin живыми сессиями.
.DESCRIPTION
Три режима, по возрастанию стоимости:
-Check компиляция мимо bin (сервер можно не останавливать) — «оно вообще собирается?»
(по умолчанию) полная сборка в bin + юнит-тесты — новый бинарь для теневого запуска
-NoTest то же без юнит-тестов
Каталог bin занят, пока хотя бы одна сессия MCP запущена ПРЯМО из него (см. .mcp.json).
С tools/dev/launch-dev-mcp.ps1 сервер работает из теневой копии, и bin свободен всегда.
.EXAMPLE
pwsh -NoProfile -File tools/dev/rebuild-mcp.ps1 -Check
.EXAMPLE
pwsh -NoProfile -File tools/dev/rebuild-mcp.ps1
#>
[CmdletBinding()]
param(
[switch] $Check,
[switch] $NoTest
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$repo = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
$binDir = Join-Path $repo 'src\Kompas.Mcp.Host\bin\x64\Release\net8.0-windows'
function Get-BlockingProcess {
param([Parameter(Mandatory)] [string] $Directory)
$target = $Directory.TrimEnd('\').ToLowerInvariant()
$found = @()
foreach ($p in (Get-Process -Name 'kompas-mcp' -ErrorAction SilentlyContinue)) {
try {
if (-not $p.Path) { continue }
if ((Split-Path -Parent $p.Path).TrimEnd('\').ToLowerInvariant() -eq $target) { $found += $p }
}
catch {
# .Path недоступен (чужой пользователь / процесс уже умер) — считать такой
# процесс блокирующим нельзя, но и молчать нехорошо: сборка ниже всё равно
# покажет настоящего держателя файла в тексте MSB3027.
Write-Verbose "не удалось прочитать путь процесса $($p.Id): $($_.Exception.Message)"
}
}
return $found
}
if ($Check) {
# Стабильный (не случайный) каталог — чтобы работала инкрементальная сборка.
# Перенаправляем ТОЛЬКО BaseOutputPath: если увести ещё и BaseIntermediateOutputPath,
# старый obj\ в дереве остаётся видимым для glob'а и даёт CS0579 «Повторяющийся
# атрибут» на сгенерированном AssemblyInfo. Обратная сторона — obj\ остаётся общим
# с обычной сборкой, поэтому два параллельных прогона топчут инкрементальный кэш
# друг друга (безопасно, но лишняя перекомпиляция).
$stamp = [System.BitConverter]::ToString(
[System.Security.Cryptography.SHA256]::HashData(
[System.Text.Encoding]::UTF8.GetBytes($repo.ToLowerInvariant()))[0..3]).Replace('-', '')
$out = Join-Path ([System.IO.Path]::GetTempPath()) "kompas-mcp-check\$stamp\"
Write-Host "Проверка компиляции (bin не трогаем, выход → $out)" -ForegroundColor Cyan
dotnet build -c Release -p:BaseOutputPath=$out
exit $LASTEXITCODE
}
$blocking = Get-BlockingProcess -Directory $binDir
if ($blocking.Count -gt 0) {
Write-Host 'Сборка невозможна: bin занят живыми сессиями MCP.' -ForegroundColor Red
foreach ($p in $blocking) {
Write-Host (" PID {0}, запущен {1}" -f $p.Id, $p.StartTime.ToString('HH:mm:ss'))
}
Write-Host ''
Write-Host 'Варианты:' -ForegroundColor Yellow
Write-Host ' 1. Перевести .mcp.json на tools/dev/launch-dev-mcp.ps1 (теневой запуск) — тогда'
Write-Host ' bin свободен всегда, и эта ситуация больше не повторится.'
Write-Host ' 2. Закрыть все сессии Claude Code с этим MCP и повторить.'
Write-Host ' 3. Разовая проверка компиляции без остановки сервера: -Check'
exit 2
}
Write-Host 'Сборка (Release)…' -ForegroundColor Cyan
dotnet build -c Release
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if (-not $NoTest) {
Write-Host 'Юнит-тесты…' -ForegroundColor Cyan
dotnet test --filter 'Category=Unit' --nologo
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
Import-Module (Join-Path $PSScriptRoot 'KompasMcpDev.psm1') -Force
$fp = Get-KompasMcpBuildFingerprint -Directory $binDir
Write-Host ''
Write-Host "Готово. Отпечаток сборки: $fp" -ForegroundColor Green
Write-Host 'Сервер подхватит её при следующем запуске процесса MCP:'
Write-Host ' /mcp → переподключить kompas (если клиент умеет), иначе — перезапуск сессии.'
Write-Host 'После переподключения: kompas_status покажет живое соединение; эскизы по id теряются.'