feedBack-desktop/.github/workflows/addon-ci.yml
2026-06-16 18:48:12 +02:00

129 lines
5.6 KiB
YAML

name: Addon CI
# Per-OS build + load smoke-test for the native audio addon
# (slopsmith_audio.node), on every pull request.
#
# Why this exists, separate from build.yml:
# build.yml runs the FULL electron-builder packaging/signing matrix and is
# deliberately gated off the pull_request event (every review commit would
# otherwise re-run a ~3x multi-GB release build). That left a gap: a PR that
# breaks the macOS/Linux *addon* compile or link was never built on those
# OSes until it had already merged to main. That is exactly how the #250
# regression shipped — an undefined symbol (typeinfo for
# slopsmith::sandbox::SandboxedProcessor) that links fine but fails at
# runtime dlopen, leaving AppImage/dmg users with empty audio device lists
# (issue #266, regression fixed in #263).
#
# This job is the cheap counterpart: it builds ONLY the addon
# (`npm run build:audio` — no Python, no electron-builder, no
# signing) on all three OSes, then loads the resulting .node and asserts the
# device-enumeration API is present. A plain build can't catch the #250 class
# of bug: Node native modules link with undefined symbols permitted
# (macOS uses -undefined dynamic_lookup; Linux has no -Wl,--no-undefined), so
# the compile and link succeed even with an unresolved symbol — the failure
# only surfaces at load time. The smoke-test load is what makes that fail CI.
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
# Cancel an in-flight run when new commits land on the same PR/branch — no
# point burning three runners on a superseded commit during a review loop.
concurrency:
group: addon-ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Least privilege: this workflow only checks out code.
permissions:
contents: read
jobs:
addon:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
platform: linux
arch: x64
- os: macos-14
platform: mac
arch: arm64
- os: windows-latest
platform: win
arch: x64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive # JUCE (and any vendored audio third_party)
# No step after checkout uses GITHUB_TOKEN (public repo, public
# submodules, no push), and this runs on pull_request — including
# from forks — so don't leave the token persisted in .git/config.
persist-credentials: false
# Match the Node version the app ships with — the addon is built against
# the Electron ABI, but node-addon-api headers come from node_modules and
# the toolchain is pinned via .build-config.json (single source of truth,
# same as build.yml).
- name: Read build configuration
id: config
shell: bash
run: |
NODE_VERSION=$(node -p "require('./.build-config.json').versions.node")
echo "node=$NODE_VERSION" >> "$GITHUB_OUTPUT"
echo "Node ${NODE_VERSION}"
- uses: actions/setup-node@v4
with:
node-version: ${{ steps.config.outputs.node }}
# Pin the Node binary architecture per runner (arm64 on macos-14,
# x64 elsewhere) so the addon's node-addon-api headers and the
# toolchain match the OS the build targets.
architecture: ${{ matrix.arch }}
# Linux: the addon links juce_audio_devices (ALSA/JACK) and pulls in
# juce_audio_processors' transitive X11/freetype/fontconfig deps at build
# time. Install from .packages/apt.txt — the same canonical list
# build-linux-ubuntu.sh uses — so this stays in sync with the real build.
# xvfb is added on top (not in apt.txt) purely as insurance for the
# load-time smoke-test below.
- name: Install Linux build deps
if: matrix.platform == 'linux'
run: |
sudo apt-get update
PACKAGES=$(grep -v '^[[:space:]]*#' .packages/apt.txt | grep -v '^[[:space:]]*$' | tr '\n' ' ')
sudo apt-get install -y --no-install-recommends $PACKAGES xvfb
# cmake-js + node-addon-api come from node_modules. No package-lock.json
# in this repo, so `npm install` (not `npm ci`).
- name: Install npm dependencies
run: npm install
- name: Build native audio addon
run: npm run build:audio
shell: bash
# Load the freshly built .node and assert the device-enumeration entry
# points are present. slopsmith_audio.node is an N-API addon (ABI-stable
# across runtimes), so plain `node` can load an Electron-built binary —
# this is the exact reproduction Byron confirmed locally for #250, where
# the load fails with "undefined symbol: ...SandboxedProcessor..." even
# though the build was green. typeof getDeviceTypes === 'function' also
# verifies the device API the empty-device-list regression was about.
- name: Smoke-test addon load (Linux)
if: matrix.platform == 'linux'
shell: bash
run: |
xvfb-run -a node -e "const a=require('./build/Release/slopsmith_audio.node'); if(typeof a.getDeviceTypes!=='function'){console.error('FAIL: getDeviceTypes export missing'); process.exit(1);} console.log('addon loaded; device API present');"
- name: Smoke-test addon load (macOS / Windows)
if: matrix.platform != 'linux'
shell: bash
run: |
node -e "const a=require('./build/Release/slopsmith_audio.node'); if(typeof a.getDeviceTypes!=='function'){console.error('FAIL: getDeviceTypes export missing'); process.exit(1);} console.log('addon loaded; device API present');"