build: add self-contained portable publish variant (v0.2.1)
PublishSerialMcp target now inherits the GUI's SelfContained setting, so a --self-contained true publish also produces a runtime-free ETTerms.SerialMcp. Document both publish variants in ARCHITECTURE.md / CLAUDE.md: framework-dependent ETTerms_v{Version}\ and portable ETTerms_v{Version}_portable\ (no .NET runtime install needed).
This commit is contained in:
+24
-7
@@ -513,23 +513,40 @@ ETTerms_v0.2.0\
|
|||||||
```
|
```
|
||||||
|
|
||||||
**規則:**
|
**規則:**
|
||||||
1. **GUI publish 會自動帶上 MCP**:`ETTerms.csproj` 有 `PublishSerialMcp` target(`AfterTargets="Publish"`),會把 `ETTerms.SerialMcp` 一併發佈到 `ETTerms_v{Version}\ETTerms.SerialMcp\` **子資料夾**(與 GUI 相依 dll 隔離)。因此**只要發佈 GUI 一個指令**即可,不必再單獨發 MCP。
|
1. **GUI publish 會自動帶上 MCP**:`ETTerms.csproj` 有 `PublishSerialMcp` target(`AfterTargets="Publish"`),會把 `ETTerms.SerialMcp` 一併發佈到 `<publish>\ETTerms.SerialMcp\` **子資料夾**(與 GUI 相依 dll 隔離)。因此**只要發佈 GUI 一個指令**即可,不必再單獨發 MCP。
|
||||||
- 對齊 `McpRegistrar.ResolveServerExe()`:它解析的 `<ETTerms.exe>\ETTerms.SerialMcp\ETTerms.SerialMcp.exe` 因此**必定存在**,AI MCP 一鍵設定寫進去的路徑才不會落空。
|
- 對齊 `McpRegistrar.ResolveServerExe()`:它解析的 `<ETTerms.exe>\ETTerms.SerialMcp\ETTerms.SerialMcp.exe` 因此**必定存在**,AI MCP 一鍵設定寫進去的路徑才不會落空。
|
||||||
|
- MCP 子發佈會**跟隨 GUI 的 `SelfContained` 設定**(target 內以 `$(SelfContained)` 傳入):框架相依版的 MCP 也框架相依;portable 版的 MCP 也免 runtime。
|
||||||
2. **主 exe 改名**:`dotnet publish` 產生的 `ETTerms.exe` 重新命名為 **`ETTerms v{Version}.exe`**。
|
2. **主 exe 改名**:`dotnet publish` 產生的 `ETTerms.exe` 重新命名為 **`ETTerms v{Version}.exe`**。
|
||||||
- 可安全改名:.NET apphost 內部記錄要載入的 `ETTerms.dll`,**不靠自身檔名**,改名後仍正常啟動。
|
- 可安全改名:.NET apphost 內部記錄要載入的 `ETTerms.dll`,**不靠自身檔名**,改名後仍正常啟動。
|
||||||
3. **框架相依**:`--self-contained false -r win-x64`(目標機需已裝 .NET 8 Desktop Runtime)。
|
|
||||||
|
|
||||||
**PowerShell 範例:**
|
### 兩種發佈版本(兩個都要產出)
|
||||||
|
|
||||||
|
| 版本 | 資料夾 | 指令旗標 | 需 .NET Runtime? | 用途 |
|
||||||
|
|------|--------|----------|-------------------|------|
|
||||||
|
| **A. 框架相依(預設)** | `ETTerms_v{Version}\` | `--self-contained false` | ✅ 需先裝 .NET 8 Desktop Runtime | 體積小;給已具備 runtime 的環境 |
|
||||||
|
| **B. Portable(免安裝)** | `ETTerms_v{Version}_portable\` | `--self-contained true` | ❌ 不需要,runtime 已內含 | 體積大(約 240MB);給受 MIS 管控、不便裝 runtime 的環境,解壓即用、免系統管理員權限 |
|
||||||
|
|
||||||
|
> ⚠️ **不要開 trimming**(`PublishTrimmed`):WinForms 大量用反射,trim 後易在執行時出錯。
|
||||||
|
|
||||||
|
**PowerShell 範例(一次產出兩種):**
|
||||||
```powershell
|
```powershell
|
||||||
$ver = ([regex]::Match((Get-Content src\ETTerms\ETTerms.csproj -Raw), '<Version>([^<]+)</Version>')).Groups[1].Value
|
$ver = ([regex]::Match((Get-Content src\ETTerms\ETTerms.csproj -Raw), '<Version>([^<]+)</Version>')).Groups[1].Value
|
||||||
$root = "src\ETTerms\Publish\ETTerms_v$ver"
|
|
||||||
if (Test-Path $root) { Remove-Item $root -Recurse -Force }
|
|
||||||
|
|
||||||
# 發佈 GUI;ETTerms.SerialMcp 由 PublishSerialMcp target 自動發到 $root\ETTerms.SerialMcp\
|
# A. 框架相依版 → ETTerms_v{Version}\
|
||||||
|
$root = "src\ETTerms\Publish\ETTerms_v$ver"
|
||||||
|
if (Test-Path $root) { Remove-Item $root -Recurse -Force }
|
||||||
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false -o $root
|
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false -o $root
|
||||||
Rename-Item (Join-Path $root "ETTerms.exe") "ETTerms v$ver.exe"
|
Rename-Item (Join-Path $root "ETTerms.exe") "ETTerms v$ver.exe"
|
||||||
|
|
||||||
|
# B. Portable 免安裝版 → ETTerms_v{Version}_portable\(runtime 已內含;MCP 也跟著 self-contained)
|
||||||
|
$proot = "src\ETTerms\Publish\ETTerms_v${ver}_portable"
|
||||||
|
if (Test-Path $proot) { Remove-Item $proot -Recurse -Force }
|
||||||
|
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained true -o $proot
|
||||||
|
Rename-Item (Join-Path $proot "ETTerms.exe") "ETTerms v$ver.exe"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 兩版的 `ETTerms.SerialMcp\` 子資料夾都由 `PublishSerialMcp` target 自動產生;portable 版的 MCP 也是 self-contained,故 AI MCP 功能在無 runtime 環境同樣可用。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Development Phases
|
## Development Phases
|
||||||
|
|||||||
@@ -40,14 +40,21 @@ dotnet run --project src\ETTerms\ETTerms.csproj
|
|||||||
# 加套件
|
# 加套件
|
||||||
dotnet add src\ETTerms package SSH.NET
|
dotnet add src\ETTerms package SSH.NET
|
||||||
|
|
||||||
# 打包(見「Publish / 打包慣例」,輸出固定到 src\ETTerms\Publish\ETTerms_v{Version}\)
|
# 打包(見「Publish / 打包慣例」)—— 兩種版本都產出,輸出到 src\ETTerms\Publish\
|
||||||
# GUI publish 會「自動」把 ETTerms.SerialMcp 一併發到 \ETTerms.SerialMcp\ 子資料夾
|
# GUI publish 會「自動」把 ETTerms.SerialMcp 一併發到 \ETTerms.SerialMcp\ 子資料夾
|
||||||
# (ETTerms.csproj 的 PublishSerialMcp target,AfterTargets=Publish),不必再單獨發 MCP。
|
# (ETTerms.csproj 的 PublishSerialMcp target,AfterTargets=Publish),且 MCP 跟隨 GUI 的 self-contained 設定。
|
||||||
$ver = ([regex]::Match((Get-Content src\ETTerms\ETTerms.csproj -Raw), '<Version>([^<]+)</Version>')).Groups[1].Value
|
$ver = ([regex]::Match((Get-Content src\ETTerms\ETTerms.csproj -Raw), '<Version>([^<]+)</Version>')).Groups[1].Value
|
||||||
|
|
||||||
|
# A. 框架相依版(需目標機已裝 .NET 8 Desktop Runtime)→ ETTerms_v{Version}\
|
||||||
$root = "src\ETTerms\Publish\ETTerms_v$ver"
|
$root = "src\ETTerms\Publish\ETTerms_v$ver"
|
||||||
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false -o $root
|
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false -o $root
|
||||||
Rename-Item (Join-Path $root "ETTerms.exe") "ETTerms v$ver.exe"
|
Rename-Item (Join-Path $root "ETTerms.exe") "ETTerms v$ver.exe"
|
||||||
|
|
||||||
|
# B. Portable 免安裝版(runtime 內含,免裝、免管理員)→ ETTerms_v{Version}_portable\
|
||||||
|
$proot = "src\ETTerms\Publish\ETTerms_v${ver}_portable"
|
||||||
|
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained true -o $proot
|
||||||
|
Rename-Item (Join-Path $proot "ETTerms.exe") "ETTerms v$ver.exe"
|
||||||
|
|
||||||
# 註冊 Serial MCP server(給 AI agent 操作 serial)
|
# 註冊 Serial MCP server(給 AI agent 操作 serial)
|
||||||
# 推薦:GUI Settings → AI MCP 分頁,對 Claude Code / Kiro 按 Setup 一鍵註冊(McpRegistrar)。
|
# 推薦:GUI Settings → AI MCP 分頁,對 Claude Code / Kiro 按 Setup 一鍵註冊(McpRegistrar)。
|
||||||
# 或手動 CLI:
|
# 或手動 CLI:
|
||||||
@@ -57,7 +64,7 @@ kiro-cli mcp add --name serial --command dotnet --args "run --project src\ETTerm
|
|||||||
## 開發慣例
|
## 開發慣例
|
||||||
|
|
||||||
- **命名:** PascalCase 類別 / 方法,`_camelCase` 私有欄位;檔名 = 類別名。
|
- **命名:** PascalCase 類別 / 方法,`_camelCase` 私有欄位;檔名 = 類別名。
|
||||||
- **Publish / 打包:** 固定輸出到 `src\ETTerms\Publish\ETTerms_v{Version}\`(`{Version}` 取自 csproj `<Version>`);主 exe 改名為 `ETTerms v{Version}.exe`;`ETTerms.SerialMcp` 一併發到其下 `ETTerms.SerialMcp\` 子資料夾。框架相依(`--self-contained false -r win-x64`)。詳見 [ARCHITECTURE.md](ARCHITECTURE.md#publish--打包慣例)。
|
- **Publish / 打包:** 輸出到 `src\ETTerms\Publish\`;主 exe 改名為 `ETTerms v{Version}.exe`;`ETTerms.SerialMcp` 一併發到其下 `ETTerms.SerialMcp\` 子資料夾(且跟隨 GUI 的 self-contained 設定)。**兩種版本都產出**:框架相依 `ETTerms_v{Version}\`(`--self-contained false`,需裝 .NET 8 Desktop Runtime)+ portable 免安裝 `ETTerms_v{Version}_portable\`(`--self-contained true`,runtime 內含)。不要開 trimming(WinForms 反射)。詳見 [ARCHITECTURE.md](ARCHITECTURE.md#publish--打包慣例)。
|
||||||
- **分層:** UI(`App/`)只認 `ISessionChannel` 抽象,不直接相依 SSH.NET / SerialPort。
|
- **分層:** UI(`App/`)只認 `ISessionChannel` 抽象,不直接相依 SSH.NET / SerialPort。
|
||||||
- **執行緒:** channel I/O 在背景;所有 UI 更新一律 `Control.Invoke` 回 UI thread。
|
- **執行緒:** channel I/O 在背景;所有 UI 更新一律 `Control.Invoke` 回 UI thread。
|
||||||
- **commit:** 走 Conventional Commits(`feat:` / `fix:` / `refactor:` …)。
|
- **commit:** 走 Conventional Commits(`feat:` / `fix:` / `refactor:` …)。
|
||||||
|
|||||||
@@ -37,14 +37,17 @@
|
|||||||
這樣單一 `dotnet publish src\ETTerms` 就會產生完整自洽的 bundle,
|
這樣單一 `dotnet publish src\ETTerms` 就會產生完整自洽的 bundle,
|
||||||
且 McpRegistrar.ResolveServerExe() 解析的 <ETTerms.exe>\ETTerms.SerialMcp\ETTerms.SerialMcp.exe 必定存在。
|
且 McpRegistrar.ResolveServerExe() 解析的 <ETTerms.exe>\ETTerms.SerialMcp\ETTerms.SerialMcp.exe 必定存在。
|
||||||
刻意放子資料夾:與 GUI 的相依 dll 隔離,避免互相覆蓋。
|
刻意放子資料夾:與 GUI 的相依 dll 隔離,避免互相覆蓋。
|
||||||
|
MCP 跟隨 GUI 的 self-contained 設定:框架相依版 → MCP 也框架相依;portable(self-contained)版 → MCP 也免 runtime。
|
||||||
-->
|
-->
|
||||||
<Target Name="PublishSerialMcp" AfterTargets="Publish">
|
<Target Name="PublishSerialMcp" AfterTargets="Publish">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_McpRid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</_McpRid>
|
<_McpRid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</_McpRid>
|
||||||
<_McpRid Condition="'$(RuntimeIdentifier)' == ''">win-x64</_McpRid>
|
<_McpRid Condition="'$(RuntimeIdentifier)' == ''">win-x64</_McpRid>
|
||||||
|
<_McpSelfContained Condition="'$(SelfContained)' != ''">$(SelfContained)</_McpSelfContained>
|
||||||
|
<_McpSelfContained Condition="'$(SelfContained)' == ''">false</_McpSelfContained>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Message Importance="high" Text="[ETTerms] Publishing ETTerms.SerialMcp -> $(PublishDir)ETTerms.SerialMcp" />
|
<Message Importance="high" Text="[ETTerms] Publishing ETTerms.SerialMcp -> $(PublishDir)ETTerms.SerialMcp (self-contained=$(_McpSelfContained))" />
|
||||||
<Exec Command="dotnet publish "$(MSBuildThisFileDirectory)..\ETTerms.SerialMcp\ETTerms.SerialMcp.csproj" -c $(Configuration) -r $(_McpRid) --self-contained false -o "$(PublishDir)ETTerms.SerialMcp"" />
|
<Exec Command="dotnet publish "$(MSBuildThisFileDirectory)..\ETTerms.SerialMcp\ETTerms.SerialMcp.csproj" -c $(Configuration) -r $(_McpRid) --self-contained $(_McpSelfContained) -o "$(PublishDir)ETTerms.SerialMcp"" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user