feat: implement macOS notarization and ad-hoc signing in afterSign script
feat: add run-server script to manage server execution and compatibility checks feat: create main application logic for desktop with window management, server handling, and auto-updater test: add unit tests for desktop runtime helpers feat: implement desktop server environment configuration and health check test: add tests for server configuration and desktop routes feat: add native module guard for better-sqlite3 compatibility test: add tests for native module guard functionality test: add comprehensive tests for account verification feedback handling feat: implement account verification feedback helpers chore: add TypeScript configuration for desktop builds
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { notarize } from '@electron/notarize';
|
||||
import { execFile } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
async function hasSignature(appPath) {
|
||||
try {
|
||||
await execFileAsync('codesign', ['--verify', '--deep', '--strict', appPath]);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function adHocSign(appPath) {
|
||||
await execFileAsync('codesign', ['--force', '--deep', '--sign', '-', appPath]);
|
||||
}
|
||||
|
||||
export default async function afterSign(context) {
|
||||
if (context.electronPlatformName !== 'darwin') return;
|
||||
|
||||
const appPath = `${context.appOutDir}/${context.packager.appInfo.productFilename}.app`;
|
||||
const appleId = process.env.APPLE_ID || process.env.APPLEID;
|
||||
const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD || process.env.APPLEIDPASS;
|
||||
const teamId = process.env.APPLE_TEAM_ID;
|
||||
|
||||
const signed = await hasSignature(appPath);
|
||||
if (!signed) {
|
||||
console.log('[metapi-desktop] No macOS signing identity detected, applying ad-hoc signature.');
|
||||
await adHocSign(appPath);
|
||||
}
|
||||
|
||||
if (!appleId || !appleIdPassword || !teamId) {
|
||||
console.log('[metapi-desktop] Skipping notarization: APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID not fully configured.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[metapi-desktop] Notarizing macOS build...');
|
||||
await notarize({
|
||||
appPath,
|
||||
appleId,
|
||||
appleIdPassword,
|
||||
teamId,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
import {
|
||||
buildBetterSqliteRebuildCommand,
|
||||
isNodeModuleVersionMismatch,
|
||||
} from '../../src/server/nativeModuleGuard.js';
|
||||
|
||||
function run(command: string, args: string[]) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const child = spawn(command, args, {
|
||||
cwd: process.cwd(),
|
||||
stdio: 'inherit',
|
||||
shell: false,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
child.once('exit', (code, signal) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(new Error(`Command failed: ${command} ${args.join(' ')} (code=${code ?? 'null'}, signal=${signal ?? 'null'})`));
|
||||
});
|
||||
|
||||
child.once('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureBetterSqliteCompatible() {
|
||||
try {
|
||||
await import('better-sqlite3');
|
||||
return;
|
||||
} catch (error) {
|
||||
if (!isNodeModuleVersionMismatch(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rebuild = buildBetterSqliteRebuildCommand();
|
||||
console.log('[metapi] Detected better-sqlite3 ABI mismatch. Rebuilding for the current Node.js runtime...');
|
||||
await run(rebuild.command, rebuild.args);
|
||||
|
||||
try {
|
||||
await import('better-sqlite3');
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`better-sqlite3 is still incompatible after rebuild: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const isWatchMode = process.argv.includes('--watch');
|
||||
await ensureBetterSqliteCompatible();
|
||||
|
||||
if (isWatchMode) {
|
||||
const tsxCli = join(process.cwd(), 'node_modules', 'tsx', 'dist', 'cli.mjs');
|
||||
const child = spawn(process.execPath, [tsxCli, 'watch', 'src/server/index.ts'], {
|
||||
cwd: process.cwd(),
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
});
|
||||
child.once('exit', (code) => {
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const child = spawn(process.execPath, ['dist/server/index.js'], {
|
||||
cwd: process.cwd(),
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
});
|
||||
child.once('exit', (code) => {
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
rem Check whether better-sqlite3 matches the current Node.js ABI.
|
||||
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){if(err&&err.code==='ERR_DLOPEN_FAILED'&&String(err.message||'').includes('NODE_MODULE_VERSION')){process.exit(42);}console.error(err);process.exit(1);}"
|
||||
set "NATIVE_CHECK_CODE=%errorlevel%"
|
||||
|
||||
if %NATIVE_CHECK_CODE% EQU 42 (
|
||||
echo [metapi] Detected Node.js/native module ABI mismatch. Rebuilding better-sqlite3...
|
||||
call npm rebuild better-sqlite3 --no-audit --fund=false
|
||||
if errorlevel 1 (
|
||||
echo [metapi] npm rebuild failed. Running npm ci --omit=dev as fallback...
|
||||
call npm ci --omit=dev --no-audit --fund=false
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
)
|
||||
|
||||
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){console.error(err);process.exit(1);}"
|
||||
if errorlevel 1 (
|
||||
echo [metapi] better-sqlite3 is still incompatible with current Node.js.
|
||||
echo [metapi] Recommended: use Node.js 22 LTS or run Docker image 1467078763/metapi:latest.
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
) else if NOT %NATIVE_CHECK_CODE% EQU 0 (
|
||||
exit /b %NATIVE_CHECK_CODE%
|
||||
)
|
||||
|
||||
node dist/server/db/migrate.js
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
|
||||
node dist/server/index.js
|
||||
exit /b %errorlevel%
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
# Check whether better-sqlite3 matches the current Node.js ABI.
|
||||
set +e
|
||||
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){if(err&&err.code==='ERR_DLOPEN_FAILED'&&String(err.message||'').includes('NODE_MODULE_VERSION')){process.exit(42);}console.error(err);process.exit(1);}"
|
||||
native_check_code=$?
|
||||
set -e
|
||||
|
||||
if [ "$native_check_code" -eq 42 ]; then
|
||||
echo "[metapi] Detected Node.js/native module ABI mismatch. Rebuilding better-sqlite3..."
|
||||
if ! npm rebuild better-sqlite3 --no-audit --fund=false; then
|
||||
echo "[metapi] npm rebuild failed. Running npm ci --omit=dev as fallback..."
|
||||
npm ci --omit=dev --no-audit --fund=false
|
||||
fi
|
||||
|
||||
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){console.error(err);process.exit(1);}"
|
||||
elif [ "$native_check_code" -ne 0 ]; then
|
||||
exit "$native_check_code"
|
||||
fi
|
||||
|
||||
node dist/server/db/migrate.js
|
||||
exec node dist/server/index.js
|
||||
Reference in New Issue
Block a user