187 lines
8.5 KiB
YAML
187 lines
8.5 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
|
||
# Icons (owner ruling, 2026-10-13) — per-platform sources in build/:
|
||
# mac build/icon.icon — the Icon Composer document (layered Liquid
|
||
# Glass); actool (Xcode 26+) compiles Assets.car (CFBundleIconName,
|
||
# macOS 26+ renders the true layered icon) + an actool fallback icns
|
||
# capped at 256px, which scripts/after-pack.cjs swaps for the
|
||
# hand-packed full-res build/icon.icns (pre-Tahoe keeps 16→1024).
|
||
# Requires actool ≥ 26: desktop:dist injects DEVELOPER_DIR locally;
|
||
# the release workflow's mac job runs setup-xcode.
|
||
# win build/icon.ico — generated (16→256) from the flat design by
|
||
# scripts/make-icons.mjs.
|
||
# linux build/icon.png — 512×512, same script. Rounded squircle with
|
||
# baked transparency on both (owner ruling) — Windows/Linux don't
|
||
# mask, the shape ships in the artwork.
|
||
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/**/*
|
||
# Main-process ID-token signature verification (no bundled OAuth implementation).
|
||
- node_modules/jose/**/*
|
||
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:
|
||
icon: build/icon.icon
|
||
# 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}"
|
||
# Owner artwork: 540×380 at 1×; the sibling background@2x.png is
|
||
# combined into a Retina TIFF by dmg-builder. Centres use 1× coordinates.
|
||
background: build/background.png
|
||
iconSize: 80
|
||
contents:
|
||
- x: 130
|
||
y: 190
|
||
type: file
|
||
- x: 410
|
||
y: 190
|
||
type: link
|
||
path: /Applications
|
||
afterPack: scripts/after-pack.cjs
|
||
win:
|
||
icon: build/icon.ico
|
||
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:
|
||
icon: build/icon.png
|
||
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
|