Skip to content

Instantly share code, notes, and snippets.

@graphemecluster
Last active November 7, 2025 23:55
Show Gist options
  • Select an option

  • Save graphemecluster/8c54087cfaba7cdfe6b97d15885842ee to your computer and use it in GitHub Desktop.

Select an option

Save graphemecluster/8c54087cfaba7cdfe6b97d15885842ee to your computer and use it in GitHub Desktop.
Script for signing a TypeDuck Windows release (since I've got a physical HSM). Supply the version as the first argument and it will download the exe from a GitHub release, sign its content and itself and upload back it. Require access to the https://github.com/TypeDuck-HK/TypeDuck-Windows repo. Download TypeDuck: https://typeduck.hk
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Mandatory, Position = 0)]
[ValidatePattern("^v?\d+\.\d+\.\d+$")]
[string]$Version,
[Parameter()]
[ValidatePattern("^\d+$")]
[string]$Build = "0"
)
# common parameter, must be obtained this way
$VerboseFlag = $PSBoundParameters["Verbose"] -or $VerbosePreference -ne "SilentlyContinue" ? @("/v") : @()
if (!(Get-Command wget -ErrorAction SilentlyContinue)) {
function wget {
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Mandatory, Position = 0)]
[string]$Uri,
[Parameter()]
[string]$P = "."
)
$fileName = Split-Path $Uri -Leaf
Invoke-WebRequest $Uri -OutFile $P/$fileName
}
}
function Sign {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromRemainingArguments, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string[]]$Paths
)
signtool sign @VerboseFlag /n "The Education University of Hong Kong" /tr http://timestamp.sectigo.com /td sha256 /fd sha256 @Paths
if ($LASTEXITCODE) {
throw "Failed to sign"
}
}
function Verify {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromRemainingArguments, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string[]]$Paths
)
signtool verify @VerboseFlag /pa @Paths *> $null
return !$LASTEXITCODE
}
# https://stackoverflow.com/a/77056311
$oldErrorActionPreference = $global:ErrorActionPreference
$global:ErrorActionPreference = "Stop"
try {
if ($PSBoundParameters.ContainsKey("ErrorAction") -and $PSBoundParameters["ErrorAction"] -ne "Stop") {
throw "The 'ErrorAction' common parameter must be 'Stop' if provided to avoid partial completion of the whole process"
}
$missingCommands = @("wget", "7z", "signtool", "makensis", "gh") | Where-Object { !(Get-Command $_ -ErrorAction SilentlyContinue) }
if ($missingCommands) {
$message = "Please specify the path$($missingCommands.Count -gt 1 ? 's' : '') to $($missingCommands -join ', ') in the PATH."
if ($missingCommands -contains "signtool") {
$message += " Hint: You should run this script inside a Developer PowerShell for Visual Studio for signtool to be defined"
}
throw $message
}
$WEASEL_VERSION = $Version -replace "^v", ""
$WEASEL_BUILD = $Build
$SOURCE_URL_PREFIX = "https://raw.githubusercontent.com/TypeDuck-HK/TypeDuck-Windows/refs/tags/v${WEASEL_VERSION}"
# If the folder already exists, the following produces an error and the script is terminated, which prevent re-signing
mkdir $PSScriptRoot/${WEASEL_VERSION}
pushd $PSScriptRoot/${WEASEL_VERSION}
try {
gh release download v${WEASEL_VERSION} --repo TypeDuck-HK/TypeDuck-Windows
$executable = Get-Item TypeDuck*.exe
if ($executable.Count -gt 1) {
throw "Expect only a single executable, got $($executable.Count)"
}
if (Verify TypeDuck*.exe) {
throw "$($executable.Name) is already signed"
}
7z x TypeDuck*.exe -aou -ooutput
pushd output
try {
# TODO rime.dll should be signed in advance (right after it is built)
# TODO 7z.exe, 7z.dll, curl.exe, WinSparkle.dll and the NSIS plugin DLLs are not signed. n.b. if they are signed, their original hashes will be lost. Anti-virus softwares no longer recognise them and may flag them the other way round.
# TODO When we migrate to newer versions of Weasel, there will be a Win32 folder, the executables in which need to be signed too, c.f. https://github.com/rime/rime-cantonese/blob/41816434b0b2457c772eae5e7dfd85054e8f5e24/.ci/package-windows.sh
# n.b. Expect case-insensitive match, be careful when running in POSIX systems
Sign TypeDuck*.exe TypeDuck*.dll TypeDuck*.ime rime.dll
mkdir ../resource
wget ${SOURCE_URL_PREFIX}/resource/TypeDuck.ico -P ../resource
wget ${SOURCE_URL_PREFIX}/resource/Installer.bmp -P ../resource
wget ${SOURCE_URL_PREFIX}/output/install.nsi
mkdir archives
makensis /DWEASEL_VERSION=${WEASEL_VERSION} /DWEASEL_BUILD=${WEASEL_BUILD} install.nsi
Sign archives/TypeDuck*.exe
gh release upload v${WEASEL_VERSION} archives/TypeDuck*.exe --repo TypeDuck-HK/TypeDuck-Windows --clobber
}
finally {
popd
}
}
finally {
popd
}
}
finally {
$global:ErrorActionPreference = $oldErrorActionPreference
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment