SeatFlow 构建命令、多平台发布脚本、确定性构建、GitCommit 自动生成和 SHA256 校验表
本页目录
全部文档
构建与发布
构建
环境要求
- .NET 10 SDK
- Windows / macOS(需要Apple开发者账号) / Linux
- 推荐 IDE:Rider、VS Code + C# 扩展、Visual Studio 2022+
构建命令
# 构建全部 9 个项目(使用 .slnx)
dotnet build
# 构建特定项目
dotnet build src/SeatFlow.Desktop/SeatFlow.Desktop.csproj
# 还原 NuGet 依赖
dotnet restore
解决方案结构
SeatFlow.slnx 使用新的 XML 格式(.slnx),包含 9 个项目:
| 项目 | 用途 |
|---|---|
SeatFlow.Core |
领域核心 |
SeatFlow.Core.Tests |
Core 层测试 |
SeatFlow.Application |
应用层 |
SeatFlow.Application.Tests |
Application 层测试 |
SeatFlow.Infrastructure |
基础设施 |
SeatFlow.Infrastructure.Tests |
Infrastructure 层测试 |
SeatFlow.Presentation.Avalonia |
共享 UI 类库(桌面 + 浏览器) |
SeatFlow.Desktop |
桌面启动壳 |
SeatFlow.Browser |
浏览器 WASM 启动壳 |
项目配置要点
共享 UI 类库(SeatFlow.Presentation.Avalonia.csproj)同时面向桌面与浏览器:
<TargetFrameworks>net10.0;net10.0-browser</TargetFrameworks>
<!-- 编译绑定 -->
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<!-- Designer.cs 条件编译 -->
<Compile Remove="Lang\Resources.Designer.cs"
Condition="!Exists('Lang\Resources.Designer.cs')" />
桌面壳(SeatFlow.Desktop.csproj)输出 EXE 文件名为 SeatFlow:
<!-- 输出文件名 -->
<AssemblyName>SeatFlow</AssemblyName>
<!-- 输出 EXE 为 SeatFlow.exe,而非 SeatFlow.Desktop.exe -->
<!-- 抑制 DI 构造函数警告 -->
<NoWarn>$(NoWarn);AVLN3001</NoWarn>
<!-- Windows DPI 感知 -->
<ApplicationManifest>app.manifest</ApplicationManifest>
确定性构建
项目配置了确定性构建,确保相同源码始终产生相同的输出哈希:
<Deterministic>true</Deterministic>
<PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)'))=./</PathMap>
<Deterministic>true</Deterministic>:启用确定性编译,相同源码 → 相同 IL<PathMap>:将绝对路径映射为相对路径./,消除构建机器的路径差异
排查非确定性构建
如果多次 dotnet build 产生不同的输出哈希,检查以下常见原因:
- 自动生成的
GitCommit.g.cs是否包含提交哈希(每次提交不同,但相同提交应相同) - NuGet 包还原是否一致
- 时间戳属性(如
<SourceRevisionId>、<Version>)是否在构建间变化
GitCommit 自动生成
构建时自动生成 GitCommit.g.cs 文件,包含当前 git 提交哈希:
// $(IntermediateOutputPath)Generated\GitCommit.g.cs
internal static class GitCommit
{
public const string Hash = "abc1234";
}
实现机制
MSBuild 目标 GenerateGitCommit 在每次构建前运行:
<Target Name="GenerateGitCommit" BeforeTargets="BeforeBuild">
<Exec Command="git rev-parse --short HEAD"
ConsoleToMSBuild="true"
StandardOutputImportance="low"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" />
</Exec>
<PropertyGroup>
<GitCommitHash Condition="'$(GitCommitHash)' == ''">unknown-commit-id</GitCommitHash>
</PropertyGroup>
<WriteLinesToFile
File="$(IntermediateOutputPath)Generated\GitCommit.g.cs"
Lines="...
public const string Hash = "$(GitCommitHash)";..."
Overwrite="true" />
</Target>
- 生成的文件位于
$(IntermediateOutputPath)Generated\GitCommit.g.cs(如obj/Debug/net10.0/Generated/) - 通过
obj/目录的.gitignore规则间接排除,不提交到版本控制 - git 不可用时回退到
"unknown-commit-id"
在代码中的使用
// TelemetryService.cs — 上报时附加提交标识
var commitId = GitCommit.Hash;
// AboutViewModel.cs — 版本号包含提交 ID
Version = $"{VersionInfo.Version}-{VersionInfo.CommitId}";
显示格式示例:1.5.0-abc1234
发布脚本
scripts/build/publish.sh(Linux/macOS)和 scripts/build/publish.ps1(Windows)是多平台发布工具,支持交互式和 CLI 模式。
交互模式
cd scripts/build
./publish.sh # 启动 TUI 交互界面
# 在交互界面中选择:
# - 发布类型(自包含/框架依赖/全部)
# - 目标平台(Windows x64、Linux x64、macOS x64/arm64)
# - 构建配置(Debug/Release)
# - 可选选项(裁剪、AOT)
CLI 模式
# 语法: publish.sh <mode> <config> [opt] [suffix] [version] [clean] [aot]
# 全平台自包含+框架依赖,裁剪+AOT
./publish.sh both Release opt "" "1.2.1" clean aot
# 仅 Windows x64 自包含发布
./publish.sh full Release "" "" "1.2.1" clean
# 仅框架依赖发布(不包含运行时)
./publish.sh slim Release "" "" "1.2.1"
# 为已有发布文件生成 SHA256 校验表
./publish.sh hash
发布类型
| 类型 | 参数 | 说明 |
|---|---|---|
| 全部(both) | both |
同时生成自包含和框架依赖两种发布 |
| 自包含(full) | full |
包含 .NET 运行时,无需预装 SDK |
| 框架依赖(slim) | slim |
不包含运行时,文件更小 |
发布选项
| 选项 | 参数 | 说明 |
|---|---|---|
| 清理 | clean |
构建前清理输出目录 |
| AOT | aot |
使用 Native AOT 预编译(仅自包含) |
| 裁剪 | opt |
启用 IL 裁剪减小体积(仅自包含) |
目标平台
| 平台 | RID | 说明 |
|---|---|---|
| Windows | win-x64 |
输出 .exe + .zip |
| Linux | linux-x64 |
输出 .tar.gz |
| macOS Intel | osx-x64 |
输出 .tar.gz |
| macOS Apple Silicon | osx-arm64 |
输出 .tar.gz |
SHA256 校验表
publish.sh hash 扫描 publish/ 目录下所有 SeatFlow-* 文件,输出 Markdown 格式的 SHA256 校验表到标准输出。校验表同时会嵌入 GitHub Release 正文中。
| File | SHA256 |
|------|--------|
| SeatFlow-1.2.1-win-x64.zip | a1b2c3d4... |
| SeatFlow-1.2.1-linux-x64.tar.gz | e5f6g7h8... |
| SeatFlow-1.2.1-osx-arm64.tar.gz | i9j0k1l2... |
清理脚本
scripts/build/clean.sh 和 scripts/build/clean.ps1 递归清理所有项目的 bin/ 和 obj/ 目录。
cd scripts/build
# 确认后删除
./clean.sh
# 预览模式(dry-run)
./clean.sh -n
# 强制删除(跳过确认)
./clean.sh -f
运行应用
# 开发模式运行
dotnet run --project src/SeatFlow.Desktop
# 发布后运行(自包含)
./publish/SeatFlow_1.2.1_linux-x64/SeatFlow
WatchdogService(仅桌面)
生产运行时,WatchdogService 会监测 UI 线程响应。如果 UI 线程卡顿超过 45 秒,服务会:
- 收集进程信息(PID、内存、线程数、句柄数、CPU 时间)、所有线程状态和托管线程池信息
- 将诊断信息写入
err_<yyyyMMdd-HHmmss>.log - 尝试弹出错误对话框
- 通过
Environment.Exit(1)强制退出应用
这在开发调试时需要注意——如果断点暂停超过 45 秒,进程会被杀死。
Velopack 打包(桌面)
SeatFlow 通过 release.py 编排完整的 Velopack 打包流程,包括:
- 自动更新支持
- 增量更新(delta updates)
- 静默安装和卸载
- 生成
.nupkg+ 安装程序 +releases.{channel}.json更新源
Velopack 深度集成在桌面壳:Program.cs 通过 VelopackApp.Build() 注册钩子,UpdateService 使用 UpdateManager 实现自动更新检查和下载,VelopackLocator 用于运行时目录检测。
详见 ADR-010。
Web(浏览器)发布
浏览器版发布为纯静态站点,无需安装程序。发布前需安装 WASM 工作负载:
dotnet workload install wasm-tools
发布命令:
# 直接发布浏览器壳,产物为可静态托管的 wwwroot/
dotnet publish src/SeatFlow.Browser -c Release
# 或使用发布脚本
cd scripts/build
./publish.sh web Release
本地验证:
dotnet serve -d src/SeatFlow.Browser/bin/Release/net10.0-browser/publish/wwwroot
静态托管要点
- 单线程变体(
WasmEnableThreads=false)无需 COOP/COEP 响应头 .wasm文件需配置application/wasmMIME 类型- CSP 需允许
wasm-eval - 发布产物自带
.br/.gz预压缩文件
Web 版已完成构建与运行验证,在线版部署于 online.seatflow.work(OSS 版本化目录 + Cloudflare Worker 代理,见
deploy/README.md与 ADR-006),不随桌面安装包发布。
构建验证清单
每次提交前执行:
# 1. 编译验证
dotnet build
# 2. 全部测试
dotnet test
# 3. 脚本测试
python3 -m pytest scripts/tests/ -v
# 4. i18n 一致性(可选)
python3 scripts/i18n.py check
# 5. 浏览器构建(涉及浏览器端改动时)
dotnet build src/SeatFlow.Browser -c Release
故障排查
构建失败:Designer.cs 不存在
运行 python3 scripts/i18n.py sync 生成 Resources.Designer.cs。
构建失败:Avalonia 编译绑定错误
确保所有 {x:Static} 引用正确,且命名空间声明正确:
xmlns:lang="using:SeatFlow.Presentation.Avalonia.Lang"
发布失败:AOT 不支持某些 API
Native AOT 不支持以下功能:
- 运行时代码生成
- 反射(部分支持)
System.Reflection.Emit
如果在 AOT 构建中遇到错误,检查代码是否使用了不兼容的 API。
构建失败:缺少 wasm-tools 工作负载
浏览器目标(net10.0-browser)构建需要 WASM 工作负载:
dotnet workload install wasm-tools