Commit Graph
57 Commits
Author SHA1 Message Date
Evan Hu 8fa00f5576 chore(ci): 把工作流同步到 main
release.yml 是从 main 运行的,branch-guard 也按 PR 的 base 分支加载工作流定义。两者只合进 develop 时都不生效:main 上的 release-please 仍会把版本 PR 开到默认分支 develop,而提到 main 的 PR 根本不会触发拦截。
2026-09-06 22:51:28 +09:00
Evan 39c94b092d Merge pull request #196 from metasequoiaime/fix/ci-sanitizer-format
fix(ci): separate automatic and manual release locks
2026-09-06 18:39:52 +09:00
Evan Hu 276625fe31 fix(ci): separate automatic and manual release locks 2026-09-06 18:30:33 +09:00
Evan Hu 88720a80df fix(ci): 修复引擎 bump 自动合并的守卫,并补回 Issue 模板的脱敏警告
engine-bump-triage.yml 里 `&& steps.lock.outputs.locked == 'yes'` 被写进了 HEAD 这个环境变量的值,而不是步骤的 if:。它在那个位置根本不是条件:不论契约锁校验通过与否,自动合并都会执行;步骤随后发出的评论还会把 commit 后面跟上字面量 `&& steps.lock.outputs.locked == 'yes'` 一起打印出来。因为没有任何东西失败,这个问题没有暴露过。

(实际风险有限:Product inputs 是必需检查,未审的 bump 不会真的被放行。但这条守卫本身写了等于没写。)

补 tests/test_engine_bump_triage.py 三个断言:自动合并的条件必须同时包含判定和锁;任何步骤的 env 值里都不许再出现条件片段;评论用的 BASE/HEAD 必须只是 range 输出本身。第二条是通用的,同类错误以后写在别的步骤上也会被抓到。

同时补回本仓 bug_report.yml 相对组织模板丢掉的两项:日志字段的「不要粘贴真实输入内容、用户词库、API Key 或任何凭据」加粗警告,以及「这不是安全漏洞」的必选确认项。全组织的 Issue 流量几乎都在这个仓库,恰恰是最需要这两条的地方——输入法的日志天然贴近用户真实输入。
2026-09-06 18:15:51 +09:00
Evan Hu 1c25466511 test(ci): 给解析不可信输入的宿主无关代码加 ASan / UBSan
MSIME-Linux 的 ci.yml 注释里已经写下了这个结论:「前端层才是解析不可信输入的那层……Windows 前端上次审计修的就是一批 race 和空指针,现有的 Debug 构建一个都抓不到。」Linux 仓照这个结论补了 sanitizer job,Windows 仓一条都没有。

给整个 Server 上 sanitizer 意味着要在 Windows runner 上做一份带 sanitizer 的 Boost、curl、sqlite 和 WebView2,代价很高。这里换一个能立刻落地的做法:把宿主无关的解析与策略代码单独建成一个不依赖任何包管理器的小目标,在 Linux runner 上用 ASan + UBSan 跑,几秒钟完成,每个 PR 都跑。

覆盖面确实小于全量 sanitize 构建,但它是真的在跑。目标从皮肤 CSS 策略起步,后续凡是能与 Windows 头文件解耦的、读取用户没写过的数据的逻辑(词库文件、IPC 负载、配置解析)都可以加进这个列表。

该目标同时以 -Wall -Wextra -Wpedantic -Werror 编译,与 Linux、Apple 两仓的标准一致。本机已用 ASan+UBSan 构建并运行通过。
2026-09-06 18:11:37 +09:00
Evan Hu 1ec7bdd690 fix(settings): 检查更新时消费 update.json 的校验值与签名状态
update.json 一直发布四个关于安装包的字段(installerName / installerUrl / installerSha256 / signed),而设置页的 UpdateManifest 类型只声明了 version 和 releaseUrl,另外两个字段发布出来却没有任何人消费。结果是:在安装包未签名的当下,SHA256 是用户唯一的完整性防线,服务端早已算好,却要用户自己去官网翻。

现在发现新版本的对话框会显示:signed 为 false 时给出明确警告(SmartScreen 会拦截、uiAccess 失效导致候选窗无法浮在提权程序之上),以及 Get-FileHash 命令和应得到的摘要,可选中复制。

「不做后台自动检查」是 PRIVACY.md 明示的隐私设计,没有改动——改的只是用户按下检查更新之后看到什么。

校验与展示逻辑抽到 update-manifest.ts:validateManifest 收紧了 releaseUrl 的判断(此前 startsWith 前缀匹配会放过 …/releases.attacker.example 这类同前缀域名),并对三个可选字段做格式校验——安装包名会进入一段让用户粘贴的命令,限制成发布流水线实际产出的形状;格式不对的可选字段按缺失处理,不让整次更新检查失败,老版本 manifest 仍然可用。

接入 vitest,11 个用例覆盖版本解析与排序(0.10.0 > 0.9.0、0.3 == 0.3.0)、manifest 校验的拒绝路径、以及签名/未签名/未声明三种状态下的展示。CI 在 build 之前加一步 pnpm test。
2026-09-06 18:08:34 +09:00
Evan Hu a4d583bf2d ci: sign uiAccess server and installer 2026-09-06 17:04:08 +09:00
Evan Hu ef62de3f7e ci: sign only the release installer 2026-09-06 17:01:04 +09:00
Evan ef55ff67bb Merge branch 'main' into ci/self-hosted-runner-signing 2026-09-06 16:55:18 +09:00
Evan Hu e574a2381e ci: use self-hosted Windows signing runner 2026-09-06 16:54:49 +09:00
Evan 598b641dd1 docs: 校正签名配额的前提,README 加 badge 与 star 趋势
四处文档把「签名证书次数有限」当作设计理由,其中 docs/product-release.md 据此建议把若干修复攒成
一个版本以省额度。证书实际允许每月 3000 次签名,这个项目的发布频率远低于该量级,前提不成立。
改为如实说明,并把攒版本的真实理由(发布节奏与 release notes 的碎片化)写清楚——那个考量仍然
成立,只是和配额无关。
2026-09-06 12:43:16 +09:00
Evan 5bd82e9c5f ci(windows): 在 CI 里执行 clang-format
移植 MSIME-Linux 的 scripts/format.sh,接进已经是 required 的 Workflow validation 作业。
四个组件各自的 .clang-format 一直在仓库里,但此前没有任何一处强制执行。

.git-blame-ignore-revs 指向上一个重排提交,git blame 会跳过它。
2026-09-06 12:17:29 +09:00
Evan f70f64acaa fix(ci,ui): 修掉 6 条 CodeQL high 告警,补上发布目标的祖先校验
- validate-draft-release.sh 增加「target_sha 必须在 main 历史上」的校验。此前有 write 权限的人可以
  建一个指向任意 commit 的 draft release 再 dispatch,那份代码就会被编译并签进正式安装包;唯一的
  祖先检查跑在 checkout 之后的不可信工作树里,而负责编译的 TSF job 根本没有这道检查。校验放在
  prepare(默认分支上下文)里,所有构建 job 都 needs 它。
- 字体名引用只转义了 `"` 没转义反斜线,以 `\` 结尾的字体名能吃掉闭合引号让后续内容按 CSS 解析。
  两处(appearance.ts、shared.ts)都改为先转义转义符。
- voice_input_service.cpp: float 平方先提升到 double
- engine-bump-triage.yml 顶层权限从 write 收到 read,写权限下放到 job;runner 与同仓其他 workflow
  统一为 ubuntu-24.04

新增 CONTRIBUTING.md,把散在各 AGENTS.md / 组件 README 里的环境要求、构建顺序、打包步骤和提交前
检查串成外部贡献者能照着走完的一条路径,并从 README 首屏链出。
2026-09-06 12:09:50 +09:00
Evan 4fc16952b3 wip(windows): 补齐隐私与许可交付,修好外部人的构建入口
- 新增 PRIVACY.md:逐项写明每个联网端点、发送内容、默认开关与关闭方式,明确云候选默认开启、
  token 在 config.toml 明文存储、无任何遥测
- 设置页「用户协议」「隐私政策」两个无 handler 的死按钮接上;前者改名为「开源许可协议」指向
  LICENSE,开源产品的用户条款就是许可证本身,不再承诺一份不存在的 EULA
- 安装包随包装入 GPL 正文并在安装向导显示(GPLv3 §4/§6),补上打包测试断言
- release.yml 增加 build provenance attestation,并在发布后向 MSIME-Web 发 repository_dispatch,
  让 msime.app/update.json 不再等两小时一次的 cron
- 所有 CMakePresets 的 vcpkg 路径改用 $env{VCPKG_ROOT},server 的 Boost 回退不再指向个人目录
- ui/README.md 指向本机绝对路径的 markdown 死链改成相对链接
- 新增 check-developer-paths.sh 并接入 required 的 quality 检查,防止路径回潮
- docs/installation.md 不再写死版本号
2026-09-06 12:05:06 +09:00
Evan 5a4484feea fix(ci): stop the engine bump triage from executing engine file names (#174)
The Hold-for-review step built its comment with printf and spliced ${{ steps.classify.outputs.reason }} into the command text inside single quotes. That value is the list of file names the engine changed. GitHub substitutes ${{ }} textually before bash parses the line, so a path containing a single quote closes the quoting and the rest runs as a command — on a runner holding a contents:write and pull-requests:write token for this repository. Merge rights in MSIME-Engine became command execution here. Non-adversarially the same splice makes the step die under set -euo pipefail on any engine path containing an apostrophe, so a routine bump gets a red triage job and no review comment.

Every value now reaches the shell through env: and the comment body goes through --body-file. The $GITHUB_OUTPUT heredoc that carries those same file names gets a random delimiter, since a path equal to the literal EOF would otherwise close the block early.

Separately, the classifier could auto-merge a range it had not inspected. With an empty file list, echo emits one blank line, grep -v prints it, and command substitution strips it, so the emptiness test read "nothing suspicious changed" and enabled auto-merge. The compare API returns an empty file list whenever head is already an ancestor of base — an engine force-push, or the pin sitting ahead — which is exactly the case where a bump silently reverts engine source this frontend depends on. It now fails closed on any compare status other than a plain fast-forward, and on an empty file list.

Found while auditing MSIME-Apple, which carried a byte-identical copy of this workflow. Fixed there in metasequoiaime/MSIME-Apple#269.
2026-09-06 11:14:01 +09:00
Evan ee4334dddd fix(ci): attach automatic release checks to the pull request (#175)
* ci(codeql): 避免版本元数据 PR 产生待人工批准的扫描

* fix(ci): attach release validation to the pull request
2026-09-06 11:10:05 +09:00
Evan Hu cc5af5356d fix(ci): stop the dependency review from skipping the release pull request
Dependency review was guarded on the pull_request event, which made it report "skipped" on every other trigger. A required status check that reports "skipped" blocks a merge rather than passing it, and #166 made this one of the nine contexts main requires, so the automatic release could no longer land its own release pull request: land-release-pr.sh watched ci.yml go green and then got "the base branch policy prohibits the merge" (run 34004706430, pull request #173).

The guard cannot simply move to the release path either. ci.yml ignores the release pull request by path, because a pull_request run started by GITHUB_TOKEN is created in action_required and never executes, so the release workflow dispatches ci.yml on the release branch and that dispatch is the only run reporting checks on that commit.

The job now runs on every trigger. A pull_request event supplies both refs on its own; every other trigger compares the default branch against the checked-out commit, which is the same question the pull request asks.
2026-09-06 10:58:00 +09:00
Evan Hu 568fd3742c feat(release): mark automatically built releases in the title and notes
GitHub has three release states and no notion of a channel, so an automatic per-merge build and a release somebody deliberately cut both land in the same Pre-release list that docs/installation.md presents to users as the 内测版 channel. Before every releasable merge produced a release that did not matter; now it does.

Releases published from a push get 「(自动构建)」 in the title and a note saying they were not curated. Releases published from a workflow_dispatch keep the bare tag as their title, which is what release-please already set. docs/installation.md tells readers which is which and why they share one list.

This is deliberately a labelling change and not a second release track. Giving automatic builds their own tag namespace would need a parallel versioning mechanism alongside the one release-please owns, and stacking that on a pipeline that is currently failing and shipping unsigned artifacts is the wrong order.

Refs MSIME-Windows#167
2026-09-06 10:50:56 +09:00
Evan 947b5e9fa2 ci(release): 分离版本分支与历史 tag 的令牌权限 (#171) 2026-09-06 10:45:17 +09:00
Evan ab0d98b719 ci(release): publish every releasable merge to main automatically (#165)
A push to main carrying a fix or a feat now ends with a published installer, with no human merging the release pull request and no manual workflow_dispatch in between.

release-please only cuts a release for a release pull request that was already merged when it ran, so the prepare job now invokes it twice: once to refresh the pull request against the commits this push brought, and once after land-release-pr.sh has merged it, to turn that merge into the draft the rest of the run builds and publishes. Merging before the first invocation would instead release everything except the push that triggered the run.

land-release-pr.sh is check-release-pr.sh plus the merge it deliberately used to skip. It merges with GITHUB_TOKEN, whose pushes do not start workflow runs, so the merge cannot produce a second Release run queued behind this one on the concurrency group; the release is created and built inside the run that merged it. It merges the exact commit CI proved via --match-head-commit, then waits for that merge commit to be reachable from main so the second release-please invocation does not quietly find nothing to release.

resolve-auto-release.sh picks which of the two invocations reported the release to build, preferring the second when both did, and applies the invariants validate-draft-release.sh gives the manual path: the release is tagged at a full commit SHA, and the tag agrees with the version.

workflow_dispatch stays as a repair path for a run that failed after creating its draft.

This spends one of the signing certificate's limited signatures on every releasable merge, which is what the previous manual design existed to avoid. docs/product-release.md records the trade, including why the queueing this can produce is not answered by cancel-in-progress.
2026-09-06 10:25:11 +09:00
Evan 9b55abdc89 ci: 补齐仓库验证与自动安全维护 (#166)
* ci(automation): 补齐持续集成与开源安全检查

* ci(automation): 将发布 PR 的质量门禁纳入手动 CI
2026-09-06 10:22:41 +09:00
Evan Hu 0baaf9a1d0 fix(server): 将本地测试入口接回合仓后的主工程 2026-09-06 09:40:36 +09:00
Evan Hu b2d736cd79 fix(build): finish local tooling migration to the monorepo 2026-09-06 09:02:13 +09:00
Evan Hu 1e7632b564 fix(ci): match native Windows job names to the required checks 2026-09-06 04:28:30 +09:00
Evan Hu c3fcc4a515 refactor(product): consume the consolidated Engine data and shared voice capture
Ports metasequoiaime/MSIME-Windows#153 onto the consolidated repository. That pull request was written against the six-repository layout: it edited product-ci.yml, which no longer exists, and re-pinned Server, UiHtml and Installer, which are directories now.

Three of its pinned commits had not reached this repository at all. The consolidation took each source repository at its main, and Server, UiHtml and Installer each landed two more commits afterwards, so dropping the pins as merely obsolete would have silently dropped the work the pull request exists to consume. The two substantive ones are ported here: Server drops its vendored miniaudio.h and builds against the engine's MetasequoiaIme::VoiceCapture, and the installer collects helpcodes from the engine. UiHtml's was an engine gitlink bump, which one submodule resolves on its own.

The engine is pinned to d0dc0c2b rather than to the b7c799ef that pull request named. b7c799ef predates 403b4a4d, which is what this repository pins today and is the commit that added pagination to the settings dictionary contract, so taking the pull request's pin verbatim would have reverted a contract main already depends on and stripped offset, limit and hasMore back out of the generated web bindings. d0dc0c2b is the engine's main and is the same commit the dictionary release declares as its source, so the code and the data now name one commit.

The helpcodes moved into the engine, so MSIME-HelpCode stops being a product input: the gitlink that pins the contracts pins the tables too. product-lock.json is down to the engine and the dictionary release, and the dictionary itself now ships from MSIME-Engine. Its manifest states which commit built the data and whether that tree was clean, and the lock verifies both, so matching digests can no longer stand in for a source nobody can rebuild from. The retired MSIME-Dict repository stays valid for the one tag that shipped from it and nothing else.

The release job that packages now checks out submodules, because the helpcodes it collects live in one.
2026-09-06 04:13:04 +09:00
dependabot[bot] 9d920d4f02 chore(deps): bump actions/cache/save in the actions group
Bumps the actions group with 1 update: [actions/cache/save](https://github.com/actions/cache).


Updates `actions/cache/save` from 4.3.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/0057852bfaa89a56745cba8c7296529d2fc39830...55cc8345863c7cc4c66a329aec7e433d2d1c52a9)

---
updated-dependencies:
- dependency-name: actions/cache/save
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-05 18:56:27 +00:00
Evan Hu 947aa16fcb fix(ci): persist vcpkg binaries before application builds 2026-09-06 03:39:07 +09:00
Evan Hu 6bb0c1e10e fix(ci): check out the engine submodule in the product inputs job
check-dictionary-contract.py compares scripts/dictionary_product.py against the engine's own copy, so the job needs the submodule's contents and not just its gitlink. The reusable workflow this job replaced checked out submodules; the rewrite dropped that.

The script reported a missing file as "differs from the pinned Engine contract", which describes a drift that had not happened and sends the reader looking for a content change. It now says the file is missing and how to get it.
2026-09-06 02:51:08 +09:00
Evan Hu 32e722c176 ci: build every component from the one checkout
The product lock existed because five repositories had to be pinned to reproduce one product. Four of them are directories now, so a commit of this repository already pins them and there is nothing left for the lock to say about them. What it still records is what the product consumes from outside: the engine submodule, the helpcodes and the dictionary release.

CI gains a job per component instead of a reusable workflow that checked out locked commits and rebuilt them. The release workflow no longer stages five sibling checkouts under historical names and no longer carries a second sparse checkout for its own scripts; Prepare-PackageFiles.ps1 already took the source directory names as parameters, so it is told the new ones, plus where the product-wide notice now lives.

Two checks the removed cross-repository pins used to provide are replaced in kind: the pages are verified against the engine submodule's web contract in CI rather than by locking UiHtml and Engine together, and the engine bump triage that Server carried moves here and now also says so when a bump leaves the lock behind.
2026-09-06 02:35:49 +09:00
Evan e08f434e31 Merge pull request #148 from metasequoiaime/architecture/product-contracts
feat(product): validate and lock the shared-session release combination
2026-09-06 01:54:16 +09:00
Evan Hu 831b828d00 refactor(ci): move the Inno Setup language check into scripts/ci
Every other piece of shell in these workflows lives in scripts/ci and the workflow only wires it up. The language probe went in inline, so it did not: twenty lines of PowerShell building an .iss heredoc inside a YAML block scalar, which is the least reviewable and least runnable place to put it.

Same check, now in check-inno-language.ps1, and runnable by hand on a Windows machine. It falls back to the system temp directory when RUNNER_TEMP is unset so that running it outside CI works.
2026-09-06 01:37:20 +09:00
Evan Hu 7c6dc8b6a0 fix(release): install the Inno Setup language file where ISCC looks
Every release build since the packaging pipeline landed has failed at "Compile the installer", which is why the last eight versions are still unpublished drafts:

    Error on line 63 in msime_setup.iss: Couldn't open include file
    "c:\program files (x86)\inno setup 6\Languages\ChineseSimplified.isl"

install-inno-language.ps1 derived the destination from `Get-Command ISCC.exe`. On the windows-2025 image Inno Setup comes from Chocolatey, so that resolves to the shim at C:\ProgramData\Chocolatey\bin\ISCC.exe and the file was written to C:\ProgramData\Chocolatey\bin\Languages\. ISPP resolves compiler: against the real installation. The step printed "Installed ... (21516 bytes)" and exited 0, and the build failed two steps later pointing at a path nothing had written to, which is why this survived eight releases.

The installation is now located by a file only it has -- ISPPBuiltins.iss or Default.isl, neither of which sits beside a shim -- checking the uninstall registry key, the two Program Files locations and any ISCC.exe on PATH, and failing with the list it tried rather than falling back to a guess.

ci.yml now compiles a throwaway script whose only content is a [Languages] entry pointing at compiler:Languages\ChineseSimplified.isl. Asserting the file exists at a path this script computed would only restate the script's own belief; compiling is what proves ISCC agrees. The URL and digest pin moved into the script so that check does not need a second copy of them.
2026-09-06 01:37:13 +09:00
Evan Hu 56db4efc8f test(product): validate the locked installer full and light package contents 2026-09-06 00:55:22 +09:00
Evan 651ce6196d Merge pull request #146 from metasequoiaime/houko/manual-release
ci(release): 发布改为手动触发,不再由合并 main 自动签名
2026-09-06 00:54:14 +09:00
Evan Hu edebf3f76a Merge remote-tracking branch 'origin/main' into architecture/product-contracts 2026-09-06 00:53:27 +09:00
Evan Hu 1c6c956530 feat(product): lock the shared-session stack and validate dictionary formats 2026-09-06 00:53:26 +09:00
Evan Hu 07d9ce91e5 revert: move back off calendar versioning
Reverts the switch to calendar versioning, before it produced a release here.

release-please has no CalVer strategy, so the switch reset the version series by hand and pinned always-bump-patch. What that analysis missed is what release-please does to the changelog when the series it is walking no longer connects to the tag history: on MSIME-Apple it rewrote the whole file into a single 158-line 2026.9.1 entry that listed every feature since the first pull request as though it had just shipped. MSIME-Linux got a 64-line one. This repository had not released yet, so there is no changelog damage to undo and reverting now costs nothing.

Restores the 0.0.17 baseline across version.txt, .release-please-manifest.json and the version resource, drops always-bump-patch from the release-please config, and removes the monthly rollover workflow. The next release goes back to the previous series.

The three version lines the switch was meant to unify stay unaligned. That problem is real but wants a mechanism that does not fight release-please.
2026-09-06 00:51:32 +09:00
Evan Hu 2a15c6a4ad ci(release): only build and sign on an explicit dispatch
The signing certificate allows a limited number of signatures, so a release costs something finite. It should not be spent by merging a pull request.

Until now a push to main was enough to spend one: release-please opened the release pull request, merge-release-pr.sh merged it, the merge commit came back through this workflow as release_created, and the build jobs signed two binaries and an installer and published the result. Nobody had to ask for any of that.

should_publish is now true only for workflow_dispatch, and the package job -- the only one that signs -- states that condition itself rather than inheriting it from four other jobs whose conditions a later edit could quietly change.

Auto-merging the release pull request goes with it, since that is the mechanism that turned a merge into a release, and leaving it would also mint a draft release and a tag on every merge to main that nobody intends to build. What remains automatic is getting a passing check onto that pull request, which is not optional: GITHUB_TOKEN cannot start a workflow run, so without the dispatch in check-release-pr.sh the release pull request has no checks at all and required_status_checks (#121) leaves it permanently unmergeable.

Releasing is now two deliberate steps. Merge the release pull request, which creates the draft release and its tag and builds nothing. Then run the Release workflow against that tag. Dispatching the same tag twice is already refused by validate-draft-release.sh, because a published release is no longer a draft, so a retry cannot quietly spend a second signature.
2026-09-06 00:48:51 +09:00
Evan Hu 9f87dc2929 chore(release): move to calendar versioning
The three platform frontends of one product carried three unrelated version lines: Windows 0.0.17, Linux 0.6.0 and Apple 0.41.0, all built on the same engine. A user reporting a problem with "0.41" said nothing about which platform they were on, and the main product carried the lowest number of the three.

All three now start from 2026.9.0. Every platform moves up, so no update mechanism sees a downgrade: Sparkle compares components numerically, the Windows update manifest accepts any dot-separated digits, and 2026.9.0 is a valid and higher version for both dpkg and rpm.

release-please has no CalVer strategy — its only options are default, always-bump-patch, always-bump-minor, always-bump-major, service-pack and prerelease — so always-bump-patch is pinned to walk the last component within a month, and .github/workflows/calver-rollover.yml moves the year and the month. Without that workflow the date in the version would freeze and keeping it truthful would become a manual edit somebody has to remember, which is the failure this project has already been bitten by elsewhere.
2026-09-05 23:46:53 +09:00
Evan Hu a81bc47c71 feat(product): require locked commits to be on their default branch at release
The lock pins immutable commits, but nothing checked that anybody merged them. Today it pins server 40d85a4 and engine 9a4b0c6, both of which exist only on architecture/product-contracts branches, and a release built from this commit would have shipped and attested to a Server that never reached main. The release job's existing provenance check covers only the TSF commit being released, not the inputs the lock names.

verify-published compares each locked commit against its own repository's default branch and accepts only behind or identical, the two states that mean it is an ancestor. ahead and diverged both mean the commit sits on something nobody merged.

It runs in the release path and deliberately not in product-ci.yml. A pull request legitimately locks branch commits while one change lands across several repositories at once, which is exactly the situation this branch is in; enforcing reachability in pull request CI would deadlock the landing it exists to protect. At release time the judgement inverts, because an input that never reached its default branch is an input nobody reviewed.
2026-09-05 23:18:03 +09:00
Evan Hu 9b2844534e feat(product): keep WebView and native contracts in one locked combination 2026-09-05 23:10:29 +09:00
Evan Hu 1c045f1738 test(ipc): exercise real Server handshakes from x86 and x64 clients 2026-09-05 22:41:45 +09:00
Evan Hu 487011c802 feat(product): lock release inputs and share negotiated IPC contracts 2026-09-05 22:32:21 +09:00
Evan Hu 550e0a1c70 fix(ci): find the release pull request by branch, not by prs_created
The step ran only when release-please reported a pull request as created, which is once. It rewrites
that branch on every later push to main, and a release pull request that could not be merged the
first time is never retried -- main advancing during the build is the normal way that happens, and
the script deliberately leaves the pull request open in that case, so the two together meant it
would sit there forever.

Finding the branch through git/matching-refs and then its open pull request covers both: a rewritten
head gets a fresh build, and a pull request left open gets picked up on the next push. Taken from
MSIME-Linux#38, which caught the same mistake in the equivalent change there.
2026-09-05 21:58:56 +09:00
Evan Hu b02e4c8cc9 ci: build the release pull request before merging it
MSIME-Windows#121 records that CI cannot be made a required check here, because release-please's
pull request never gets one: GITHUB_TOKEN may not start workflows, so the pull_request run is
created in action_required and waits for a human forever.

I previously wrote on that issue that the only way out was a GitHub App or PAT. That was wrong.
workflow_dispatch is the exception GitHub allows GITHUB_TOKEN to fire, and MSIME-Apple's release
automation has been relying on it all along. Checked its release branch: workflow_dispatch runs
exist there and succeed, alongside pull_request runs that do not.

Also checked the two alternatives that looked cheaper, and neither works:

  - A push trigger on release-please--** produces nothing. Every run on that branch in MSIME-Linux
    is a pull_request event; a GITHUB_TOKEN push creates no run at all. MSIME-Linux#31 merged on
    the belief that it does.
  - The approval policy is not the cause. All six repositories are already on
    first_time_contributors_new_to_github, the loosest setting, and bot-triggered runs are still
    gated. Comparing runs, the gate tracks triggering_actor exactly: github-actions[bot] gives
    action_required, houko gives success.

So merge-release-pr.sh now dispatches ci.yml on the release branch, polls for the run at that exact
head commit, and waits for it with --exit-status before merging. The checks land on the pull
request's head commit, which is what required_status_checks needs.

It also re-reads main and the pull request head afterwards: main can advance during a build, and
merging then would rebase the release commit onto something that was never tested.

ci.yml gains workflow_dispatch so it can be dispatched at all. The paths-ignore stays: it keeps the
unapprovable pull_request run out of the checks list, leaving only the dispatched run's results,
which avoids two check runs with the same name and different outcomes on one commit.

The prepare job gains actions: write, without which gh workflow run cannot dispatch.
2026-09-05 21:41:40 +09:00
Evan 8e0dfa4041 ci: check out the UI resources and installer by their current repository names (#132)
MetasequoiaImeUiHtml and msime-installer are MSIME-UiHtml and MSIME-Installer;
both actions/checkout steps resolved only through GitHub's rename redirect.

The staging directory names are deliberately untouched. Lines 28 and 122 of
this file record why: msime-installer's Prepare-PackageFiles.ps1 expects
siblings called MetasequoiaImeServer, MetasequoiaImeUiHtml and so on, and
staging under those names is what lets it run unmodified in CI. Repository
names are current, checkout directory names stay historical.
2026-09-05 21:16:18 +09:00
Evan Hu e90ac18723 fix(ci): take the release scripts from the default branch, not the built commit
The TSF jobs failed with:

    The term './scripts/ci/check-tsf-dll.ps1' is not recognized as a name of a cmdlet, function,
    script file, or executable program.

Those jobs check the product out at the draft release's target commit, and then ran scripts/ci
from that checkout. scripts/ci arrived later than the commit v0.0.12 points at, so the directory
simply was not there. The same trap applies to every script the packaging job runs: a workflow
change can always outrun the scripts present at the tag being built, and it means the commit being
released could substitute its own release automation.

Each job that runs a script now checks the automation out separately from the default branch, the
way MSIME-Apple's release workflow does. The product still comes from the release commit; only the
automation is pinned to the branch the workflow itself came from.

Also here, since they are all .github correctness:

The UiHtml checkout named metasequoiaime/MetasequoiaImeUiHtml, which now resolves only through the
rename redirect to MSIME-UiHtml. A release pipeline should not depend on that; if anyone ever
creates a repository at the old name the redirect stops. The MetasequoiaIme* paths elsewhere in the
file are staging directory names the installer scripts require, and a comment now says so, so they
do not get "fixed" later.

The issue templates offered components under the pre-rename names, and the QQ group contact link
pointed at the old repository URL while the documentation link pointed at MetasequoiaIME, which
redirects to MSIME-Docs.

ISSUE_TEMPLATE/config.yml was not valid YAML: the QQ group about text contains a bare "QQ Group: ",
and a colon followed by a space cannot appear in a plain scalar. It predates this change; quoting
the value fixes it.

Two checkout pins dependabot could not reach, because the steps did not exist on main when it ran,
are brought up to the version the rest of the file uses.
2026-09-05 17:47:13 +09:00
Evan 0761b410e3 Merge pull request #122 from metasequoiaime/fix/installer-language-file
fix(ci): supply the Inno Setup language file, and move release shell into scripts/ci
2026-09-05 17:21:36 +09:00
Evan Hu d59be91390 refactor(ci): move every release shell step into scripts/ci
release.yml had grown thirteen inline shell and PowerShell blocks, some of them twenty lines, which made the workflow hard to read and impossible to lint or run outside Actions. Each one is now a file under scripts/ci and the workflow only wires them up.

Two things fall out of the move rather than being separate changes. The two signing steps were near-duplicates and are now one sign-binaries.ps1 taking a path list, used for both the packaged binaries and the finished installer. The pinned Inno Setup language file URL and checksum move to the packaging job's env, where they read as configuration instead of being buried mid-script.

The Server job now checks this repository out into automation/ for its two scripts. That checkout has to come after the MSIME-Server one, because checkout cleans its destination and the workspace root would take automation/ with it.

shellcheck is clean on all six shell scripts. The PowerShell is not syntax-checked here for lack of pwsh, but four of the seven are verbatim from steps that already ran green in run 33953797415.
2026-09-05 17:19:51 +09:00
Evan Hu 22a132e3bd fix(ci): install the Chinese language file Inno Setup needs
Packaging got all the way to compiling and then aborted:

    Error on line 61 in msime_setup.iss: Couldn't open include file
    "c:\program files (x86)\inno setup 6\Languages\ChineseSimplified.isl"

msime_setup.iss declares a chinesesimplified language pointing at compiler:Languages\ChineseSimplified.isl, and the Inno Setup on the runner does not ship that file. A developer machine has it because it was installed by hand at some point.

The packaging job now fetches it into the Languages directory next to the resolved ISCC.exe before compiling, skipping the download when it is already there. Pinned to a jrsoftware/issrc commit and checked against its SHA-256, so the installer's Chinese text cannot change under a rebuild. The file was verified to be the 6.5.0+ message set, which matches the 6.7.1 on windows-2025.

Everything before this step passed in run 33953797415, including Prepare-PackageFiles.ps1 collecting the payload, so this is the last thing between the workflow and an actual exe.
2026-09-05 17:14:38 +09:00