fix: update macos (#12578)
* fix: update macos 1. Use `ditto` instead of `cp -r`. 2. Add prompt for extracting dmg. Signed-off-by: fufesou <linlong1266@gmail.com> * fix: error to err Signed-off-by: fufesou <linlong1266@gmail.com> * Refact: Remove "Extracting" Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,10 @@ import 'package:flutter_hbb/models/platform_model.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
final _isExtracting = false.obs;
|
||||||
|
|
||||||
void handleUpdate(String releasePageUrl) {
|
void handleUpdate(String releasePageUrl) {
|
||||||
|
_isExtracting.value = false;
|
||||||
String downloadUrl = releasePageUrl.replaceAll('tag', 'download');
|
String downloadUrl = releasePageUrl.replaceAll('tag', 'download');
|
||||||
String version = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1);
|
String version = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1);
|
||||||
final String downloadFile =
|
final String downloadFile =
|
||||||
@@ -25,13 +28,14 @@ void handleUpdate(String releasePageUrl) {
|
|||||||
gFFI.dialogManager.dismissAll();
|
gFFI.dialogManager.dismissAll();
|
||||||
gFFI.dialogManager.show((setState, close, context) {
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('Downloading {$appName}')),
|
title: Obx(() => Text(translate(
|
||||||
|
_isExtracting.isTrue ? 'Installing ...' : 'Downloading {$appName}'))),
|
||||||
content:
|
content:
|
||||||
UpdateProgress(releasePageUrl, downloadUrl, downloadId, onCanceled)
|
UpdateProgress(releasePageUrl, downloadUrl, downloadId, onCanceled)
|
||||||
.marginSymmetric(horizontal: 8)
|
.marginSymmetric(horizontal: 8)
|
||||||
.paddingOnly(top: 12),
|
.paddingOnly(top: 12),
|
||||||
actions: [
|
actions: [
|
||||||
dialogButton(translate('Cancel'), onPressed: () async {
|
if (_isExtracting.isFalse) dialogButton(translate('Cancel'), onPressed: () async {
|
||||||
onCanceled.value();
|
onCanceled.value();
|
||||||
await bind.mainSetCommon(
|
await bind.mainSetCommon(
|
||||||
key: 'cancel-downloader', value: downloadId.value);
|
key: 'cancel-downloader', value: downloadId.value);
|
||||||
@@ -71,6 +75,7 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
int _downloadedSize = 0;
|
int _downloadedSize = 0;
|
||||||
int _getDataFailedCount = 0;
|
int _getDataFailedCount = 0;
|
||||||
final String _eventKeyDownloadNewVersion = 'download-new-version';
|
final String _eventKeyDownloadNewVersion = 'download-new-version';
|
||||||
|
final String _eventKeyExtractUpdateDmg = 'extract-update-dmg';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -82,6 +87,11 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
_eventKeyDownloadNewVersion, handleDownloadNewVersion,
|
_eventKeyDownloadNewVersion, handleDownloadNewVersion,
|
||||||
replace: true);
|
replace: true);
|
||||||
bind.mainSetCommon(key: 'download-new-version', value: widget.downloadUrl);
|
bind.mainSetCommon(key: 'download-new-version', value: widget.downloadUrl);
|
||||||
|
if (isMacOS) {
|
||||||
|
platformFFI.registerEventHandler(_eventKeyExtractUpdateDmg,
|
||||||
|
_eventKeyExtractUpdateDmg, handleExtractUpdateDmg,
|
||||||
|
replace: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -89,6 +99,10 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
cancelQueryTimer();
|
cancelQueryTimer();
|
||||||
platformFFI.unregisterEventHandler(
|
platformFFI.unregisterEventHandler(
|
||||||
_eventKeyDownloadNewVersion, _eventKeyDownloadNewVersion);
|
_eventKeyDownloadNewVersion, _eventKeyDownloadNewVersion);
|
||||||
|
if (isMacOS) {
|
||||||
|
platformFFI.unregisterEventHandler(
|
||||||
|
_eventKeyExtractUpdateDmg, _eventKeyExtractUpdateDmg);
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +127,13 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onError(String error) {
|
// `isExtractDmg` is true when handling extract-update-dmg event.
|
||||||
|
// It's a rare case that the dmg file is corrupted and cannot be extracted.
|
||||||
|
void _onError(String error, {bool isExtractDmg = false}) {
|
||||||
cancelQueryTimer();
|
cancelQueryTimer();
|
||||||
|
|
||||||
debugPrint('Download new version error: $error');
|
debugPrint(
|
||||||
|
'${isExtractDmg ? "Extract" : "Download"} new version error: $error');
|
||||||
final msgBoxType = 'custom-nocancel-nook-hasclose';
|
final msgBoxType = 'custom-nocancel-nook-hasclose';
|
||||||
final msgBoxTitle = 'Error';
|
final msgBoxTitle = 'Error';
|
||||||
final msgBoxText = 'download-new-version-failed-tip';
|
final msgBoxText = 'download-new-version-failed-tip';
|
||||||
@@ -138,7 +155,7 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
|
|
||||||
final List<Widget> buttons = [
|
final List<Widget> buttons = [
|
||||||
dialogButton('Download', onPressed: jumplink),
|
dialogButton('Download', onPressed: jumplink),
|
||||||
dialogButton('Retry', onPressed: retry),
|
if (!isExtractDmg) dialogButton('Retry', onPressed: retry),
|
||||||
dialogButton('Close', onPressed: close),
|
dialogButton('Close', onPressed: close),
|
||||||
];
|
];
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
@@ -194,19 +211,13 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
_onError('The download file size is 0.');
|
_onError('The download file size is 0.');
|
||||||
} else {
|
} else {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
msgBox(
|
if (isMacOS) {
|
||||||
gFFI.sessionId,
|
bind.mainSetCommon(
|
||||||
'custom-nocancel',
|
key: 'extract-update-dmg', value: widget.downloadUrl);
|
||||||
'{$appName} Update',
|
_isExtracting.value = true;
|
||||||
'{$appName}-to-update-tip',
|
} else {
|
||||||
'',
|
updateMsgBox();
|
||||||
gFFI.dialogManager,
|
}
|
||||||
onSubmit: () {
|
|
||||||
debugPrint('Downloaded, update to new version now');
|
|
||||||
bind.mainSetCommon(key: 'update-me', value: widget.downloadUrl);
|
|
||||||
},
|
|
||||||
submitTimeout: 5,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@@ -214,17 +225,38 @@ class UpdateProgressState extends State<UpdateProgress> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void updateMsgBox() {
|
||||||
Widget build(BuildContext context) {
|
msgBox(
|
||||||
return onDownloading(context);
|
gFFI.sessionId,
|
||||||
|
'custom-nocancel',
|
||||||
|
'{$appName} Update',
|
||||||
|
'{$appName}-to-update-tip',
|
||||||
|
'',
|
||||||
|
gFFI.dialogManager,
|
||||||
|
onSubmit: () {
|
||||||
|
debugPrint('Downloaded, update to new version now');
|
||||||
|
bind.mainSetCommon(key: 'update-me', value: widget.downloadUrl);
|
||||||
|
},
|
||||||
|
submitTimeout: 5,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget onDownloading(BuildContext context) {
|
Future<void> handleExtractUpdateDmg(Map<String, dynamic> evt) async {
|
||||||
final value = _totalSize == null
|
_isExtracting.value = false;
|
||||||
|
if (evt.containsKey('err') && (evt['err'] as String).isNotEmpty) {
|
||||||
|
_onError(evt['err'] as String, isExtractDmg: true);
|
||||||
|
} else {
|
||||||
|
updateMsgBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
getValue() => _totalSize == null
|
||||||
? 0.0
|
? 0.0
|
||||||
: (_totalSize == 0 ? 1.0 : _downloadedSize / _totalSize!);
|
: (_totalSize == 0 ? 1.0 : _downloadedSize / _totalSize!);
|
||||||
return LinearProgressIndicator(
|
return LinearProgressIndicator(
|
||||||
value: value,
|
value: _isExtracting.isTrue ? null : getValue(),
|
||||||
minHeight: 20,
|
minHeight: 20,
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
backgroundColor: Colors.grey[300],
|
backgroundColor: Colors.grey[300],
|
||||||
|
|||||||
@@ -629,7 +629,10 @@ pub fn session_open_terminal(session_id: SessionID, terminal_id: i32, rows: u32,
|
|||||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
session.open_terminal(terminal_id, rows, cols);
|
session.open_terminal(terminal_id, rows, cols);
|
||||||
} else {
|
} else {
|
||||||
log::error!("[flutter_ffi] Session not found for session_id: {}", session_id);
|
log::error!(
|
||||||
|
"[flutter_ffi] Session not found for session_id: {}",
|
||||||
|
session_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2651,6 +2654,21 @@ pub fn main_set_common(_key: String, _value: String) {
|
|||||||
fs::remove_file(f).ok();
|
fs::remove_file(f).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if _key == "extract-update-dmg" {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
if let Some(new_version_file) = get_download_file_from_url(&_value) {
|
||||||
|
if let Some(f) = new_version_file.to_str() {
|
||||||
|
crate::platform::macos::extract_update_dmg(f);
|
||||||
|
} else {
|
||||||
|
// unreachable!()
|
||||||
|
log::error!("Failed to get the new version file path");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unreachable!()
|
||||||
|
log::error!("Failed to get the new version file from url: {}", _value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use objc::rc::autoreleasepool;
|
|||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use scrap::{libc::c_void, quartz::ffi::*};
|
use scrap::{libc::c_void, quartz::ffi::*};
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
os::unix::process::CommandExt,
|
os::unix::process::CommandExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
@@ -743,7 +744,7 @@ pub fn update_me() -> ResultType<()> {
|
|||||||
let update_body = format!(
|
let update_body = format!(
|
||||||
r#"
|
r#"
|
||||||
do shell script "
|
do shell script "
|
||||||
pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDesk.app && cp -R '{}' /Applications/ && chown -R {}:staff /Applications/RustDesk.app
|
pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDesk.app && ditto '{}' /Applications/RustDesk.app && chown -R {}:staff /Applications/RustDesk.app && xattr -r -d com.apple.quarantine /Applications/RustDesk.app
|
||||||
" with prompt "RustDesk wants to update itself" with administrator privileges
|
" with prompt "RustDesk wants to update itself" with administrator privileges
|
||||||
"#,
|
"#,
|
||||||
std::process::id(),
|
std::process::id(),
|
||||||
@@ -775,11 +776,26 @@ pgrep -x 'RustDesk' | grep -v {} | xargs kill -9 && rm -rf /Applications/RustDes
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_to(file: &str) -> ResultType<()> {
|
pub fn update_to(file: &str) -> ResultType<()> {
|
||||||
extract_dmg(file, UPDATE_TEMP_DIR)?;
|
|
||||||
update_extracted(UPDATE_TEMP_DIR)?;
|
update_extracted(UPDATE_TEMP_DIR)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_update_dmg(file: &str) {
|
||||||
|
let mut evt: HashMap<&str, String> =
|
||||||
|
HashMap::from([("name", "extract-update-dmg".to_string())]);
|
||||||
|
match extract_dmg(file, UPDATE_TEMP_DIR) {
|
||||||
|
Ok(_) => {
|
||||||
|
log::info!("Extracted dmg file to {}", UPDATE_TEMP_DIR);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
evt.insert("err", e.to_string());
|
||||||
|
log::error!("Failed to extract dmg file {}: {}", file, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let evt = serde_json::ser::to_string(&evt).unwrap_or("".to_owned());
|
||||||
|
crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, evt);
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
||||||
let mount_point = "/Volumes/RustDeskUpdate";
|
let mount_point = "/Volumes/RustDeskUpdate";
|
||||||
let target_path = Path::new(target_dir);
|
let target_path = Path::new(target_dir);
|
||||||
@@ -807,8 +823,8 @@ fn extract_dmg(dmg_path: &str, target_dir: &str) -> ResultType<()> {
|
|||||||
let src_path = format!("{}/{}", mount_point, app_name);
|
let src_path = format!("{}/{}", mount_point, app_name);
|
||||||
let dest_path = format!("{}/{}", target_dir, app_name);
|
let dest_path = format!("{}/{}", target_dir, app_name);
|
||||||
|
|
||||||
let copy_status = Command::new("cp")
|
let copy_status = Command::new("ditto")
|
||||||
.args(&["-R", &src_path, &dest_path])
|
.args(&[&src_path, &dest_path])
|
||||||
.status()?;
|
.status()?;
|
||||||
|
|
||||||
if !copy_status.success() {
|
if !copy_status.success() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on run {daemon_file, agent_file, user, cur_pid, source_dir}
|
|||||||
|
|
||||||
set kill_others to "pgrep -x 'RustDesk' | grep -v " & cur_pid & " | xargs kill -9;"
|
set kill_others to "pgrep -x 'RustDesk' | grep -v " & cur_pid & " | xargs kill -9;"
|
||||||
|
|
||||||
set copy_files to "rm -rf /Applications/RustDesk.app && cp -r " & source_dir & " /Applications && chown -R " & quoted form of user & ":staff /Applications/RustDesk.app;"
|
set copy_files to "rm -rf /Applications/RustDesk.app && ditto " & source_dir & " /Applications/RustDesk.app && chown -R " & quoted form of user & ":staff /Applications/RustDesk.app && xattr -r -d com.apple.quarantine /Applications/RustDesk.app;"
|
||||||
|
|
||||||
set sh1 to "echo " & quoted form of daemon_file & " > /Library/LaunchDaemons/com.carriez.RustDesk_service.plist && chown root:wheel /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
|
set sh1 to "echo " & quoted form of daemon_file & " > /Library/LaunchDaemons/com.carriez.RustDesk_service.plist && chown root:wheel /Library/LaunchDaemons/com.carriez.RustDesk_service.plist;"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user