mirror of
https://github.com/jorenn92/Maintainerr.git
synced 2026-07-16 16:47:04 +02:00
fix: keep server booting when sharp can't load on pre-x86-64-v2 CPUs (#3184)
Build Development / guard_manual_run (push) Has been cancelled
Release 1 - Prepare PR / guard_manual_run (push) Has been cancelled
Build Development / Build linux/arm64 (push) Has been cancelled
Build Development / Build linux/amd64 (push) Has been cancelled
Build Development / Merge Docker digests and push (push) Has been cancelled
Release 1 - Prepare PR / Guard release branch (push) Has been cancelled
Release 1 - Prepare PR / Create or update release PR (push) Has been cancelled
Build Development / guard_manual_run (push) Has been cancelled
Release 1 - Prepare PR / guard_manual_run (push) Has been cancelled
Build Development / Build linux/arm64 (push) Has been cancelled
Build Development / Build linux/amd64 (push) Has been cancelled
Build Development / Merge Docker digests and push (push) Has been cancelled
Release 1 - Prepare PR / Guard release branch (push) Has been cancelled
Release 1 - Prepare PR / Create or update release PR (push) Has been cancelled
sharp 0.35's prebuilt Linux x64 binaries require the x86-64-v2 microarchitecture (libvips 8.18). On older CPUs or minimal VM CPU models (e.g. QEMU kvm64) the native addon throws while loading at require() time, which crashed the whole server during Nest bootstrap — Maintainerr would not start after updating past v3.14.0. Load sharp through a small try/catch wrapper so the failure no longer takes down boot. The server starts; only the image features (overlays, collection posters) degrade, and a clear startup message explains the CPU requirement. Same sharp package — no dependency change.
This commit is contained in:
@@ -35,6 +35,8 @@ Docker images for amd64 & arm64 are available from:
|
||||
|
||||
> **Data directory.** Maintainerr stores its data at `/opt/data` inside the container. Mount a persistent volume there in your `docker run` command or Compose file. The directory must be readable and writable by the configured `user`; if no `user` is set, make it accessible to UID:GID `1000:1000`.
|
||||
|
||||
> **CPU requirement (x86-64).** Image features (overlays and collection posters) use `sharp`, whose prebuilt binaries require a CPU with the `x86-64-v2` microarchitecture. Maintainerr still starts on older CPUs, but those features are disabled. If you run in a VM and hit this, set the VM CPU type to `host`, `x86-64-v2`, or newer (e.g. Proxmox/QEMU's default `kvm64` does not expose `x86-64-v2`). This does not apply to arm64.
|
||||
|
||||
> If you set `BASE_PATH`, add it to the start of the paths too (e.g. `/maintainerr/api/health/ready`).
|
||||
|
||||
For more information, visit the [installation guide](https://docs.maintainerr.info/installation).
|
||||
|
||||
@@ -9,6 +9,7 @@ import { AppModule } from './app/app.module';
|
||||
import { dataDir } from './app/config/dataDir';
|
||||
import { MaintainerrLogger } from './modules/logging/logs.service';
|
||||
import { installStdioPipeGuards } from './modules/logging/winston/stdioPipeGuard';
|
||||
import { isSharpAvailable, SHARP_UNAVAILABLE_MESSAGE } from './utils/sharp';
|
||||
|
||||
// Pre-bootstrap guard so the console.warn/console.error calls below — and any
|
||||
// other write before LogsModule loads — cannot crash the process on a broken
|
||||
@@ -49,6 +50,12 @@ async function bootstrap() {
|
||||
app.useLogger(await app.resolve(MaintainerrLogger));
|
||||
app.enableCors({ origin: true });
|
||||
|
||||
if (!isSharpAvailable) {
|
||||
const sharpLogger = await app.resolve(MaintainerrLogger);
|
||||
sharpLogger.setContext('Sharp');
|
||||
sharpLogger.warn(SHARP_UNAVAILABLE_MESSAGE);
|
||||
}
|
||||
|
||||
const apiPort = process.env.UI_PORT || 6246;
|
||||
const apiHostname = process.env.UI_HOSTNAME || '0.0.0.0';
|
||||
await app.listen(apiPort, apiHostname);
|
||||
|
||||
@@ -2,8 +2,8 @@ import { MediaServerFeature } from '@maintainerr/contracts';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { dataDir as configDataDir } from '../../app/config/dataDir';
|
||||
import { sharp } from '../../utils/sharp';
|
||||
import { MediaServerFactory } from '../api/media-server/media-server.factory';
|
||||
import { IMediaServerService } from '../api/media-server/media-server.interface';
|
||||
import { MaintainerrLogger } from '../logging/logs.service';
|
||||
|
||||
@@ -16,8 +16,8 @@ import { format as dateFnsFormat, type Locale } from 'date-fns';
|
||||
import * as dateFnsLocales from 'date-fns/locale';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { dataDir as configDataDir } from '../../app/config/dataDir';
|
||||
import { sharp } from '../../utils/sharp';
|
||||
import { MaintainerrLogger } from '../logging/logs.service';
|
||||
|
||||
export interface TemplateRenderContext {
|
||||
|
||||
@@ -40,7 +40,7 @@ import { Response } from 'express';
|
||||
import * as fs from 'fs';
|
||||
import { ZodValidationPipe } from 'nestjs-zod';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { sharp } from '../../utils/sharp';
|
||||
import {
|
||||
type OverlayProcessRequest,
|
||||
overlayProcessRequestSchema,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Optional loader for the native `sharp` image library.
|
||||
*
|
||||
* sharp ships prebuilt native binaries. Since libvips 8.18 (sharp 0.35) the
|
||||
* prebuilt Linux x64 binary requires a CPU with the x86-64-v2 microarchitecture
|
||||
* (SSE4.2, POPCNT, …). On older CPUs — or minimal VM CPU models such as QEMU
|
||||
* `kvm64`, which do not expose those features to the guest — sharp throws
|
||||
* "Unsupported CPU" while loading its addon at `require()` time.
|
||||
*
|
||||
* Both consumers (overlay rendering and collection posters) import sharp at
|
||||
* module top level, so an uncaught load failure would crash the whole server
|
||||
* during Nest bootstrap. We require it inside a try/catch instead: the server
|
||||
* keeps booting and the guarded callers degrade with {@link
|
||||
* SHARP_UNAVAILABLE_MESSAGE} rather than crash-looping. Only the image features
|
||||
* are affected.
|
||||
*/
|
||||
type SharpFactory = typeof import('sharp');
|
||||
|
||||
let loaded: SharpFactory | null;
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
loaded = require('sharp') as SharpFactory;
|
||||
} catch {
|
||||
loaded = null;
|
||||
}
|
||||
|
||||
/** The sharp factory, or `null` when the native module could not be loaded. */
|
||||
export const sharp = loaded;
|
||||
|
||||
/** True when the native sharp module loaded successfully. */
|
||||
export const isSharpAvailable = loaded !== null;
|
||||
|
||||
/** Operator-facing explanation + remediation, shared by every guard. */
|
||||
export const SHARP_UNAVAILABLE_MESSAGE =
|
||||
'Image processing is unavailable: the native "sharp" module could not be ' +
|
||||
'loaded. Its prebuilt Linux x64 binaries require a CPU with the x86-64-v2 ' +
|
||||
'microarchitecture. If you run Maintainerr in a VM, set the CPU type to ' +
|
||||
'"host", "x86-64-v2", or newer. Overlay rendering and collection poster ' +
|
||||
'generation stay disabled until this is resolved.';
|
||||
Reference in New Issue
Block a user