156 lines
7.1 KiB
YAML
156 lines
7.1 KiB
YAML
# electron-builder config — LearnHarness desktop (macOS arm64; Windows/Linux x64).
|
|
#
|
|
# Shape (fact-checked 2026-09-17):
|
|
# - the app's real server (.next/standalone, curated by
|
|
# scripts/build-server.mjs) ships via extraResources — OUTSIDE the asar,
|
|
# because native .node/dylib can't dlopen from a read-only archive and
|
|
# server.js process.chdir()s into its own dir;
|
|
# - the asar holds only the shell (electron/main.cjs + package.json);
|
|
# - signed + notarized since 2026-10-08 (Developer ID Application cert in
|
|
# the login keychain; notary credentials in the LH_NOTARY keychain
|
|
# profile; the profile + team env vars are baked into desktop:dist).
|
|
appId: com.learnharness.LearnHarness
|
|
productName: LearnHarness
|
|
directories:
|
|
output: dist
|
|
files:
|
|
# Only the shell goes into the asar. ORDER IS LOAD-BEARING (payload
|
|
# wave, 2026-09-24): electron-builder's dependency collector packs the
|
|
# whole production dep tree into node_modules/ REGARDLESS of this
|
|
# allow-list — normalizeFiles turns this array into one {filter: [...]}
|
|
# fileSet, getNodeModuleFileMatcher feeds EVERY filter entry (positives
|
|
# included) to the per-dep copier, and without a negation its implicit
|
|
# '**/*' passes everything (the measured result: a 502MB asar holding
|
|
# next/@ai-sdk/pdfjs/mermaid/puppeteer… while the launcher requires
|
|
# nothing but electron + node builtins — the server ships via
|
|
# extraResources).
|
|
#
|
|
# The fix is gitignore-style ordering (verified against the installed
|
|
# app-builder-lib 26.15.3 + a DEBUG-mode rebuild): '!**/*' first excludes
|
|
# EVERYTHING, the two positives after it re-include only the shell.
|
|
# The per-dep collector then contributes ZERO files — app-builder-lib's
|
|
# filter evaluates each dep file's DESTINATION path (the
|
|
# stat.moduleFullFilePath quirk in util/filter.js getRelativePath), so
|
|
# '!**/*' matches every 'node_modules/<dep>/...' path and the bare
|
|
# positives can never re-include one (DEBUG logs confirm filesCount=0
|
|
# for all 566 modules). If a future electron-builder fixes that quirk,
|
|
# ~1MB of package.json stubs would start landing — harmless. The
|
|
# two-package.json layout avoids the collector entirely but
|
|
# restructures the repo. app.asar.unpacked dies with it: the natives
|
|
# were only unpacked because their packages were packed.
|
|
- "!**/*"
|
|
- electron/**/*
|
|
- package.json
|
|
# Auto-update (Wave C, 2026-10-10): electron-updater + its runtime tree
|
|
# re-included explicitly. The negation above excludes node_modules, and
|
|
# the shell's bare positives can't re-include them — these explicit
|
|
# node_modules positives do (gitignore order: the LAST matching pattern
|
|
# wins; verified post-build by listing the asar — see desktop:dist notes).
|
|
# Tree fact-checked via each package's declared dependencies:
|
|
# electron-updater → fs-extra, js-yaml, lazy-val, lodash.escaperegexp,
|
|
# lodash.isequal, semver, tiny-typed-emitter, builder-util-runtime;
|
|
# builder-util-runtime → debug, sax; fs-extra → graceful-fs, jsonfile,
|
|
# universalify; debug → ms; jsonfile → universalify (again); js-yaml →
|
|
# argparse. Keep in sync when electron-updater bumps (npm ls --all).
|
|
- node_modules/electron-updater/**/*
|
|
- node_modules/fs-extra/**/*
|
|
- node_modules/js-yaml/**/*
|
|
- node_modules/lazy-val/**/*
|
|
- node_modules/lodash.escaperegexp/**/*
|
|
- node_modules/lodash.isequal/**/*
|
|
- node_modules/semver/**/*
|
|
- node_modules/tiny-typed-emitter/**/*
|
|
- node_modules/builder-util-runtime/**/*
|
|
- node_modules/debug/**/*
|
|
- node_modules/sax/**/*
|
|
- node_modules/graceful-fs/**/*
|
|
- node_modules/jsonfile/**/*
|
|
- node_modules/universalify/**/*
|
|
- node_modules/ms/**/*
|
|
- node_modules/argparse/**/*
|
|
extraResources:
|
|
# Copy from the PARENT dir (.next), not .next/standalone itself:
|
|
# electron-builder hard-excludes a copy's root node_modules and the
|
|
# default no-filter pattern is a literal path match (empirically both
|
|
# node_modules/ and .next/ were silently dropped → server died on
|
|
# "Cannot find module 'next'"). With `from: .next` the filter's
|
|
# relative paths start with standalone/, dodging the root-node_modules
|
|
# rule, and the explicit wildcard (minimatch dot:true) reaches
|
|
# standalone/.next and standalone/node_modules.
|
|
- from: .next
|
|
to: server
|
|
filter:
|
|
- "standalone/**/*"
|
|
mac:
|
|
# zip rides next to dmg (flipped 2026-10-08): Squirrel.Mac — Electron's
|
|
# macOS auto-updater — installs from ZIP and cannot read DMGs. DMG is
|
|
# for humans; ZIP + blockmap are the updater's payload.
|
|
target:
|
|
- target: dmg
|
|
arch:
|
|
- arm64
|
|
- target: zip
|
|
arch:
|
|
- arm64
|
|
# Locale trim (payload wave, 2026-09-24): English-only app — cuts
|
|
# every non-en .lproj from the frameworks (the STT/mic entitlements
|
|
# don't depend on locales). Revisit only if localization ever lands.
|
|
electronLanguages:
|
|
- en
|
|
category: public.app-category.education
|
|
# Laid for the STT milestone (researcher-verified): the TCC mic prompt
|
|
# reads this string; macOS asks regardless of Electron's own handlers.
|
|
extendInfo:
|
|
NSMicrophoneUsageDescription: "LearnHarness uses the microphone only for speech-to-text input into your sessions."
|
|
hardenedRuntime: true
|
|
entitlements: build/entitlements.mac.plist
|
|
# Signing + notarization (flipped 2026-10-08): identity auto-detects the
|
|
# Developer ID Application certificate from the login keychain;
|
|
# notarize: true makes electron-builder sign → submit to Apple's notary
|
|
# service → staple the ticket, all inside this one build (credentials
|
|
# via the LH_NOTARY keychain profile, env baked into desktop:dist).
|
|
# Adds minutes per build (Apple's scan queue). Verification battery:
|
|
# codesign --verify --deep / spctl --assess / stapler validate.
|
|
notarize: true
|
|
dmg:
|
|
title: "${productName} ${version}"
|
|
win:
|
|
target:
|
|
- target: nsis
|
|
arch:
|
|
- x64
|
|
artifactName: "${productName}-${version}-${arch}-setup.${ext}"
|
|
# Unsigned Windows release (owner ruling): preserve icon/version metadata,
|
|
# skip signing, and do not require a publisher signature for updates.
|
|
signExecutable: false
|
|
verifyUpdateCodeSignature: false
|
|
nsis:
|
|
oneClick: true
|
|
perMachine: false
|
|
linux:
|
|
target:
|
|
- target: AppImage
|
|
arch:
|
|
- x64
|
|
- target: deb
|
|
arch:
|
|
- x64
|
|
artifactName: "${productName}-${version}-${arch}.${ext}"
|
|
category: Education
|
|
# Match Electron's app_id/WM_CLASS to the installed desktop launcher.
|
|
syncDesktopName: true
|
|
# Both targets update via electron-updater 6.8.9. AppImage replaces its
|
|
# own file; deb uses dpkg/apt and may request administrator permission.
|
|
publish:
|
|
# Update feed = GitHub Releases (decided 2026-10-08): latest-mac.yml +
|
|
# zip/blockmap live on releases; electron-updater reads them anonymously.
|
|
# REQUIRES THE REPO TO BE PUBLIC before 0.3.0 ships — anonymous downloads
|
|
# from a private repo are impossible, and a token baked into the app
|
|
# would be a leak. desktop:dist passes no --publish flag, so builds never
|
|
# upload; publishing happens at release time (GH_TOKEN + --publish).
|
|
- provider: github
|
|
owner: amosblomqvist
|
|
repo: learnharness
|
|
# All three CI jobs contribute assets; only the owner publishes it.
|
|
releaseType: draft
|