fix: support image processing on CPUs without x86-64-v2
Some checks failed
Test and deploy / Verify production build (push) Failing after 15m8s

This commit is contained in:
2026-09-15 22:42:06 +03:30
parent 4ede4975ce
commit bc49f0ed31
5 changed files with 39 additions and 3 deletions

View File

@@ -57,6 +57,11 @@ Drizzle Kit, npm installs, or a compiler at runtime. Startup applies the committ
SQL migrations and seeds samples only when the database is empty. `/api/health` SQL migrations and seeds samples only when the database is empty. `/api/health`
checks database readiness without rendering the portfolio. checks database readiness without rendering the portfolio.
Sharp uses native image processing where supported and includes a WebAssembly
fallback for servers without the required x86-64 CPU features. The fallback
preserves uploads and image optimization, but can use more CPU time. Keep
`@img/sharp-wasm32` at the same version as `sharp` when upgrading.
Use one application instance with a local filesystem. Do not mount the SQLite Use one application instance with a local filesystem. Do not mount the SQLite
database over NFS/SMB, use an ephemeral serverless disk, or run multiple replicas database over NFS/SMB, use an ephemeral serverless disk, or run multiple replicas
against the same data volume. PostgreSQL would be the next step if those become against the same data volume. PostgreSQL would be the next step if those become

View File

@@ -3,7 +3,12 @@ const nextConfig = {
output: 'standalone', output: 'standalone',
serverExternalPackages: ['better-sqlite3'], serverExternalPackages: ['better-sqlite3'],
outputFileTracingIncludes: { outputFileTracingIncludes: {
'/*': ['./migrations/**/*'], '/*': [
'./migrations/**/*',
'./node_modules/@img/sharp-wasm32/**/*',
'./node_modules/@emnapi/runtime/**/*',
'./node_modules/tslib/**/*',
],
}, },
outputFileTracingExcludes: { outputFileTracingExcludes: {
'*': ['./data/**/*', './backups/**/*', './**/auth-secret', './**/*.sqlite*', './.env*'], '*': ['./data/**/*', './backups/**/*', './**/auth-secret', './**/*.sqlite*', './.env*'],

3
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "portfolio-v2-antigravity", "name": "portfolio-v2-antigravity",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@img/sharp-wasm32": "0.35.4",
"better-auth": "^1.7.5", "better-auth": "^1.7.5",
"better-sqlite3": "^13.0.3", "better-sqlite3": "^13.0.3",
"drizzle-orm": "^0.45.2", "drizzle-orm": "^0.45.2",
@@ -456,7 +457,6 @@
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@emnapi/runtime/-/runtime-1.11.3.tgz", "resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@emnapi/runtime/-/runtime-1.11.3.tgz",
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
@@ -1610,7 +1610,6 @@
"resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", "resolved": "https://package-mirror.liara.ir/artifactory/api/npm/npm/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
"integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": { "dependencies": {
"@emnapi/runtime": "^1.11.3" "@emnapi/runtime": "^1.11.3"
}, },

View File

@@ -16,6 +16,7 @@
"maintenance": "tsx --env-file-if-exists=.env scripts/maintenance.ts" "maintenance": "tsx --env-file-if-exists=.env scripts/maintenance.ts"
}, },
"dependencies": { "dependencies": {
"@img/sharp-wasm32": "0.35.4",
"better-auth": "^1.7.5", "better-auth": "^1.7.5",
"better-sqlite3": "^13.0.3", "better-sqlite3": "^13.0.3",
"drizzle-orm": "^0.45.2", "drizzle-orm": "^0.45.2",

View File

@@ -0,0 +1,26 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import test from 'node:test';
test('image processing falls back to WebAssembly without native CPU support', () => {
// Isolate the native-binding simulation from the rest of the test process.
const result = spawnSync(process.execPath, ['-e', `
const Module = require('node:module');
const original = Module._load;
Module._load = function (id, ...args) {
if (id.endsWith('.node') && !id.includes('wasm32')) {
const error = new Error('Simulated unavailable native binding');
error.code = 'MODULE_NOT_FOUND';
throw error;
}
return original.call(this, id, ...args);
};
const sharp = require('sharp');
(async () => {
const image = await sharp({ create: { width: 8, height: 8, channels: 3, background: '#336699' } }).webp().toBuffer();
const metadata = await sharp(image).metadata();
if (metadata.format !== 'webp' || metadata.width !== 8 || metadata.height !== 8) throw new Error('Image round trip failed');
})().catch(error => { console.error(error); process.exitCode = 1; });
`], { encoding: 'utf8', timeout: 30000 });
assert.equal(result.status, 0, result.stderr || result.error?.message);
});