Skip to content

🤖 fix: unpack sharp runtime assets for macOS attach_file#3088

Merged
ThomasK33 merged 3 commits intomainfrom
fix/macos-attach-file-sharp-runtime
Mar 29, 2026
Merged

🤖 fix: unpack sharp runtime assets for macOS attach_file#3088
ThomasK33 merged 3 commits intomainfrom
fix/macos-attach-file-sharp-runtime

Conversation

@ThomasK33
Copy link
Copy Markdown
Member

Summary

Unpack sharp's native runtime assets from the Electron ASAR on macOS and add a packaged-app smoke test so nightly/release builds fail before shipping broken image attachments.

Background

Nightly macOS builds could reach the real attach_file raster path and then fail when sharp tried to load libvips from inside the packaged app. The existing unit tests covered resize behavior in development mode, but nothing verified the packaged .app or app.asar.unpacked contents.

Implementation

  • added sharp and @img runtime trees to Electron's asarUnpack list
  • added a desktop-only smoke-test entry path that runs the real readAttachmentFromPath() flow against oversized PNG/JPEG fixtures and exits
  • added scripts/checkMacAttachFileRuntime.ts plus make check-mac-attach-file-runtime to verify unpacked native assets and launch the packaged app in smoke-test mode
  • wired the macOS release workflow to run the packaged smoke test immediately after make dist-mac-release

Validation

  • nix shell nixpkgs#hadolint -c make static-check
  • env LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 bun test src/node/services/tools/attach_file.test.ts

Risks

This increases the unpacked macOS app footprint by shipping the full sharp and @img runtime trees outside the ASAR. The change is intentionally scoped to packaging and smoke-test plumbing; the production attachment logic remains unchanged.

Pains

The Linux sandbox here cannot build or launch the packaged macOS .app, so the new packaged smoke test is validated structurally in code and delegated to the macOS release lane for runtime execution. Local sharp testing in this environment also required LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 to work around the sandbox's default C++ runtime path.


📋 Implementation Plan

Fix macOS nightly image attachments by unpacking sharp native assets

Goal

Nightly macOS Electron builds should be able to attach raster images through attach_file without failing to load sharp/libvips at runtime.

Verified context

  • Production image attachments flow through src/node/services/tools/attach_file.ts -> src/node/utils/attachments/readAttachmentFromPath.ts -> src/node/utils/attachments/resizeRasterImageAttachment.ts.
  • resizeRasterImageAttachment.ts lazy-loads sharp via createRequire(__filename) / requireSharp("sharp"), so the failure happens only when a raster image is processed.
  • package.json currently unpacks native assets for node-pty and @duckdb under build.asarUnpack, but not for sharp or the @img/* packages that hold sharp's platform binaries and libvips dylibs.
  • The user-visible error references @img/sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib, which matches sharp 0.34.x's platform-package layout.
  • Docker packaging already copies node_modules/sharp and node_modules/@img, which is a strong repo-local signal that runtime needs both trees available outside a compressed bundle.
  • Existing tests exercise resize behavior (src/node/services/tools/attach_file.test.ts) but do not validate a packaged macOS app, so ASAR regressions currently slip through.
Why this looks like packaging, not install-time dependency drift

The failure is surfacing in the packaged nightly app after build, not during bun install or unit tests. Current evidence points to Electron ASAR packing as the primary breakage: native sharp artifacts are present during development/test installs, but the packaged app cannot resolve the darwin-arm64 dylib chain once those assets remain inside the ASAR. That makes build.asarUnpack the minimal, first-line fix.

Recommended approach (primary)

Approach A: unpack sharp's full runtime trees in Electron packaging
Net product-code estimate: +2 LoC in package.json

Change package.json build.asarUnpack to add broad, low-risk globs for the full sharp runtime trees:

  • **/node_modules/sharp/**/*
  • **/node_modules/@img/**/*

Why this is the best first fix

  • It matches the existing pattern already used for other native runtime dependencies (node-pty, @duckdb).
  • It keeps the production attachment code path unchanged; the bug is in packaging, not in attach_file logic.
  • It covers both the sharp loader package and the platform-specific libvips assets without overfitting to one extension or one macOS CPU architecture.
  • It aligns with the repo's Docker runtime behavior, which already preserves those directories wholesale.

Fallback only if app size or packaging behavior becomes a problem

Approach B: narrow the unpack globs to sharp build/lib + native files under @img
Net product-code estimate: +4 to +8 LoC in package.json

Use more targeted patterns such as:

  • **/node_modules/sharp/build/**/*
  • **/node_modules/sharp/lib/**/*
  • **/node_modules/@img/**/*.node
  • **/node_modules/@img/**/*.dylib
  • **/node_modules/@img/**/*.so

Only take this route if the broad globs materially bloat the unpacked app or if electron-builder's glob handling needs tighter targeting.

Implementation plan

Phase 1 — Fix the packaged runtime

Files: package.json

  1. Update build.asarUnpack so Electron ships sharp's runtime artifacts outside the compressed ASAR.
  2. Keep the change minimal and grouped with the existing native-module unpack entries.
  3. Do not change src/node/utils/attachments/resizeRasterImageAttachment.ts unless the packaging-only fix is proven insufficient.

Quality gate after Phase 1

  • Build the macOS app (make dist-mac or the nightly-equivalent release target).
  • Inspect the produced app bundle and confirm app.asar.unpacked contains both:
    • node_modules/sharp/...
    • node_modules/@img/...
  • Spot-check for a darwin sharp binary (sharp.node) and a darwin libvips dylib (libvips-cpp*.dylib).
  • If those files are still absent but the macOS runner's node_modules does contain sharp + @img before packaging, escalate to Approach B before touching runtime code.
  • If the macOS runner never got the @img packages in node_modules to begin with, inspect .github/actions/setup-mux/action.yml / dependency-cache behavior as a second-order fix only after ruling out the ASAR unpack issue.

Phase 2 — Add a regression guard that would have caught this

Likely files: packaged-app smoke test under tests/e2e/**, a dedicated script, and/or CI wiring in .github/workflows/_desktop-release.yml or Makefile

  1. Add one packaged-app smoke test that launches the built macOS app and exercises attach_file with a large PNG or JPEG so sharp must load and resize.
  2. Assert behavior, not copy:
    • the attachment succeeds,
    • resizing occurs when expected,
    • no ERR_DLOPEN_FAILED / missing libvips-cpp error is emitted.
  3. Keep this focused on the packaged-app runtime; existing unit tests already cover resize logic in development mode.

Quality gate after Phase 2

  • Re-run targeted unit coverage for attach_file.
  • Run the packaged-app smoke test against the freshly built macOS app.
  • Confirm the smoke test fails if the new unpack globs are removed (or would have failed before the fix), so it is guarding the real regression.

Phase 3 — Wire the guard into the macOS nightly path

Likely files: .github/workflows/_desktop-release.yml, supporting Makefile/script entrypoints

  1. Run the packaged-app smoke test in the macOS release/nightly lane after the app is built.
  2. Keep the smoke test scoped to the macOS packaged build where this bug manifests.
  3. Preserve fast feedback by avoiding unnecessary cross-platform expansion in the first pass.

Quality gate after Phase 3

  • Verify the nightly lane fails closed when sharp assets are missing from the packaged app.
  • Confirm the workflow still publishes on a green macOS build with the smoke test enabled.

Acceptance criteria

  • A packaged macOS nightly app can attach at least one large PNG and one JPEG through attach_file without sharp/libvips load errors.
  • The built .app contains unpacked sharp runtime assets in app.asar.unpacked.
  • Existing attachment/resize tests still pass.
  • The macOS release/nightly path includes a regression check for packaged image attachments.
  • No changes are made to unrelated attachment logic unless packaging-only remediation is proven insufficient.

Dogfooding and reviewer evidence

  1. Build the macOS packaged app locally or in a macOS CI runner.
  2. Launch the packaged app (not the dev build) and use a fresh workspace.
  3. Ask the agent to attach_file:
    • a large PNG screenshot,
    • a JPEG with orientation metadata if available.
  4. Capture reviewer evidence:
    • a screenshot of app.asar.unpacked showing unpacked sharp and @img trees,
    • a screenshot of the successful attachment/result in the packaged app,
    • a short screen recording of the end-to-end packaged-app flow from launch to successful image attachment.
  5. If automation is available later in Exec mode, prefer Electron/agent-browser-assisted reproduction so the screenshot/video steps are repeatable.

Risks and notes

  • Broad unpack globs may increase the unpacked footprint slightly; that is acceptable for the first fix if it makes nightly macOS builds reliable.
  • Rebuilding sharp for Electron is not the first move here; the repo evidence points to packaging visibility of native assets, not a missing ABI rebuild.
  • Keep the solution defensive and minimal: fix the packaging boundary first, then add a smoke test so future ASAR changes fail fast.

Generated with mux • Model: openai:gpt-5.4 • Thinking: high • Cost: $5.41

Unpack sharp and @img assets for Electron packaging and add a packaged macOS smoke check for attach_file runtime loading.

---

_Generated with `mux` • Model: `openai:gpt-5.4` • Thinking: `high` • Cost: `$5.41`_

<!-- mux-attribution: model=openai:gpt-5.4 thinking=high costs=5.41 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a27a453bf4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Run the macOS packaging step with publish disabled so the attach_file smoke test executes before any release artifacts are uploaded.

---

_Generated with `mux` • Model: `openai:gpt-5.4` • Thinking: `high` • Cost: `$5.41`_

<!-- mux-attribution: model=openai:gpt-5.4 thinking=high costs=5.41 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Please take another look.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 995e479609

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Prefer the packaged app bundle that matches the host architecture so the macOS attach_file smoke test does not false-fail on x64 runners.

---

_Generated with `mux` • Model: `openai:gpt-5.4` • Thinking: `high` • Cost: `$5.41`_

<!-- mux-attribution: model=openai:gpt-5.4 thinking=high costs=5.41 -->
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Please take another look.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33 ThomasK33 added this pull request to the merge queue Mar 29, 2026
Merged via the queue into main with commit b70d95e Mar 29, 2026
24 checks passed
@ThomasK33 ThomasK33 deleted the fix/macos-attach-file-sharp-runtime branch March 29, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant