Compare commits

...

2 commits

Author SHA1 Message Date
CRT
c912ff371f Build releases without the build machine's paths, and scan the installer before it ships
A binary embeds the absolute paths of its sources: Rust panic locations in
textream.exe, and __FILE__ in the sherpa-onnx library, which this build
compiles locally with MSVC. `bun run release` now builds through wayBetter's
build-env (paths remapped for Rust, trimmed for MSVC C++) and then runs its
privacy-scan over the finished installer; a hit fails the release. The
reviewed false positives (an ONNX operator name, an HTTP header name) are in
privacy-allowlist.txt.
2026-09-30 17:50:55 +07:00
CRTkafa
9bc3667458 Fix the installer bitmaps' blur at its actual source
The previous anti-aliasing pass (2dce1c2) assumed the blur came from
Windows DPI-virtualizing a non-DPI-aware installer, and concluded it
was unfixable without shrinking the whole window. That assumption was
wrong: Tauri's generated installer.nsi already sets ManifestDPIAware
true and ManifestDPIAwareness PerMonitorV2, and NSIS's MUI2 scales
dialog controls to the real per-monitor DPI itself (confirmed by
reading the generated target/release/nsis/x64/installer.nsi). The
header and sidebar bitmap controls default to MUI_HEADERIMAGE_BITMAP_STRETCH
"FitControl", so at anything above 100% scaling NSIS was stretching our
150x57 / 164x314 (96 DPI-sized) source bitmaps up with GDI -- upscaling
a 1x source is exactly what produces the softness, independent of how
the source pixels were anti-aliased.

Regenerated both bitmaps at 4x native resolution (600x228, 656x1256)
from the actual app icon (src-tauri/icons/128x128@2x.png) and the
app's own ink/ink-dim/background colour tokens, with Bahnschrift Bold
text rendered with grayscale (non-ClearType) anti-aliasing. FitControl
now downsamples from oversampled source at every realistic scale
factor instead of upsampling, which is what actually fixes it -- verified
by reproducing NSIS's stretch-to-control-size step against both the old
and new bitmaps at 150% and 200%.
2026-09-28 02:34:13 +07:00
6 changed files with 76 additions and 1 deletions

View file

@ -233,6 +233,24 @@ Build the installer:
bun run app:build
```
Build the installer for a release:
```bash
bun run release
```
A binary embeds the absolute paths of what it was compiled from — Rust panic
locations, and `__FILE__` in the sherpa-onnx library this build compiles with
MSVC — and on a developer's machine those paths hold the account name. The
release build therefore runs through two commands of
[wayBetter](https://git.crt.fyi/CRT/waybetter)'s CLI (set `WAYBETTER_CLI` to its
`packages/waybetter/bin/waybetter.mjs`, or have `waybetter` on the `PATH`):
`build-env` builds with those paths remapped or trimmed, and `privacy-scan`
opens the finished installer and fails the release if the account name, or any
name listed in the file `WAYBETTER_FORBIDDEN_TERMS` points at, is still in it.
Reviewed false positives live in `privacy-allowlist.txt`. It needs CMake and
7-Zip, and only an installer that passed is published.
Run the test suite:
```bash

View file

@ -10,7 +10,8 @@
"check": "svelte-check --tsconfig ./tsconfig.json",
"tauri": "tauri",
"app": "tauri dev",
"app:build": "tauri build"
"app:build": "tauri build",
"release": "node scripts/release.mjs"
},
"dependencies": {
"@tauri-apps/api": "^2.11.1",

17
privacy-allowlist.txt Normal file
View file

@ -0,0 +1,17 @@
# Reviewed false positives of the release scan (scripts/release.mjs), one per line:
# <file glob>: <regex over the hit's surroundings, the matched word written <term>>
#
# onnxruntime.dll is Microsoft's prebuilt runtime. It names ONNX operators and quotes their
# documentation, and one operator's name is an ordinary English word that can also be a name.
# None of these is a path or a person.
**/onnxruntime.dll: Atanh<term>Min
**/onnxruntime.dll: onnxruntime::<term>_\d+<
**/onnxruntime.dll: <term> sequence length was
**/onnxruntime.dll: <term> detections to output
**/onnxruntime.dll: is_bidirectional<term> distance
**/onnxruntime.dll: = <term> ?\(
**/onnxruntime.dll: [QX]_<term>[ ,)_]
#
# textream.exe holds the names of the HTTP headers its webview layer knows; one of them carries
# the same English word.
**/textream.exe: Access-Control-<term>-Age

39
scripts/release.mjs Normal file
View file

@ -0,0 +1,39 @@
// The release build: the installer, built without this machine's paths, then scanned.
//
// A binary embeds the absolute paths of what it was compiled from: Rust panic locations, and
// `__FILE__` in the sherpa-onnx library this build compiles with MSVC. On a developer's machine
// those paths hold the account name. wayBetter's `build-env` runs the build with them remapped or
// trimmed, and its `privacy-scan` opens the finished installer and looks for what must not be in
// it. Nothing is published from here: a clean scan is what makes the installer publishable.
//
// bun run release (WAYBETTER_CLI: path to waybetter's bin/waybetter.mjs, else `waybetter`)
import { spawnSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = dirname(dirname(fileURLToPath(import.meta.url)));
const { version } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
const installer = join(root, 'target', 'release', 'bundle', 'nsis', `Textream_${version}_x64-setup.exe`);
const waybetter = process.env.WAYBETTER_CLI ? ['node', process.env.WAYBETTER_CLI] : ['waybetter'];
function run(step, [command, ...args]) {
console.log(`\n== ${step}`);
const quoted = [command, ...args].map(arg => (/\s/.test(arg) ? `"${arg}"` : arg)).join(' ');
const { status } = spawnSync(quoted, { cwd: root, stdio: 'inherit', shell: true });
if (status !== 0) {
console.error(`\nrelease: "${step}" failed (exit ${status ?? 'none'}). Nothing here is publishable.`);
process.exit(status || 1);
}
}
// CMake does not recompile objects when only the environment changed, so a sherpa-onnx left over
// from an ordinary build would keep its paths.
run('clean the release profile', ['cargo', 'clean', '--release']);
run('build the installer, path-free', [...waybetter, 'build-env', '--root', root, '--', 'bun', 'run', 'app:build']);
if (!existsSync(installer)) {
console.error(`release: the build left no ${installer}`);
process.exit(1);
}
run('scan the installer', [...waybetter, 'privacy-scan', installer, '--allow', join(root, 'privacy-allowlist.txt')]);
console.log(`\nClean. Publishable: ${installer}`);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 401 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Before After
Before After