mirror of
https://github.com/chrisbenincasa/tunarr.git
synced 2026-04-18 09:03:35 -04:00
fix: bundle and start meilisearch properly on macOS
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Tunarr.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -6,31 +6,64 @@
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let bundle = Bundle.main
|
||||
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "Tunarr Subprocess")
|
||||
var task = Process()
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
guard let executablePath = Bundle.main.url(forAuxiliaryExecutable: "tunarr-macos")
|
||||
else {
|
||||
/// TODO: Do a popup here.
|
||||
NSLog("Error: Bundled executable 'tunarr-macos' not found.")
|
||||
logger.error("Error: Bundled executable 'tunarr-macos' not found.")
|
||||
return
|
||||
}
|
||||
|
||||
guard let meilisearchPath = Bundle.main.url(forAuxiliaryExecutable: "meilisearch")
|
||||
else {
|
||||
logger.error("Error: Bundled executable 'meilisearch' not found")
|
||||
return
|
||||
}
|
||||
|
||||
task.executableURL = executablePath
|
||||
var env = ProcessInfo.processInfo.environment
|
||||
env["TUNARR_MEILISEARCH_PATH"] = meilisearchPath.absoluteURL.path()
|
||||
task.currentDirectoryURL = FileManager.default
|
||||
.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(
|
||||
"Library"
|
||||
).appendingPathComponent("Preferences")
|
||||
.appendingPathComponent("tunarr")
|
||||
task.environment = env
|
||||
logger.info("\(env, privacy: .public)")
|
||||
|
||||
let errorPipe = Pipe()
|
||||
task.standardError = errorPipe
|
||||
|
||||
do {
|
||||
errorPipe.fileHandleForReading.readabilityHandler = { pipe in
|
||||
let data = pipe.availableData
|
||||
if let line = String(data: data, encoding: .utf8) {
|
||||
if line.count > 0 {
|
||||
self.logger.info(
|
||||
"Process stderr: \(line.trimmingCharacters(in: .whitespacesAndNewlines), privacy: .public)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
try task.run()
|
||||
NSLog("Successfully started Tunarr subprocess.")
|
||||
|
||||
logger.info("Successfully started Tunarr subprocess.")
|
||||
} catch {
|
||||
NSLog("Could not launch Tunarr. Reason: \(error)")
|
||||
logger.error("Could not launch Tunarr. Reason: \(error, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
NSLog("Shutting down Tunarr.")
|
||||
logger.info("Shutting down Tunarr.")
|
||||
task.terminate()
|
||||
task.waitUntilExit()
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@ cp -R "$REPO_ROOT/macos/Tunarr/build/Release/Tunarr.app" "$APP_NAME"
|
||||
popd || exit
|
||||
|
||||
cp -a "$REPO_ROOT/server/bin/$BINARY_NAME" "$APP_NAME/Contents/MacOS/tunarr-macos"
|
||||
|
||||
chmod +x "$APP_NAME/Contents/MacOS/tunarr-macos"
|
||||
cp -a "$REPO_ROOT/server/bin/meilisearch-macos-$2" "$APP_NAME/Contents/MacOS/meilisearch"
|
||||
chmod +x "$APP_NAME/Contents/MacOS/tunarr-macos" "$APP_NAME/Contents/MacOS/meilisearch"
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getNumericEnvVar,
|
||||
TUNARR_ENV_VARS,
|
||||
} from '../util/env.ts';
|
||||
import { LoggerFactory } from '../util/logging/LoggerFactory.ts';
|
||||
import type { GlobalArgsType } from './types.ts';
|
||||
|
||||
export type ServerArgsType = GlobalArgsType & {
|
||||
@@ -57,6 +58,16 @@ export const RunServerCommand: CommandModule<GlobalArgsType, ServerArgsType> = {
|
||||
// port precedence - env var -> argument -> settings
|
||||
setServerOptions({ ...opts, port: portToUse });
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
LoggerFactory.root.error(err, 'Uncaught exception');
|
||||
LoggerFactory.root.flush();
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (err) => {
|
||||
LoggerFactory.root.error(err, 'Uncaught exception');
|
||||
LoggerFactory.root.flush();
|
||||
});
|
||||
|
||||
// Hard fail without database connection.
|
||||
assert(!!container.get<DBAccess>(DBAccess).db);
|
||||
await container.get(App).start();
|
||||
|
||||
@@ -372,6 +372,8 @@ export class MeilisearchService implements ISearchService {
|
||||
'--db-path',
|
||||
`${this.dbPath}`,
|
||||
'--no-analytics',
|
||||
'--log-level',
|
||||
'trace',
|
||||
];
|
||||
|
||||
const indexingRamSetting =
|
||||
@@ -398,7 +400,10 @@ export class MeilisearchService implements ISearchService {
|
||||
|
||||
if (
|
||||
!isWindows() &&
|
||||
getBooleanEnvVar(TUNARR_ENV_VARS.DEBUG__REDUCE_SEARCH_INDEXING_MEMORY)
|
||||
getBooleanEnvVar(
|
||||
TUNARR_ENV_VARS.DEBUG__REDUCE_SEARCH_INDEXING_MEMORY,
|
||||
os.platform() === 'linux',
|
||||
)
|
||||
) {
|
||||
args.push('--experimental-reduce-indexing-memory-usage');
|
||||
}
|
||||
@@ -439,6 +444,9 @@ export class MeilisearchService implements ISearchService {
|
||||
|
||||
this.proc = await this.childProcessHelper.spawn(executablePath, args, {
|
||||
maxAttempts: 3,
|
||||
additionalOpts: {
|
||||
cwd: this.serverOptions.databaseDirectory,
|
||||
},
|
||||
});
|
||||
this.logger.info('Meilisearch service started on port %d', this.port);
|
||||
const outStream = createWriteStream(searchServerLogFile);
|
||||
|
||||
@@ -3,7 +3,11 @@ import { isNonEmptyString } from '@/util/index.js';
|
||||
import { sanitizeForExec } from '@/util/strings.js';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import type { ChildProcessByStdio, ExecOptions } from 'node:child_process';
|
||||
import type {
|
||||
ChildProcessByStdio,
|
||||
ExecOptions,
|
||||
SpawnOptions,
|
||||
} from 'node:child_process';
|
||||
import { execFile, spawn } from 'node:child_process';
|
||||
import events from 'node:events';
|
||||
import { Readable } from 'node:stream';
|
||||
@@ -18,6 +22,7 @@ type SpawnOpts = {
|
||||
name: string;
|
||||
restartOnFailure: boolean;
|
||||
maxAttempts: number;
|
||||
additionalOpts?: SpawnOptions;
|
||||
};
|
||||
|
||||
type ChildProcessEvents = {
|
||||
@@ -72,6 +77,7 @@ export class ChildProcessWrapper extends ITypedEventEmitter {
|
||||
|
||||
this.logger.debug('Starting process: %s %O', this.path, this.args);
|
||||
const proc = spawn(this.path, this.args, {
|
||||
...(this.opts.additionalOpts ?? {}),
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
signal: this.controller.signal,
|
||||
env: this.env,
|
||||
@@ -102,6 +108,7 @@ export class ChildProcessWrapper extends ITypedEventEmitter {
|
||||
|
||||
if (!this.wasAborted && code !== 0) {
|
||||
const bufferedBytes = bufferedOut.getLastN().toString('utf-8');
|
||||
this.logger.error(bufferedBytes);
|
||||
console.error(bufferedBytes);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user