fix(docker): ship libvips so sharp can actually load at runtime (#6499)

* fix(docker): ship libvips so sharp can actually load at runtime

* style(config): use TSDoc for the tracing-includes note
This commit is contained in:
Waleed
2026-08-10 12:21:24 -07:00
committed by GitHub
parent 56910c002e
commit c9f73542c1
4 changed files with 18 additions and 20 deletions
@@ -6,15 +6,7 @@ import os from 'os'
import path from 'path'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
/**
* `sharp` resolves a platform-specific `@img/sharp-*` native binary that the
* standalone file tracer cannot follow, so a deployment can ship without it. It
* must therefore be loaded lazily and its failure contained: an unreadable OG
* dimension is optional metadata, not a reason to take down `/blog`, `/library`,
* and every tag, author, slug, and RSS route that reads the registry.
*
* This mock makes `import('sharp')` fail the way a missing native binary does.
*/
/** Fails `import('sharp')` the way an unloadable native binary does. */
vi.mock('sharp', () => {
throw new Error('Could not load the sharp module using the linux-x64 runtime')
})
+3 -6
View File
@@ -103,12 +103,9 @@ export function createContentRegistry(config: ContentRegistryConfig): ContentReg
* `image-size` package, archived upstream with unpatched DoS advisories in
* its ICNS/JXL/HEIF parsers (GHSA-w3rx-r6r6-pgpr, GHSA-5p2g-fcmc-qvqq).
*
* `sharp` is loaded lazily, never as a top-level import. It resolves a
* platform-specific `@img/sharp-*` native binary that the standalone file
* tracer cannot follow, so a deployment that ships without it makes
* `import 'sharp'` throw at module scope — which would take down every route
* that touches this registry (`/blog`, `/library`, their tag, author, slug,
* and RSS routes) rather than degrading one optional OG dimension.
* Imported lazily, never at module scope: sharp resolves a native binary, and a
* top-level import that fails to load would take down every route reading this
* registry instead of dropping one optional dimension.
*/
async function readOgImageDimensions(
ogImage: string
+5 -5
View File
@@ -168,11 +168,11 @@ const nextConfig: NextConfig = {
'/api/internal/file-doc/seed': ['./node_modules/jsdom/**/*'],
'/api/internal/file-doc/merge': ['./node_modules/jsdom/**/*'],
'/api/internal/file-doc/persist': ['./node_modules/jsdom/**/*'],
'/*': [
'./node_modules/sharp/**/*',
'./node_modules/@img/**/*',
'./lib/execution/sandbox/bundles/*.cjs',
],
/**
* No `sharp`/`@img` entries: these globs resolve against apps/sim while both hoist to the
* monorepo root, so they matched nothing. docker/app.Dockerfile copies them instead.
*/
'/*': ['./lib/execution/sandbox/bundles/*.cjs'],
},
experimental: {
/**
+9
View File
@@ -130,6 +130,15 @@ COPY --from=deps --chown=nextjs:nodejs /app/node_modules/lib0 ./node_modules/lib
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/yjs ./node_modules/yjs
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/y-protocols ./node_modules/y-protocols
# `@img/sharp-<platform>` loads libvips from `@img/sharp-libvips-<platform>` through the dynamic
# linker, not a JS require, so the tracer copies the binding but not the library and sharp dies with
# "ERR_DLOPEN_FAILED: libvips-cpp.so: cannot open shared object file". Same hoisting reason as the Yjs
# stack above. Copying whole directories keeps these arch-agnostic (each build's deps stage holds only
# its own platform's packages) and keeps sharp and its binding on the same install. Must stay below
# the standalone COPY, which ships its own partial node_modules that would otherwise win.
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/sharp ./node_modules/sharp
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/@img ./node_modules/@img
# Copy the isolated-vm worker script
COPY --from=builder --chown=nextjs:nodejs /app/apps/sim/lib/execution/isolated-vm-worker.cjs ./apps/sim/lib/execution/isolated-vm-worker.cjs