fix(release): publish binaries — checkout CHANGELOG + externalize @fusion/engine (#1753)

## Problem

The latest release **v0.47.0 published 0 binaries**, and every desktop
build leg has been failing.

Two independent bugs in the release pipeline:

### 1. `github-release` job crashed → no binaries published
The job downloads artifacts but never checks out the repo. A step added
2026-06-24 (`Extract release notes from CHANGELOG`) does
`fs.readFileSync('CHANGELOG.md')`, which threw `ENOENT: no such file or
directory` and failed the whole job — so v0.47.0 shipped **0 assets**
even though every bun build leg succeeded. (v0.45/v0.46 predated this
step and still published 21 assets each.)

### 2. Desktop Windows EXE / macOS DMG legs failing
`packages/desktop/src/local-runtime.ts` dynamically imports
`@fusion/engine`, but engine was missing from the esbuild externals list
(only `@fusion/core` and `@fusion/dashboard` were there). esbuild
followed the import and tried to bundle engine's transitive `node-pty`
native binaries:

```
X [ERROR] No loader is configured for ".node" files:
  .../@homebridge/node-pty-prebuilt-multiarch/build/Release/pty.node
```

## Fix

- **`.github/workflows/release.yml`** — sparse-checkout `CHANGELOG.md`
in the `github-release` job, and harden the notes script to fall back to
a plain `Release vX.Y.Z` body instead of crashing the publish.
- **`packages/desktop/scripts/build.ts`** — externalize `@fusion/engine`
alongside the other workspace packages; it resolves from `node_modules`
at runtime, same as `core`/`dashboard`.

## Verification

- `pnpm build` runs clean locally — `dist/main.js` (623 kB) builds with
no `.node` loader error.
- YAML validated; `pnpm check:changesets` passes.

## Changeset

`"@runfusion/fusion": patch` (category `fix`) — restores
binary/installer publishing.

## Follow-up
These take effect on the next release. To backfill v0.47.0's missing
binaries, after merge delete + re-push the `v0.47.0` tag (the
tag-triggered run uses the workflow file at the tag).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed release publishing so binary and desktop installer artifacts are
published correctly again.
* Improved release note generation to fall back gracefully when
changelog details aren’t available, preventing failed releases.
* Updated desktop app bundling to avoid packaging native dependencies
incorrectly, reducing build and packaging issues.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
gsxdsm
2026-06-24 23:50:20 -07:00
committed by GitHub
3 changed files with 37 additions and 3 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix release pipeline so binaries and desktop installers publish again.
category: fix
dev: github-release job sparse-checks-out CHANGELOG.md (was missing a checkout, so the release-notes step threw ENOENT and published 0 assets on v0.47.0); desktop esbuild build externalizes @fusion/engine so it no longer tries to bundle node-pty's native .node binaries.

View File

@@ -373,6 +373,18 @@ jobs:
contents: write
steps:
# FNXC:Changelog 2026-06-25-09:30:
# The release-notes step reads root CHANGELOG.md, so this job needs the repo
# tree — not just downloaded artifacts. Without this checkout the notes step
# threw `ENOENT: CHANGELOG.md` and failed the whole job, publishing zero
# binaries on v0.47.0 even though every bun build leg succeeded. Sparse-checkout
# only CHANGELOG.md to keep the job lean; no submodules or full history needed.
- name: Checkout CHANGELOG for release notes
uses: actions/checkout@v4
with:
sparse-checkout: CHANGELOG.md
sparse-checkout-cone-mode: false
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
@@ -401,18 +413,25 @@ jobs:
id: notes
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
# FNXC:Changelog 2026-06-25-09:30:
# Never let release-note extraction fail the publish. A missing/unreadable
# CHANGELOG or absent version section falls back to a plain "Release vX.Y.Z"
# body so binaries still ship — matching the job's "partial over none" intent.
NOTES=$(node -e "
const fs = require('fs');
const content = fs.readFileSync('CHANGELOG.md', 'utf8');
const fallback = 'Release v${VERSION}';
let content = '';
try { content = fs.readFileSync('CHANGELOG.md', 'utf8'); }
catch (e) { console.log(fallback); process.exit(0); }
const lines = content.split(/\r?\n/);
const header = '## ' + '${VERSION}';
const start = lines.findIndex(l => l.trim() === header);
if (start === -1) { console.log('Release v${VERSION}'); process.exit(0); }
if (start === -1) { console.log(fallback); process.exit(0); }
let end = lines.length;
for (let i = start + 1; i < lines.length; i++) {
if (lines[i].startsWith('## ')) { end = i; break; }
}
console.log(lines.slice(start + 1, end).join('\n').trim() || 'Release v${VERSION}');
console.log(lines.slice(start + 1, end).join('\n').trim() || fallback);
")
echo "$NOTES" > /tmp/release-notes.md

View File

@@ -5,10 +5,18 @@ import { buildDashboardClient, packageRoot, workspaceRoot } from "./workspace-to
const dashboardClientDir = join(workspaceRoot, "packages", "dashboard", "dist", "client");
const desktopDistDir = join(packageRoot, "dist");
const desktopClientDistDir = join(desktopDistDir, "client");
// FNXC:DesktopBuild 2026-06-25-09:45:
// Every workspace @fusion/* package and native (.node) module must stay external
// to the Electron main/preload bundles — they resolve from node_modules at runtime.
// @fusion/engine was missing here, so esbuild followed local-runtime.ts's dynamic
// `import("@fusion/engine")` and tried to bundle engine's transitive node-pty
// (@homebridge/node-pty-prebuilt-multiarch) native binaries, failing with
// "No loader is configured for .node files" and breaking every desktop release build.
const sharedExternals = [
"electron",
"@fusion/core",
"@fusion/dashboard",
"@fusion/engine",
"better-sqlite3",
];
const mainExternals = sharedExternals;