Release 1.4.35; build the one-file Full Setup on macOS too

- Version 1.4.35, published to the Gitea package registry and verified: the app
  downloads it, checks size and sha256, and lands a byte-identical installer.
- tools/build-full-installer.cjs picked node_modules/7zip-bin/win/x64/7za.exe
  unconditionally, so the one-file Full Setup could only be built on Windows.
  7za only packs the payload — the SFX stub and its config are concatenated
  byte-for-byte — so the host's own 7za yields the same Windows .exe. Also names
  the three payload files instead of passing a "*" that each 7za build expands
  its own way, and that would otherwise sweep the output archive into itself.
- BUILD-AND-DEPLOY.md / UPDATES.md rewritten for the laptop workflow and the
  Gitea update source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 13:25:40 -04:00
co-authored by Claude Opus 5
parent 838a87dbf3
commit 4e6d24093c
4 changed files with 88 additions and 69 deletions
+15 -3
View File
@@ -31,7 +31,19 @@ const installerName = `Bible App Setup ${version}.exe`;
const installerPath = path.join(DIST, installerName);
const modelPath = path.join(ROOT, 'vendor', 'llama', 'model.gguf');
const sfxModule = path.join(ROOT, 'tools', 'sfx', '7zSD.sfx');
const sevenZip = path.join(ROOT, 'node_modules', '7zip-bin', 'win', 'x64', '7za.exe');
/*
* 7za only packs the payload archive; the SFX stub and its config are plain byte concatenation, so
* the HOST's own 7za produces exactly the same Windows .exe. This used to be hard-coded to
* win/x64/7za.exe, which made the one-file installer buildable on Windows only — now the laptop can
* build it too.
*/
const sevenZip = (() => {
const base = path.join(ROOT, 'node_modules', '7zip-bin');
const arch = ['arm64', 'ia32', 'x64'].includes(process.arch) ? process.arch : 'x64';
if (process.platform === 'win32') return path.join(base, 'win', arch, '7za.exe');
if (process.platform === 'darwin') return path.join(base, 'mac', arch === 'arm64' ? 'arm64' : 'x64', '7za');
return path.join(base, 'linux', arch, '7za');
})();
const outName = `Bible App Full Setup ${version}.exe`;
const outPath = path.join(DIST, outName);
@@ -45,7 +57,7 @@ function need(p, what) {
need(installerPath, 'installer');
need(modelPath, 'AI model (vendor/llama/model.gguf)');
need(sfxModule, 'SFX module (tools/sfx/7zSD.sfx)');
need(sevenZip, '7za.exe');
need(sevenZip, `7za for ${process.platform}/${process.arch}`);
const mb = (n) => (n / 1048576).toFixed(1);
console.log(`Building one-file installer for ${version}`);
@@ -90,7 +102,7 @@ fs.writeFileSync(path.join(stage, 'install.cmd'), [
// LZMA would burn many minutes for ~0% gain.
const bundle = path.join(stage, 'payload.7z');
console.log(' packing : building archive (store mode)…');
execFileSync(sevenZip, ['a', '-t7z', '-mx0', '-bso0', '-bsp0', bundle, path.join(stage, '*')], { stdio: 'inherit' });
execFileSync(sevenZip, ['a', '-t7z', '-mx0', '-bso0', '-bsp0', bundle, installerName, 'model.gguf', 'install.cmd'], { cwd: stage, stdio: 'inherit' });
/*
* ExecuteFile, NOT RunProgram. RunProgram launches via CreateProcess, which cannot start a .cmd at