A contributor who only wants to try a change should not have to install
vcpkg, Boost, Inno Setup and the Windows SDK. The Dev build workflow builds,
packages and signs on the runner, and the artifact is a full installer plus
its signing certificate.
Three ways in: a feature-branch push builds automatically; a pull_request
run gives reviewers and fork contributors an installer from the check page
(pull_request deliberately, not pull_request_target -- building PR code
executes scripts from that code, and pull_request_target would hand those
scripts a writable token); workflow_dispatch stays for picking a single
component or rebuilding without a push. develop, main and release-please
maintenance branches never trigger it.
The certificate is minted per run by installer/sign-dev-build.ps1 with the
fixed subject CN=Metasequoia IME Dev Build Code Signing; the private key
lives only in the runner's memory. installer/import-dev-trust.ps1 ships in
the artifact for the user side: remove the previous certificate by subject,
import the new one, so repeated builds do not grow the trust stores.
The workflow reuses ci.yml's vcpkg cache scopes and sccache, downloads the
locked dictionaries, embeds the uiAccess manifest and passes the dev version
through MSIME_PRODUCT_VERSION_OVERRIDE so the about page shows the full dev
string. Artifacts expire in 7 days. ci.yml remains the merge gate; nothing
here creates a release or touches the update manifest.
* ci(release): run the SignPath path on GitHub-hosted runners
SignPath's open-source plan accepts a signing request only when every job leading up to it ran on a GitHub-hosted runner, and tsf, server and package all ran on the self-hosted release machine. With WINDOWS_SIGNING_PROVIDER=signpath they now run on windows-2025, restoring vcpkg binaries and (for the Server) sccache the way ci.yml does; the certificate path keeps the self-hosted runner unchanged.
The org secret holding the SignPath API token is SIGNPATH_TOKEN, so the workflow and detect-release-signing.ps1 read that name instead of SIGNPATH_API_TOKEN.
AGENTS.md lists what SignPath needs configured, and the code signing policy records the accepted application and the hosted-runner rule.
* fix(ci): let the SignPath action read the run's artifacts
* fix(installer): drop authenticode-sign attributes SignPath Foundation rejects
* fix(ci): fail the hosted SignPath release before signatures are spent
- Probe ISCC with WizardStyle=modern dynamic and run that probe at the start of the hosted package job, so an image ISCC older than 6.6 fails before the payload signing request instead of after it.
- Verify the TSF vcpkg root before installing into it or saving its archives under the -complete key.
- Check the Server job's C:\vcpkg override against the vcpkg the hosted image puts on PATH.
- Code signing policy: drop the stale "once approved" wording and state that the hosted-runner rule applies to SignPath-signed releases.
* docs: list only the configured SignPath approver
---------
Co-authored-by: fanlusky <1730976608@qq.com>
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.
Automatic per-merge builds shared one list with deliberately cut releases, and the previous change only made them identifiable rather than separate: both were still prereleases, which is the thing the issue objects to. Labelling was the wrong answer to it.
GitHub has no channel concept, but it has two published states, and there are exactly two kinds of release here. A push-triggered build is now a prerelease; a workflow_dispatch build is an ordinary release and the newest one carries the Latest badge. The Releases page shows two lists instead of one list with two kinds of title in it, and /releases/latest resolves for the first time.
The prerelease flag therefore means "automatic and uncurated" rather than "beta". The product being 内测 is stated in the release notes and in docs/installation.md, which is where a claim about stability belongs; the flag cannot carry that meaning while it is also the only thing separating the channels. docs/installation.md now tells readers to take the Latest badge, describes both channels, and says explicitly that being in the release channel means somebody picked it, not that it is stable.
Already published releases are not touched, so the Latest badge stays empty until the first dispatch after this lands.
Refs MSIME-Windows#167
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.
MSIME-HelpCode is no longer a product input and the dictionary now ships from MSIME-Engine, so the readme, the agent notes, the release documentation and the issue templates stop naming a repository the product does not consume.
The product README was under windows/ after the move, but it was never a component readme: it is the download page, the feature guide and the contribution guide for the whole product, so it goes back to the root and windows/ gets a real component readme.
AGENTS.md at the root now carries the repository map, the component boundaries that used to be cross-repository rules, and the release process, since the scripts that process drives live at the root. The component files keep what is genuinely theirs and say which paths they are relative to. References that named sibling repositories now name sibling directories, and the lock documentation records that Server, the pages, the GUI framework and the installer are no longer inputs to pin.
Preparation for folding Server, UI, UiHtml, Installer, ImeLog and TsfEditControl into this repository. The Windows tip's own sources, build files and local build scripts move under windows/ so every component sits at the same level. Product-level files stay at the root: the lock, the release manifest, the shared CI scripts, the integration tests, the docs and the Engine submodule.
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.
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.
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.
The Windows installer has been produced by hand: build the TSF DLLs and the Server locally, build the settings page, copy dictionaries out of a developer checkout, then run the msime-installer scripts against a self-signed certificate. This automates that end to end, keeping the same artifact name the releases have always carried, MetasequoiaIME_Setup_v<version>.exe.
The workflow follows the shape of MSIME-Apple's release.yml. release-please owns version.txt and opens a release PR; merging it creates a draft release, which the publish path then builds and fills in. A manual dispatch can rebuild an existing draft tag, and validates that the tag matches version.txt at an immutable commit SHA before anything is built.
Four jobs feed the packaging job: the TSF DLLs for x64 and Win32, the Server Release build, and the settings page. msime-installer needs no changes because the packaging job stages each checkout under the historical directory name its scripts already address, so Prepare-PackageFiles.ps1 and Compile-Installer.ps1 run unmodified. windows-2025 ships Inno Setup 6.7.1, above the 6.6 the scripts require.
Dictionaries are not rebuilt here. They are downloaded from a dict-* release of metasequoiaime/MSIME-Dict and checked against its SHA256SUMS.txt, with the Mozc IPAdic / ICOT / Okinawa notice placed where Prepare-PackageFiles.ps1 expects so it reaches the installed product.
Signing follows the pattern MSIME-Apple uses: sign when the certificate secrets are configured, otherwise publish with an -unsigned suffix and say in the release notes that uiAccess will not take effect. Sign-PackageBinaries-Local.ps1 mints a self-signed certificate for local testing and is never invoked here.
release-please cannot drive the VERSIONINFO block in the resource script, which spells the version both as a comma quad and as a dotted string, so scripts/apply_version.py stamps it from version.txt before the release build configures CMake. It also has a --check mode. The committed resource moves off the stale 1.0.0.1 placeholder to the current 0.0.9.
The manifest starts at 0.0.9 rather than the four-part 0.0.9.2 that semver cannot express, so the next release will be 0.0.10, which still sorts after every existing tag.