mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
* ci(electron): verify packaged app.asar and smoke-test launch Adds coverage for the gap that let #7320 ship: CI built electron TS and ran electron-builder on release, but never exercised the packaged binary. A stray relative import reaching out of electron/** was compiled by tsc into src/app/util/*.js, fell outside the files glob in electron-builder.yaml, and was missing from app.asar — the main process crashed on launch for every 18.2.7 user. Two new checks, both running on ubuntu-latest: 1. tools/verify-electron-requires.js extracts app.asar and re-resolves every relative require() under electron/** against the packaged tree. Runs on every PR that touches electron/**, electron-builder config, package.json, or the workflow itself. ~10s after the build. 2. A launch-under-xvfb smoke test starts the packaged binary, waits 30s for a crash, and greps stderr for 'Cannot find module' / 'Uncaught Exception'. Runs on push to master, release tags, and manual dispatch (not every PR — needs xvfb setup + idle wait). Exposed as npm run electron:verify-asar for local use after dist. * ci(electron): harden smoke workflow and verify script from review Review feedback on the previous commit surfaced a handful of real defects. Addressed here: verify-electron-requires.js - Guard resolved paths against escaping the extracted asar tree. Without this, a relative require with enough `..` climbs above the temp dir and Node's resolver hits the host filesystem, masking a genuinely missing module behind a stray file on the CI runner. - Walk .cjs and .mjs files too. electron/simple-store.test.cjs ships via the `electron/**/*` glob and was being skipped. - Move cleanup out of the path where `process.exit(1)` runs inside a `try/finally` (synchronous exit does not guarantee `finally`), so the temp dir is reliably removed. - Code comment noting that esbuild-bundled preload.js is opaque to this walker — that coverage comes from the launch smoke test. electron-smoke.yml - Broaden pull_request paths filter. The previous filter only matched electron/**, which is exactly the wrong answer: #7320's root cause was a file under src/app/util/ that electron/ imported. Now uses a negative list — docs, translations, android/, ios/, other workflows. - Explicit Xvfb screen geometry (`-screen 0 1280x720x24`) and `--disable-dev-shm-usage` on the Electron side. Both known CI flake preventers. - Expanded crash-marker regex: SIGSEGV, Segmentation fault, TypeError:, ReferenceError:, FATAL ERROR, Check failed. Also scan the log during the liveness loop, not just at the end — catches main-process exceptions that get logged without killing the process. - Move the launch log under .tmp/smoke/ so upload-artifact@v7 reliably finds it on failure; /tmp paths are fragile across runner images. - Sweep stray electron worker processes on teardown via pkill. --------- Co-authored-by: Claude <noreply@anthropic.com>
212 lines
8.2 KiB
YAML
212 lines
8.2 KiB
YAML
name: Electron Packaging Smoke Test
|
|
|
|
# Validates the packaged Electron app — not just that sources compile.
|
|
#
|
|
# Catches bugs where code works at source level (tsc succeeds, `npm start`
|
|
# runs fine from loose files) but the production app.asar is missing a
|
|
# file that the main process require()s at startup. See #7320 for the
|
|
# motivating incident: electron/backup.ts imported from ../src/app/util/
|
|
# and the compiled dep was omitted from the electron-builder files glob,
|
|
# so every 18.2.7 binary crashed on launch.
|
|
#
|
|
# PRs run the cheap static check (~10s after the build) — paths filter
|
|
# excludes docs, translations, and platform-specific trees but still covers
|
|
# src/**, because #7320's root cause lived under src/app/util/. The full
|
|
# launch-under-xvfb smoke test runs on merge to master, release tags, and
|
|
# manual dispatch — stronger signal but needs xvfb setup + ~30s of wait.
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [master, main]
|
|
# Trigger broadly — #7320's root cause was a file under src/app/util/
|
|
# that electron/ imported, so narrowing to electron/** would have let
|
|
# the exact bug this workflow exists to prevent slip through again.
|
|
# Translation-only edits and non-code trees are excluded to keep
|
|
# unrelated PRs cheap.
|
|
paths:
|
|
- '**'
|
|
- '!docs/**'
|
|
- '!**/*.md'
|
|
- '!src/assets/i18n/**'
|
|
- '!android/**'
|
|
- '!ios/**'
|
|
- '!.github/ISSUE_TEMPLATE/**'
|
|
- '!.github/workflows/**'
|
|
- '.github/workflows/electron-smoke.yml'
|
|
push:
|
|
branches: [master]
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
linux-packaged-app:
|
|
name: Linux Packaged Electron App
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
UNSPLASH_KEY: ${{ secrets.UNSPLASH_KEY }}
|
|
UNSPLASH_CLIENT_ID: ${{ secrets.UNSPLASH_CLIENT_ID }}
|
|
steps:
|
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Check out Git repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# Work around npm installs from git+https://github.com/johannesjo/J2M.git
|
|
- name: Reconfigure git to use HTTP authentication
|
|
run: >
|
|
git config --global url."https://github.com/".insteadOf
|
|
ssh://git@github.com/
|
|
|
|
- name: Get npm cache directory
|
|
id: npm-cache-dir
|
|
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
|
|
|
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
|
with:
|
|
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
|
key: ${{ runner.os }}-node22-${{ hashFiles('**/package-lock.json') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-node22-
|
|
|
|
- name: Install npm packages
|
|
run: npm i
|
|
|
|
- run: npm run env
|
|
|
|
- name: Build frontend (production es6)
|
|
run: npm run buildFrontend:prod:es6
|
|
env:
|
|
NODE_OPTIONS: '--max-old-space-size=4096'
|
|
|
|
- name: Build electron main process
|
|
run: npm run electron:build
|
|
|
|
- name: Package electron app (linux dir, unpacked)
|
|
# --linux dir produces linux-unpacked/ with the real app.asar but
|
|
# skips AppImage/deb/snap/rpm — cheap and enough to exercise the
|
|
# same packaging pipeline release uses.
|
|
run: npx electron-builder --linux dir --publish never
|
|
|
|
- name: Verify require() targets in app.asar
|
|
run: |
|
|
ASAR=.tmp/app-builds/linux-unpacked/resources/app.asar
|
|
if [ ! -f "$ASAR" ]; then
|
|
echo "::error::Packaged app.asar not found at $ASAR"
|
|
ls -la .tmp/app-builds/ || true
|
|
exit 1
|
|
fi
|
|
node tools/verify-electron-requires.js "$ASAR"
|
|
|
|
- name: Install xvfb
|
|
if: github.event_name != 'pull_request'
|
|
run: sudo apt-get update && sudo apt-get install -y xvfb
|
|
|
|
- name: Smoke test — launch packaged app
|
|
if: github.event_name != 'pull_request'
|
|
run: |
|
|
set -uo pipefail
|
|
|
|
UNPACKED=.tmp/app-builds/linux-unpacked
|
|
# afterPack.js renames the real binary to superproductivity-bin and
|
|
# installs a shell wrapper at superproductivity. Launch the wrapper
|
|
# so the smoke test exercises the exact entry path users hit.
|
|
BIN="$UNPACKED/superproductivity"
|
|
if [ ! -x "$BIN" ]; then
|
|
echo "::error::Expected launcher at $BIN not found or not executable"
|
|
ls -la "$UNPACKED" || true
|
|
exit 1
|
|
fi
|
|
|
|
# Keep the log inside the workspace so upload-artifact can find it
|
|
# when a step fails. /tmp is accessible but fragile across runner
|
|
# images and upload-artifact glob evaluation.
|
|
mkdir -p .tmp/smoke
|
|
LOG=.tmp/smoke/launch.log
|
|
: > "$LOG"
|
|
|
|
echo "Launching $BIN under xvfb (log: $LOG)"
|
|
# Electron flags:
|
|
# --no-sandbox: chrome-sandbox needs setuid root, not set on runners
|
|
# --disable-gpu: no GPU on the runner
|
|
# --disable-dev-shm-usage: /dev/shm is small in GH-hosted containers,
|
|
# and Chromium/Electron can die with shared-memory errors without it
|
|
# Xvfb flags:
|
|
# --server-args: explicit screen geometry; Xvfb's 640x480x8 default
|
|
# trips some Chromium color-depth assertions
|
|
xvfb-run \
|
|
--auto-servernum \
|
|
--server-args="-screen 0 1280x720x24" \
|
|
"$BIN" \
|
|
--no-sandbox \
|
|
--disable-gpu \
|
|
--disable-dev-shm-usage \
|
|
> "$LOG" 2>&1 &
|
|
WRAPPER_PID=$!
|
|
|
|
# Wait up to 30s for the app to crash. The #7320 crash surfaces
|
|
# within ~100ms — 30s is generous headroom for Angular bootstrap
|
|
# in CI. xvfb-run exits when its child exits, so the wrapper PID
|
|
# going away is a reliable crash signal. We also scan stderr for
|
|
# known failure markers as a belt-and-suspenders backstop: some
|
|
# main-process exceptions log without killing the process.
|
|
CRASH_RE='(Uncaught Exception|Cannot find module|A JavaScript error occurred in the main process|Segmentation fault|SIGSEGV|SIGABRT|TypeError:|ReferenceError:|FATAL ERROR|Check failed)'
|
|
for i in $(seq 1 30); do
|
|
if ! kill -0 $WRAPPER_PID 2>/dev/null; then
|
|
echo "::error::xvfb-run/Electron exited after ${i}s — likely crash."
|
|
echo "----8<---- launch log"
|
|
cat "$LOG" || true
|
|
echo "---->8----"
|
|
exit 1
|
|
fi
|
|
if grep -E -q "$CRASH_RE" "$LOG" 2>/dev/null; then
|
|
echo "::error::Crash marker detected in log after ${i}s:"
|
|
grep -E "$CRASH_RE" "$LOG" || true
|
|
echo "----8<---- launch log"
|
|
cat "$LOG" || true
|
|
echo "---->8----"
|
|
kill -9 $WRAPPER_PID 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "App still alive after 30s — startup OK. Terminating."
|
|
kill $WRAPPER_PID 2>/dev/null || true
|
|
# xvfb-run forwards SIGTERM to its child; give them 5s to exit
|
|
# cleanly before SIGKILL. Also sweep any stray electron workers.
|
|
for i in 1 2 3 4 5; do
|
|
if ! kill -0 $WRAPPER_PID 2>/dev/null; then break; fi
|
|
sleep 1
|
|
done
|
|
kill -9 $WRAPPER_PID 2>/dev/null || true
|
|
pkill -9 -f "$UNPACKED/superproductivity" 2>/dev/null || true
|
|
wait $WRAPPER_PID 2>/dev/null || true
|
|
|
|
# Final scan for errors that didn't crash the main process but
|
|
# would still indicate a broken build.
|
|
if grep -E -q "$CRASH_RE" "$LOG" 2>/dev/null; then
|
|
echo "::error::Crash marker detected in final log scan:"
|
|
grep -E "$CRASH_RE" "$LOG" || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "Smoke test passed."
|
|
|
|
- name: Upload logs on failure
|
|
if: failure()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: electron-smoke-logs
|
|
path: |
|
|
.tmp/smoke/
|
|
.tmp/app-builds/builder-debug.yml
|
|
.tmp/app-builds/builder-effective-config.yaml
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|