Last active
September 6, 2024 04:31
-
-
Save wispborne/ee751a8626311481796353ade796a454 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example Runner | |
try { | |
await replaceSelf(directoryWithNewVersionFiles); | |
} catch (error) { | |
Fimber.w('Error self-updating something. YOLOing.', ex: error); | |
} | |
if (currentPlatform == TargetPlatform.windows) { | |
await Process.start( | |
'cmd', | |
['/c', "start", "", Platform.resolvedExecutable], | |
runInShell: true, | |
mode: ProcessStartMode.detached, | |
); | |
} else if (currentPlatform == TargetPlatform.linux) { | |
await Process.start( | |
'nohup', | |
[Platform.resolvedExecutable], | |
runInShell: true, | |
mode: ProcessStartMode.detached, | |
); | |
} else if (currentPlatform == TargetPlatform.macOS) { | |
// Doesn't work! | |
await Process.start( | |
'open', | |
['-n', currentMacOSAppPath.path], | |
runInShell: true, | |
mode: ProcessStartMode.detached, | |
); | |
} | |
if (exitSelfAfter) { | |
await Future.delayed(const Duration(milliseconds: 500)); | |
Fimber.i( | |
'Exiting old version of self, new should have already started.'); | |
exit(0); | |
} | |
/// Replaces all files in the current working directory with files that have the same relative path | |
/// in the given source directory. | |
Future<void> replaceSelf(Directory sourceDirectory) async { | |
final allNewFiles = | |
sourceDirectory.listSync(recursive: true, followLinks: true); | |
final currentDir = currentPlatform != TargetPlatform.macOS | |
? currentDirectory | |
: currentMacOSAppPath; | |
final jobs = <Future<void>>[]; | |
for (final newFile in allNewFiles) { | |
if (newFile.isFile()) { | |
final newFileRelative = | |
newFile.toFile().relativeTo(sourceDirectory).toFile(); | |
final fileToReplace = | |
File(p.join(currentDir.path, newFileRelative.path)); | |
jobs.add(updateLockedFileInPlace( | |
newFile.toFile(), fileToReplace, oldFileSuffix)); | |
} | |
} | |
await Future.wait(jobs); | |
} | |
/// Updates or replaces a locked file in place. | |
/// | |
/// Depending on the destination file's existence and type: | |
/// - If the file doesn't exist, it copies the source file to the destination. | |
/// - If it's a `.so` file, it replaces the destination's contents with the source's contents. | |
/// - Otherwise, it renames the destination file by appending the given suffix and then copies the source file to the destination. | |
/// | |
/// Parameters: | |
/// - [sourceFile]: The file to copy or use for content replacement. | |
/// - [destFile]: The target file to update or replace. | |
/// - [oldFileSuffix]: Suffix for renaming the existing file. | |
/// | |
/// Returns: | |
/// - A [Future] that completes when the operation is done. | |
Future<void> updateLockedFileInPlace( | |
File sourceFile, | |
File destFile, | |
String oldFileSuffix, | |
) async { | |
final sourceExt = sourceFile.extension; | |
final doesDestExist = destFile.existsSync(); | |
// Create parent directories if they don't exist. | |
if (!doesDestExist && !destFile.parent.existsSync()) { | |
destFile.parent.createSync(recursive: true); | |
} | |
if (!doesDestExist) { | |
// Nothing to replace, just copy the file. | |
Fimber.d("Copying new file: ${sourceFile.path} to ${destFile.path}"); | |
await sourceFile.copy(destFile.path); | |
} else if (currentPlatform == TargetPlatform.windows && | |
sourceExt == ".so") { | |
// Can't rename .so files on Windows, but we can replace their content. | |
Fimber.d( | |
"Replacing contents of .so file: ${destFile.path} with that of ${sourceFile.path}"); | |
await destFile.writeAsBytes(await sourceFile.readAsBytes()); | |
} else { | |
// Can't replace content of other locked files, but can rename them. | |
// Can always rename on Linux. | |
var oldFile = File(destFile.path + oldFileSuffix); | |
Fimber.d("Renaming locked file: ${destFile.path} to ${oldFile.path}, " | |
"and copying new file: ${sourceFile.path} to ${destFile.path}"); | |
if (oldFile.existsSync()) { | |
if (await oldFile.isWritable()) { | |
Fimber.d("Old file already exists, deleting: ${oldFile.path}"); | |
oldFile.deleteSync(); | |
} | |
} | |
await destFile.rename(oldFile.path); | |
await sourceFile.copy(destFile.path); | |
} | |
} | |
static Future<void> cleanUpOldUpdateFiles() async { | |
final filesInCurrentDir = currentDirectory | |
.listSync(recursive: true) | |
.where((element) => element.path.endsWith(oldFileSuffix)) | |
.toList(); | |
for (final file in filesInCurrentDir) { | |
if (file is File) { | |
try { | |
await file.delete(); | |
} catch (error) { | |
Fimber.w('Error deleting old file: ${file.path}', ex: error); | |
} | |
} | |
} | |
Fimber.i('Cleaned up ${filesInCurrentDir.length} old update files.'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment