@@ -390,7 +390,7 @@ class UpdateCheckerService {
390390 return result.isGranted;
391391 }
392392
393- // Download update file with progress tracking
393+ // Download update file with progress tracking and move to install dir (Linux/Windows)
394394 Future <String ?> downloadUpdate ({
395395 required ReleaseInfo release,
396396 required Function (double progress, int downloaded, int total) onProgress,
@@ -407,23 +407,23 @@ class UpdateCheckerService {
407407 final dio = Dio ();
408408 final downloadsDir = await _getDownloadsDirectory ();
409409 final fileExtension = _getFileExtension ();
410- final fileName = "openlib_${ release . version } .$fileExtension " ;
411- final filePath = "${downloadsDir .path }/$fileName " ;
410+ final tempFileName = "openlib_update_temp .$fileExtension " ;
411+ final tempFilePath = "${downloadsDir .path }/$tempFileName " ;
412412
413413 _logger.info ("Starting update download" , tag: "UpdateChecker" , metadata: {
414414 "url" : downloadUrl,
415- "destination" : filePath ,
415+ "destination" : tempFilePath ,
416416 });
417417
418- // Delete existing file if present
419- final existingFile = File (filePath );
418+ // Delete existing temp file if present
419+ final existingFile = File (tempFilePath );
420420 if (await existingFile.exists ()) {
421421 await existingFile.delete ();
422422 }
423423
424424 await dio.download (
425425 downloadUrl,
426- filePath ,
426+ tempFilePath ,
427427 cancelToken: cancelToken,
428428 options: Options (
429429 headers: {
@@ -440,24 +440,86 @@ class UpdateCheckerService {
440440
441441 dio.close ();
442442
443+ // Determine install directory and target filename
444+ String installDirPath;
445+ String targetFileName;
446+ if (Platform .isWindows) {
447+ // Use the directory of the running executable
448+ installDirPath = File (Platform .resolvedExecutable).parent.path;
449+ targetFileName = "openlib.exe" ;
450+ } else if (Platform .isLinux) {
451+ installDirPath = File (Platform .resolvedExecutable).parent.path;
452+ targetFileName = "openlib.AppImage" ;
453+ } else {
454+ // For other platforms, just return the temp file path
455+ if (Platform .isMacOS || Platform .isIOS || Platform .isAndroid) {
456+ // No special move needed
457+ if (Platform .isLinux) {
458+ await Process .run ("chmod" , ["+x" , tempFilePath]);
459+ }
460+ return tempFilePath;
461+ }
462+ return tempFilePath;
463+ }
464+
465+ final targetFilePath = "$installDirPath /$targetFileName " ;
466+
467+ // Remove old versioned executables in the install dir
468+ final dir = Directory (installDirPath);
469+ final files = dir.listSync ();
470+ for (final f in files) {
471+ if (f is File ) {
472+ final name = f.path.split (Platform .pathSeparator).last;
473+ if ((Platform .isWindows &&
474+ name.startsWith ("openlib_" ) &&
475+ name.endsWith (".exe" )) ||
476+ (Platform .isLinux &&
477+ name.startsWith ("openlib_" ) &&
478+ name.endsWith (".AppImage" ))) {
479+ try {
480+ await f.delete ();
481+ } catch (_) {}
482+ }
483+ }
484+ }
485+
486+ // Move the downloaded file to the install directory, overwrite if exists
487+ final tempFile = File (tempFilePath);
488+ final targetFile = File (targetFilePath);
489+ if (await targetFile.exists ()) {
490+ await targetFile.delete ();
491+ }
492+ try {
493+ await tempFile.rename (targetFilePath);
494+ } on FileSystemException catch (e) {
495+ // errno=18 is EXDEV (cross-device link)
496+ if (e.osError != null && e.osError! .errorCode == 18 ) {
497+ // Fallback: copy then delete
498+ await tempFile.copy (targetFilePath);
499+ await tempFile.delete ();
500+ } else {
501+ rethrow ;
502+ }
503+ }
504+
443505 // Make file executable on Linux
444506 if (Platform .isLinux) {
445- await Process .run ("chmod" , ["+x" , filePath ]);
507+ await Process .run ("chmod" , ["+x" , targetFilePath ]);
446508 }
447509
448- _logger.info ("Update downloaded successfully " ,
510+ _logger.info ("Update moved to install directory " ,
449511 tag: "UpdateChecker" ,
450512 metadata: {
451- "path" : filePath ,
513+ "path" : targetFilePath ,
452514 });
453515
454- return filePath ;
516+ return targetFilePath ;
455517 } catch (e, stackTrace) {
456518 if (e is DioException && e.type == DioExceptionType .cancel) {
457519 _logger.info ("Update download cancelled" , tag: "UpdateChecker" );
458520 return null ;
459521 }
460- _logger.error ("Failed to download update" ,
522+ _logger.error ("Failed to download/move update" ,
461523 tag: "UpdateChecker" , error: e, stackTrace: stackTrace);
462524 rethrow ;
463525 }
@@ -501,11 +563,14 @@ class UpdateCheckerService {
501563 if (! context.mounted) return false ;
502564 return await _installApkWithFallbacks (filePath, context,
503565 release: release);
504- } else if (Platform .isWindows) {
505- await Process .start (filePath, [], mode: ProcessStartMode .detached);
506- return true ;
507- } else if (Platform .isLinux) {
508- await Process .start (filePath, [], mode: ProcessStartMode .detached);
566+ } else if (Platform .isWindows || Platform .isLinux) {
567+ // Always use the non-versioned filename in the install dir
568+ String installDirPath = File (Platform .resolvedExecutable).parent.path;
569+ String targetFileName =
570+ Platform .isWindows ? "openlib.exe" : "openlib.AppImage" ;
571+ String targetFilePath = "$installDirPath /$targetFileName " ;
572+ // Launch the new executable
573+ await Process .start (targetFilePath, [], mode: ProcessStartMode .detached);
509574 return true ;
510575 } else if (Platform .isMacOS) {
511576 await OpenFile .open (filePath, type: "application/x-apple-diskimage" );
0 commit comments