fix: actually generate the UTF-8 locale makensis needs

`locale-gen <name>` is an Ubuntu extension; plain Debian's locale-gen
takes no arguments and only reads /etc/locale.gen, so the previous fix
generated nothing and makensis kept aborting with exit 84 / "FATAL: main
argv conversion failed!" under the runner's C/POSIX locale.

Write the entry into /etc/locale.gen, run locale-gen with no arguments,
and assert `locale -a` lists it — a missing locale now fails the install
step with a clear message instead of dying four minutes later inside
electron-builder.

build-release.ps1 sets LC_ALL/LANG itself before packaging a Windows
target on a Linux host, picking a real UTF-8 locale out of `locale -a`.
The electron-builder child then inherits it however the script was
started, including a runner that drops step env.

Also adds a non-fatal probe step that runs the cached makensis with
-VERSION under en_US.UTF-8 and under C, so a failure that is *not* the
locale is distinguishable from one that is without another blind fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 15:42:43 +02:00
co-authored by Claude Opus 5
parent f5ad546c62
commit d4f23f5b87
2 changed files with 91 additions and 3 deletions
+56
View File
@@ -169,6 +169,54 @@ function Invoke-Checked {
}
}
function Set-Utf8Locale {
<#
electron-builder's native Linux makensis converts its own argv through the
locale charset before it parses anything, and cannot do it under a non-UTF-8
locale:
.../nsis-3.0.4.1/linux/makensis process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE
Exit code: 84
Output: FATAL: main argv conversion failed!
A minimal Debian box — a CI runner in particular — defaults to C/POSIX,
which is exactly that case. Setting it here rather than only in the CI
workflow's env: block means the electron-builder child inherits it however
this script was started: a runner that drops step env, a bare
`pwsh ./scripts/build-release.ps1` over ssh, cron.
Which locales exist is a property of the host, so `locale -a` is consulted
rather than guessed at: C.UTF-8 is a glibc built-in only from 2.35
(Debian 12+), and en_US.UTF-8 exists only once someone has run locale-gen.
#>
$current = if ($env:LC_ALL) { $env:LC_ALL } else { $env:LANG }
if ($current -match '(?i)\.utf-?8$') { return }
$available = @()
try { $available = @(& locale -a 2>$null) } catch { }
# Debian spells them "C.utf8" / "en_US.utf8" in `locale -a` but wants
# "en_US.UTF-8 UTF-8" in /etc/locale.gen; both spellings are accepted by
# setlocale, so match either and fall back to any UTF-8 locale at all.
$pick = @("C.UTF-8", "C.utf8", "en_US.UTF-8", "en_US.utf8") |
Where-Object { $available -contains $_ } | Select-Object -First 1
if (-not $pick) {
$pick = $available | Where-Object { $_ -match '(?i)\.utf-?8$' } | Select-Object -First 1
}
if (-not $pick) {
throw ("no UTF-8 locale on this host, and makensis aborts with 'FATAL: main argv " +
"conversion failed!' without one. Generate one:`n" +
" sudo apt-get install -y locales`n" +
" echo 'en_US.UTF-8 UTF-8' | sudo tee -a /etc/locale.gen`n" +
" sudo locale-gen`n" +
"then re-run. 'locale -a' listed: $($available -join ', ')")
}
Write-Host " locale: LC_ALL=$pick (was '$current'; makensis needs UTF-8)" -ForegroundColor DarkGray
$env:LC_ALL = $pick
$env:LANG = $pick
}
function Get-ArtifactName {
<#
electron-builder rejects an artifactName that has no ${ext} macro, and
@@ -562,6 +610,14 @@ try {
"mkdir -p $(ConvertTo-BashArg $wslStage) && rm -f $(ConvertTo-BashArg $wslStage)/$prefix-*")
}
# Both Windows targets go through makensis (portable is an NSIS build too), and
# on a Linux host that is a native binary this process's locale reaches. The WSL
# path is not covered: wsl.exe does not carry $env:LC_ALL into the distro, and a
# distro someone installed interactively has a UTF-8 locale already.
if (-not $onWindows -and @($resolvedTargets | Where-Object { $_ -like "win*" }).Count) {
Set-Utf8Locale
}
foreach ($target in $resolvedTargets) {
Write-Host "Packaging $target..." -ForegroundColor Cyan