free-code-dotnet/docs/测试与构建/测试与构建-构建与部署.md
应文浩wenhao.ying@xiaobao100.com e25ac591a7 init easy-code
2026-04-06 07:24:24 +08:00

7.7 KiB
Raw Permalink Blame History

构建与部署

所属项目: free-code .NET 10 重写 文档类型: 构建设计 来源章节: DESIGN-NET10-PART3.md § 23 上级文档: 测试与构建总览


概述

构建目标是跨 5 个平台的 AOT 原生二进制输出单文件、无运行时依赖的可执行文件。MSBuild 通过 Directory.Build.props 统一管理全局属性,build.sh 封装多平台批量发布,install.sh 提供一键下载安装体验。

原始 TypeScript 项目使用 Bun 打包为单一 JavaScript bundle。.NET 重写的 AOT 路径在启动时间和内存占用上接近原生,同时消除了 Bun 运行时依赖。


23.1 MSBuild 配置

Directory.Build.props全局属性

所有子项目自动继承这些属性,无需在每个 .csproj 中重复声明。

<!-- Directory.Build.props -->
<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>13.0</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <AotPublish>true</AotPublish>
    <PublishSingleFile>true</PublishSingleFile>
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>partial</TrimMode>
    <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
    <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
    <EnableAotAnalyzer>true</EnableAotAnalyzer>
    <Version>1.0.0</Version>
    <AssemblyName>free-code</AssemblyName>
    <RootNamespace>FreeCode</RootNamespace>
  </PropertyGroup>
</Project>

关键属性说明:

属性 作用
AotPublish true 启用 AOT 原生编译
PublishSingleFile true 输出单一可执行文件
PublishTrimmed true 裁剪未使用的程序集
TrimMode partial 部分裁剪,保留反射入口点
JsonSerializerIsReflectionEnabledByDefault false 强制使用 Source Generator 序列化
EnableAotAnalyzer true 编译期检测 AOT 不兼容代码
LangVersion 13.0 C# 13 特性primary constructors、集合表达式等

FreeCode.csproj主项目

<!-- FreeCode.csproj (主项目) -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\FreeCode.Core\FreeCode.Core.csproj" />
    <ProjectReference Include="..\FreeCode.Engine\FreeCode.Engine.csproj" />
    <ProjectReference Include="..\FreeCode.Tools\FreeCode.Tools.csproj" />
    <ProjectReference Include="..\FreeCode.Commands\FreeCode.Commands.csproj" />
    <ProjectReference Include="..\FreeCode.ApiProviders\FreeCode.ApiProviders.csproj" />
    <ProjectReference Include="..\FreeCode.Mcp\FreeCode.Mcp.csproj" />
    <ProjectReference Include="..\FreeCode.Lsp\FreeCode.Lsp.csproj" />
    <ProjectReference Include="..\FreeCode.Bridge\FreeCode.Bridge.csproj" />
    <ProjectReference Include="..\FreeCode.Services\FreeCode.Services.csproj" />
    <ProjectReference Include="..\FreeCode.Tasks\FreeCode.Tasks.csproj" />
    <ProjectReference Include="..\FreeCode.Skills\FreeCode.Skills.csproj" />
    <ProjectReference Include="..\FreeCode.Plugins\FreeCode.Plugins.csproj" />
    <ProjectReference Include="..\FreeCode.Features\FreeCode.Features.csproj" />
    <ProjectReference Include="..\FreeCode.State\FreeCode.State.csproj" />
    <ProjectReference Include="..\FreeCode.TerminalUI\FreeCode.TerminalUI.csproj" />
  </ItemGroup>
</Project>

主项目引用全部 15 个子项目,自身仅包含 Program.cs 入口,所有业务逻辑分布在各子项目库中。


23.2 构建脚本

build.sh多平台批量构建

#!/bin/bash
# build.sh — 多平台构建
set -euo pipefail

VERSION="${1:-$(git describe --tags --always)}"
OUTPUT_DIR="./dist"

# 构建目标平台
PLATFORMS=(
    "osx-arm64"
    "osx-x64"
    "linux-x64"
    "linux-arm64"
    "win-x64"
)

for PLATFORM in "${PLATFORMS[@]}"; do
    echo "Building for $PLATFORM..."
    RID="${PLATFORM}"

    dotnet publish src/FreeCode/FreeCode.csproj \
        -c Release \
        -r "$RID" \
        -o "$OUTPUT_DIR/$PLATFORM" \
        /p:VersionPrefix="$VERSION" \
        /p:AotPublish=true \
        /p:PublishSingleFile=true \
        /p:PublishTrimmed=true

    # 重命名二进制
    if [[ "$PLATFORM" == win-* ]]; then
        mv "$OUTPUT_DIR/$PLATFORM/free-code.exe" \
           "$OUTPUT_DIR/$PLATFORM/free-code-$VERSION-$PLATFORM.exe"
    else
        chmod +x "$OUTPUT_DIR/$PLATFORM/free-code"
        mv "$OUTPUT_DIR/$PLATFORM/free-code" \
           "$OUTPUT_DIR/$PLATFORM/free-code-$VERSION-$PLATFORM"
    fi

    echo "✓ $PLATFORM done"
done

echo "All builds complete in $OUTPUT_DIR/"

脚本逻辑说明:

  1. 版本号优先读取命令行参数fallback 到 git describe --tags --always
  2. 遍历 5 个 RID每个平台独立调用 dotnet publish
  3. 输出产物按 free-code-{VERSION}-{PLATFORM} 格式命名
  4. Windows 平台保留 .exe 后缀,其余平台添加可执行权限

23.3 安装脚本

install.sh一键安装

#!/bin/bash
# install.sh — 一键安装
set -euo pipefail

# 检测平台
OS="$(uname -s)"
ARCH="$(uname -m)"

case "$OS-$ARCH" in
    Darwin-arm64)   PLATFORM="osx-arm64"   ;;
    Darwin-x86_64)  PLATFORM="osx-x64"     ;;
    Linux-x86_64)   PLATFORM="linux-x64"   ;;
    Linux-aarch64)  PLATFORM="linux-arm64" ;;
    *) echo "Unsupported platform: $OS-$ARCH"; exit 1 ;;
esac

INSTALL_DIR="$HOME/.free-code/bin"
mkdir -p "$INSTALL_DIR"

# 下载
BINARY_URL="https://github.com/paoloanzn/free-code/releases/latest/download/free-code-latest-$PLATFORM"
echo "Downloading free-code for $PLATFORM..."
curl -fsSL "$BINARY_URL" -o "$INSTALL_DIR/free-code"
chmod +x "$INSTALL_DIR/free-code"

# 添加到 PATH
if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then
    SHELL_RC="$HOME/.zshrc"
    [ -f "$HOME/.bashrc" ] && SHELL_RC="$HOME/.bashrc"
    echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$SHELL_RC"
    echo "Added $INSTALL_DIR to PATH in $SHELL_RC"
fi

echo "✓ free-code installed to $INSTALL_DIR/free-code"
echo "  Run 'free-code' to start"

安装流程:

  1. 通过 uname -suname -m 检测当前平台
  2. 从 GitHub Releases 下载对应 RID 的预编译二进制
  3. 安装到 ~/.free-code/bin/free-code
  4. 自动检测 shell 配置文件(优先 .zshrcfallback .bashrc)并追加 PATH

23.4 目标平台矩阵

RID 操作系统 架构 说明
osx-arm64 macOS Apple Silicon M1/M2/M3 Mac
osx-x64 macOS Intel x64 Intel Mac
linux-x64 Linux x86-64 主流 Linux 服务器
linux-arm64 Linux AArch64 Raspberry Pi、AWS Graviton 等
win-x64 Windows x64 Windows 10/11

23.5 发布参数说明

dotnet publish 参数 含义
-c Release Release 配置,开启优化
-r {RID} 目标运行时,指定后产物为自包含
/p:AotPublish=true AOT 原生编译,消除 JIT 开销
/p:PublishSingleFile=true 将所有资源打包为单一可执行文件
/p:PublishTrimmed=true 裁剪未引用的程序集,减小体积
/p:VersionPrefix={VERSION} 注入版本号到程序集元数据

AOT 编译将 IL 代码提前编译为目标平台原生机器码,启动时间从 JIT 模式的数百毫秒降至个位数毫秒,与原始 Bun 构建产物的冷启动时间相当。


参考资料