The release job can now sign through SignPath instead of the certificate in the repository secrets. It only does so when the WINDOWS_SIGNING_PROVIDER repository variable is set to signpath, which nothing sets yet, so every release keeps taking the existing certificate path until a maintainer switches it. On the SignPath path, scripts/ci/signpath-files.ps1 stages only the project's own binaries (the seven Server executables and both TSF DLLs) as the signing request's artifact, because SignPath Foundation does not allow signing the third-party DLLs vcpkg builds for the Server. The signed files are checked for a valid Authenticode signature before they replace the originals, and staging fails if a MetasequoiaIme* binary is missing from the list. The installer goes through a second request after it is compiled. installer/signpath/ holds the reviewed copies of the two artifact configurations to paste into SignPath. detect-release-signing.ps1 fails the release when the provider is signpath but a SignPath setting is missing, or when the provider value is unknown, instead of falling back to the certificate or to an unsigned build.
Metasequoia IME Installer
本目录的脚本从 Windows 合仓目录收集产物、签名、用 Inno Setup 打成安装包。它服务两条流程:
- 正式发布:由
MSIME-Windows的 release workflow 驱动,用真实证书签名包内全部 EXE/DLL 和最终安装包;产物是挂在 Release 上的MetasequoiaIME_Setup_v<版本>.exe,并携带与 Release 二进制匹配的 PDB 符号文件。见下面「CI 契约」 - 本地测试:手工跑,用本机自签名证书,用来在自己机器上验证安装流程。本文其余部分讲的是这条
版本号不是固定的,由 Prepare-PackageFiles.ps1 的 -TargetVersion 决定,默认 0.0.1;正式发布时 CI 传入真实版本。
本仓库 不包含 正式代码签名证书、私钥、指纹,也不包含编译好的 EXE/DLL、词库或安装包。
签名脚本会在你自己的机器上生成并复用一张自签名测试证书。
CI 契约
根 .github/workflows/release.yml 不加修改地调用本目录的 Prepare-PackageFiles.ps1 和 Compile-Installer.ps1。源目录名是 Prepare-PackageFiles.ps1 的参数,不是写死在脚本里的字面量:默认值为 windows/、server/、ui-html/ 和仓根授权文件;旧目录或自定义布局通过参数覆盖。回归检查同时执行默认路径和显式参数的打包。辅助码默认读取 engine/helpcode/,词库缓存位于 MetasequoiaImeDict/out/。
合仓后 release workflow 传的是本仓的目录名,辅助码随固定 Engine gitlink 检出,词库按产品锁下载到仓根缓存:
pwsh -File ./Prepare-PackageFiles.ps1 -TargetVersion 1.2.3 -RepoRoot .. `
-TsfDirectory windows -ServerDirectory server `
-UiHtmlDirectory ui-html -NoticesDirectory .
由此产生几条约束:
- 改动
Prepare-PackageFiles.ps1里那些Assert-PathExists的源路径,会直接弄坏发布流水线。 它断言的源路径由 workflow 逐一准备,改了要同步改.github/workflows/release.yml。目录名本身可以由调用方指定,但每个目录内部的相对路径(如build-release\bin\Release)仍是硬契约 THIRD_PARTY_NOTICES.txt从-NoticesDirectory指向的目录取,随安装包装到程序目录。 合仓后这份声明覆盖整个产品、放在仓根,所以它和-TsfDirectory分成了两个参数。词库主体含 rime-ice(GPL-3.0)内容,其许可要求保留署名,所以这份文件缺失会让打包直接失败,而不是静默跳过Sign-PackageBinaries-Local.ps1和Sign-Installer-Local.ps1只用于本地验证,CI 不会调用它们。 它们创建本机自签名证书并尝试写入受信任存储,正式发布走 workflow 里用仓库 secret 中真实证书的签名步骤- 词库不再从相邻的
MetasequoiaImeDict工作目录取,CI 从产品锁指定的dict-*release 下载并校验 SHA256,再放到脚本期望的位置 - 语言模型
sc.lm不在那个 release 里。它由scripts/build-language-model.ps1在打包前从language-model/lock.json钉住的 libime 语料转换出来,下载摘要和产物摘要都要对上。release workflow 里对应的是「Build the word-lattice language model」这一步,缺了它Prepare-PackageFiles.ps1会直接失败 windows-2025runner 自带 Inno Setup 6.7.1,Compile-Installer.ps1能自己找到ISCC.exe。但它不带ChineseSimplified.isl,msime_setup.iss的[Languages]段依赖那个文件,所以 workflow 会在编译前按固定 revision 和校验和把它装进去
数据目录
程序本体固定装在 Program Files\metasequoiaime(Server 带 uiAccess=true,只有装在受信任目录里这个标志才生效),用户数据目录可以在安装向导里改:词库、config.toml、用户词库、皮肤和前端资源都在那里,整包几百 MB,装 C 盘吃紧的用户可以放到别的盘。
- 默认值
%LOCALAPPDATA%\metasequoiaime;升级安装时默认沿用上次的位置 - 选择写进
HKLM\Software\Metasequoia\MetasequoiaIME的DataDir。这是运行期唯一的权威来源:Server(server/src/utils/ime_paths.cpp)、TSF DLL(windows/src/Utils/FanyUtils.cpp)和引擎(engine/core/data_path.h)各自按METASEQUOIA_IME_DATA_DIR环境变量 → 该注册表值 →%LOCALAPPDATA%\metasequoiaime的顺序解析,三处必须保持一致。32 位 TSF DLL 用KEY_WOW64_64KEY读,所以这个值必须写在 64 位视图里 - 升级时改了位置,安装器会把用户词库(
msime_user.db及其 WAL/SHM)、config.toml、config.base.toml和skins\搬到新目录,再删掉旧目录;词库和前端资源由本次安装重新写入,不搬 - 数据目录必须整个归输入法(#537)。用户选的目录里已经有别的文件、或者选了驱动器根目录时,安装器改用其中的
metasequoiaime子目录,并在向导里把实际位置告诉用户;连那个子目录也不空就拒绝。输入法用过的目录(有标记,或历史默认位置)原样沿用 - 安装器在数据目录里放一个
.metasequoiaime-data标记文件。只有标记里带exclusive: adopted empty by the installer这一行(安装时目录是空的),或目录就是历史默认位置,覆盖安装才整目录清理、卸载才整目录删除 - 旧版安装器不管目录里原来有什么都写标记,所以只有旧标记(没有那一行)的目录不能再信:覆盖安装、换目录和卸载都只按
IsShippedAppDataItem/IsRuntimeAppDataItem/IsPreservedAppDataItem名单删输入法自己的条目,html、logs、skins这种通用目录名只删输入法写进去的部分,最后目录空了才删目录。包里新增顶层文件要同步IsShippedAppDataItem,tests/package-files.ps1会拿真实打出的app_data核对;Server 在数据目录根下新写文件要同步IsRuntimeAppDataItem - 仓库
skins\下的外部皮肤(除default\和素材授权为UNVERIFIED*的)随包装到skins\<id>,完整包和轻量包都带,卸载不删。Prepare-PackageFiles.ps1同时写出bundled_skins.manifest(每行id|相对路径|sha256),安装器在PrepareToInstall里拿它核对用户机器上的同名目录:文件集合和内容完全一致就直接覆盖,否则先改名成<id>.bak(已有就<id>.2.bak……,不覆盖旧备份)再装。Server 扫描皮肤时跳过*.bak目录。tests/bundled-skins.ps1把这段 Pascal 编进探针安装器真跑一遍 - 静默安装用
/DATADIR="D:\MetasequoiaIME"指定;该值在PrepareToInstall里和向导页走同一套换子目录和校验
运行时依赖检查
安装包不携带 WebView2 Runtime 和 VC 运行库——前者有自己的 Evergreen 更新通道,后者是系统级共享组件。但缺了任何一个,输入法装完就是坏的,而故障出现在安装结束之后(Server 起不来,或者候选窗一片空白),用户看不到任何解释。所以 msime_setup.iss 的 InitializeSetup 在安装开始前先查一遍,缺哪个就当场说清楚,并给出下载地址。
- Microsoft Edge WebView2 Runtime:读 EdgeUpdate 客户端键
{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}的pv。机器级安装由 32 位的 EdgeUpdate 写入,所以主查HKLM32,再看HKLM64和HKCU(用户级安装)。pv缺失或残留成0.0.0.0都算没装 - Visual C++ 2015–2022 可再发行组件包(x64):读
Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64的Installed/Major/Minor,HKLM32和HKLM64两个视图都查。要求 14.20 以上(VC++ 2019 起):server_exe\里每个 exe 都导入了vcruntime140_1.dll,它从那一版才开始随 redist 分发,只装了 2015 / 2017 版的机器同样有Installed=1,却照样起不来。32 位 TSF DLL 用静态 CRT(windows/CMakeLists.txt的CMAKE_MSVC_RUNTIME_LIBRARY),所以不检查 x86 redist - 不要改成查
System32里的 DLL。 Setup.exe 是 32 位进程,Pascal Script 的FileExists会被 WOW64 重定向到SysWOW64,查到的是 x86 运行库;只装了 x64 redist 的机器会被误判成缺少 x64 运行库。绕开重定向的办法不可移植:EnableFsRedirection在 Inno Setup 7 里已被移除,而它的替代品在 CI 用的 6.x 里还不存在 - 缺组件时弹一次对话框:选「是」打开下载页并结束安装,选「否」继续装。不阻断安装——用户可能马上就要去补组件,也可能只是在准备环境。静默安装(CI、批量部署)用
SuppressibleMsgBox默认「否」直接继续,缺什么已经写进安装日志(/LOG=)
依赖
- Windows 10/11,PowerShell 7(
pwsh) - Inno Setup 6.6 或更高版本
- Windows SDK(提供
signtool.exe) - 先初始化本仓 submodule,并完成
windows/、server/的 Release 编译以及ui-html/设置页构建。Release 构建必须生成同目录 PDB;打包脚本会拒绝缺少匹配符号的产物(符号是否随包安装是另一回事,由-IncludeSymbols决定)。 - 在仓库根目录运行
python scripts/product_lock.py fetch-dictionaries --staging-root .,下载并验证产品锁中的词库。 - 词格整句打分用的
sc.lm由scripts\build-language-model.ps1从language-model\lock.json钉住的上游语料转换而来。Invoke-LocalTest.ps1打完整包前会自动调它,一般不用手工执行;首次运行要下 ~75 MB 并转换一次,之后摘要对得上就直接跳过。 - 本地打完整包前,可以直接运行
scripts\prepare-package-inputs.ps1,一次准备好神经整句模型、sc.lm和词库。它按各自的锁校验 SHA256,已就绪的直接跳过;词库按 release URL 直接下载,不需要登录gh。 engine/helpcode/helpcodes/是本仓的目录,普通检出即有,无需旧 HelpCode 仓库,也无需初始化 submodule。
本地打包路径
从 installer/ 运行上面的带参数打包命令即可。-RepoRoot 指向 MSIME-Windows 根目录;目录不同时通过参数指定,无需编辑脚本中的复制路径。
一键测试
先准备上述构建依赖与锁定词库。然后在管理员 PowerShell 中(把本机测试证书写入受信任存储需要提升权限):
cd path\to\MSIME-Windows\installer
pwsh -File .\test.ps1
test.ps1 会按顺序做这些事:
- 编译本仓 TSF、Server,并构建设置页;缺少组件入口则失败
Prepare-PackageFiles.ps1— 收集server_exe\、tsf_dll\、app_data\,并把msime_setup.iss的版本写成0.0.1Sign-PackageBinaries-Local.ps1— 本机自签名包内 EXE/DLLCompile-Installer.ps1— 编译出Output\MetasequoiaIME_Setup_v0.0.1.exeSign-Installer-Local.ps1— 本机自签名安装包Install.ps1— 启动安装程序
也可以按上面的顺序逐步运行。
SimplySign 真签名打包
需要在本机用 Certum SimplySign 中的真实 Code Signing 证书制作可分发安装包时,先启动 SimplySign Desktop、用手机 OTP 连接虚拟卡,然后执行:
cd path\to\MSIME-Windows\installer
pwsh -File .\package-simplysign.ps1 1.2.3
命令行版本会同时写入本轮 TSF DLL 的版本资源和安装包元数据(脚本结束后恢复源码模板)。脚本会
增量编译 TSF、Server 和设置页,制作完整包,用 http://time.certum.pl 的 RFC 3161 时间戳签名
server_exe\ 和 tsf_dll\ 下的全部 EXE/DLL,编译 Inno Setup 安装包,再签名并校验最终
安装包。产物位于 Output\MetasequoiaIME_Setup_v1.2.3.exe;脚本只产出文件,不会自动启动
安装程序。
为什么整包都要签。 MetasequoiaImeServer.exe 是唯一一个技术上必须签的——uiAccess=true
没有可信 Authenticode 签名就会被 Windows 忽略。但其余辅助进程(设置页、表情面板、手写面板、
键盘面板、看门狗、词库回放)和随包分发的 DLL 不签名会被 Microsoft Defender 按信誉拦下,
用户看到的现象是输入法装完了、某个面板却打不开。所有文件在一次 signtool sign 调用里签完:
SimplySign 可能对每次调用弹一次 PIN,逐个文件调用会变成十几次手机确认;签名配额按文件计,
合并调用不会多花配额。
默认不把 PDB 放入安装包;需要与正式 CI 一致的符号包时加 -IncludeSymbols。如果 SimplySign
同时暴露了多张有效的 Certum Code Signing 证书,脚本会列出它们并要求用
-CertificateThumbprint <指纹> 明确选择。可用 -Reconfigure 强制重新配置 CMake,也可用
-IsccPath <路径> 指定 Inno Setup 编译器。带 PDB 的安装包会使用 _with_pdb 文件名后缀,
例如 Output\MetasequoiaIME_Setup_v1.2.3_with_pdb.exe。
也可以直接使用固定包含 PDB 的一键入口,不需要再写 -IncludeSymbols:
pwsh -File .\package-simplysign-symbols.ps1 1.2.3
它是 package-simplysign.ps1 的薄包装,其他参数(例如 -Reconfigure、
-CertificateThumbprint 和 -IsccPath)保持一致,输出文件名固定带 _with_pdb 后缀。
Hotfix 轻量包
只修 TSF / Server / HTML 的 hotfix 可以发轻量包,签名流程与完整包相同(包内 EXE/DLL 和最终安装包都签):
pwsh -File .\package-simplysign-light.ps1 1.2.4
它等价于 package-simplysign.ps1 1.2.4 -Light,产物是 Output\MetasequoiaIME_Setup_v1.2.4_light.exe
(加 -IncludeSymbols 时是 _light_with_pdb)。轻量包和 test-light.ps1 用的是同一个
/DLightPackage=1 编译开关,因此有同样的限制:
- 只能覆盖安装在已有的完整安装之上。 包里没有词库、辅助码、拼音表、神经模型和出厂配置模板, 安装程序也不检查本机是否装过完整包;新用户装它会得到一个没有词库的输入法。对外发布时要写清楚, 下载页和自动更新清单仍应指向完整包。
- hotfix 不能新增配置项。 升级时 Server 以用户数据目录里的
config.default.toml为骨架合并配置, 轻量包不替换这份模板,新键会被丢弃。需要新配置项、新词库或新模型时发完整包。 - 轻量包不显示联网功能页,也不回放用户词库。
三个入口的区别
三个入口都是 Invoke-LocalTest.ps1 的薄包装,只是开关不同:
| 脚本 | 词库 | PDB |
|---|---|---|
test.ps1 |
含 | 不含 |
test-light.ps1 |
不含 | 不含 |
test-symbols.ps1(加 -Full 则含词库) |
默认不含 | 含 |
PDB 默认不进本地安装包。 它们有 ~140 MB,而真正的 EXE/DLL 只有 ~20 MB,Inno Setup 还要对它们做固实 LZMA2 压缩,是本地打包耗时的大头。只有需要在装好的机器上直接做崩溃分析时才用 test-symbols.ps1。正式发布仍然带符号:release.yml 显式传 -IncludeSymbols。
增量编译
Invoke-LocalTest.ps1 调用的 lcompile-release*.ps1 是增量的:
build-release/build{32,64}-release已有 CMake 缓存时不再重跑 configure。Visual Studio 生成器把 ZERO_CHECK 接进了每个目标,CMakeLists.txt变化时构建会自己重新生成。改了 preset 或换了工具链,用-Reconfigure强制重跑- 只构建打包用的目标(
MetasequoiaImeServer/MetasequoiaImeTsf),不构建那些打完包又被删掉的测试可执行文件。要连测试一起构建(比如为了跑 ctest)传-Target ALL_BUILD cmake --build带--parallel
没有改动时整轮编译约 15 秒;如果比这慢很多,慢的多半是打包而不是编译。
本机测试证书
第一次运行签名脚本时,会在当前用户证书存储里创建:
CN=Metasequoia IME Local Test Code Signing
之后复用同一张证书。产物只适合本机或已导入该证书公钥的测试机,不要当作正式分发包。
想让另一台测试机信任这张证书,把公钥导出成 .cer 后在那台机器上执行:
certutil -addstore Root MetasequoiaImeLocalTest.cer
certutil -addstore TrustedPublisher MetasequoiaImeLocalTest.cer
不要把 .cer / .pfx 提交进本仓库。