mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Allow apps to be installed in /Applications and update elevated on osx
This commit is contained in:
13
src/Rust/Cargo.lock
generated
13
src/Rust/Cargo.lock
generated
@@ -1203,6 +1203,18 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "runas"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49535b7c73aec5596ae2c44a6d8a7a8f8592e5744564c327fd4846750413d921"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
"which",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.28"
|
||||
@@ -1619,6 +1631,7 @@ dependencies = [
|
||||
"rand",
|
||||
"regex",
|
||||
"remove_dir_all",
|
||||
"runas",
|
||||
"semver",
|
||||
"sha1_smol",
|
||||
"simple-stopwatch",
|
||||
|
||||
@@ -64,7 +64,7 @@ ureq = { version = "2.9", default-features = false, features = [
|
||||
native-tls = "0.2"
|
||||
file-rotate = "0.7"
|
||||
derivative = "2.2"
|
||||
simple-stopwatch = "0.1.4"
|
||||
simple-stopwatch = "0.1"
|
||||
glob = "0.3"
|
||||
enum-flags = "0.3"
|
||||
remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", features = [
|
||||
@@ -72,7 +72,8 @@ remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", feature
|
||||
] }
|
||||
ntest = "0.9.0"
|
||||
zstd = "0.13"
|
||||
sha1_smol = "1.0.0"
|
||||
sha1_smol = "1.0"
|
||||
runas = "1.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
native-dialog = "0.7"
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
use crate::shared::{
|
||||
self,
|
||||
bundle::{self, BundleInfo, Manifest},
|
||||
dialogs,
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use glob::glob;
|
||||
use runas::Command as RunAsCommand;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn apply<'a>(restart: bool, wait_for_parent: bool, package: Option<&PathBuf>, exe_args: Option<Vec<&str>>) -> Result<()> {
|
||||
pub fn apply<'a>(restart: bool, wait_for_parent: bool, package: Option<&PathBuf>, exe_args: Option<Vec<&str>>, noelevate: bool) -> Result<()> {
|
||||
if wait_for_parent {
|
||||
let _ = shared::wait_for_parent_to_exit(60_000); // 1 minute
|
||||
}
|
||||
|
||||
let (root_path, app) = shared::detect_current_manifest()?;
|
||||
|
||||
if let Err(e) = apply_package(package, &app, &root_path) {
|
||||
if let Err(e) = apply_package(package, &app, &root_path, noelevate, restart, exe_args.clone()) {
|
||||
error!("Error applying package: {}", e);
|
||||
if !restart {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: if the package fails to start, or fails hooks, we could roll back the install
|
||||
if restart {
|
||||
shared::start_package(&app, &root_path, exe_args, Some("VELOPACK_RESTART"))?;
|
||||
}
|
||||
@@ -27,7 +30,7 @@ pub fn apply<'a>(restart: bool, wait_for_parent: bool, package: Option<&PathBuf>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_package<'a>(package: Option<&PathBuf>, app: &Manifest, root_path: &PathBuf) -> Result<()> {
|
||||
fn apply_package<'a>(package: Option<&PathBuf>, app: &Manifest, root_path: &PathBuf, noelevate: bool, restart: bool, exe_args: Option<Vec<&str>>) -> Result<()> {
|
||||
let mut package_manifest: Option<Manifest> = None;
|
||||
let mut package_bundle: Option<BundleInfo<'a>> = None;
|
||||
|
||||
@@ -82,13 +85,29 @@ fn apply_package<'a>(package: Option<&PathBuf>, app: &Manifest, root_path: &Path
|
||||
crate::windows::run_hook(&app, &root_path, "--veloapp-obsolete", 15);
|
||||
|
||||
let current_dir = app.get_current_path(&root_path);
|
||||
shared::replace_dir_with_rollback(current_dir.clone(), || {
|
||||
if let Err(e) = shared::replace_dir_with_rollback(current_dir.clone(), || {
|
||||
if let Some(bundle) = package_bundle.take() {
|
||||
bundle.extract_lib_contents_to_path(¤t_dir, |_| {})
|
||||
} else {
|
||||
bail!("No bundle could be loaded.");
|
||||
}
|
||||
})?;
|
||||
}) {
|
||||
// replacing the package failed, we can try to elevate it though.
|
||||
error!("Failed to apply package: {}", e);
|
||||
if !dialogs::get_silent() && !noelevate {
|
||||
info!("Will try to elevate permissions and try again...");
|
||||
let title = format!("{} Update", app.title);
|
||||
let body = format!("{} {} is ready to be installed - you have {}, would you like to do this now?", app.title, found_version, app.version);
|
||||
if dialogs::show_ok_cancel(title.as_str(), None, body.as_str(), Some("Install Update")) {
|
||||
run_apply_elevated(restart, package, exe_args)?;
|
||||
} else {
|
||||
info!("User cancelled elevation prompt.");
|
||||
return Err(e);
|
||||
}
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
crate::windows::run_hook(&package_manifest, &root_path, "--veloapp-updated", 15);
|
||||
@@ -96,3 +115,36 @@ fn apply_package<'a>(package: Option<&PathBuf>, app: &Manifest, root_path: &Path
|
||||
info!("Package applied successfully.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_apply_elevated(restart: bool, package: Option<&PathBuf>, exe_args: Option<Vec<&str>>) -> Result<()> {
|
||||
let exe = std::env::current_exe()?;
|
||||
let mut args: Vec<String> = Vec::new();
|
||||
args.push("apply".to_string());
|
||||
args.push("--noelevate".to_string());
|
||||
|
||||
if restart {
|
||||
args.push("--restart".to_string());
|
||||
}
|
||||
|
||||
let package = package.map(|p| p.to_string_lossy().to_string());
|
||||
|
||||
if let Some(pkg) = package {
|
||||
args.push("--package".to_string());
|
||||
args.push(pkg);
|
||||
}
|
||||
|
||||
if let Some(a) = exe_args {
|
||||
args.push("--".to_string());
|
||||
a.iter().for_each(|a| args.push(a.to_string()));
|
||||
}
|
||||
|
||||
info!("Attempting to elevate: {} {:?}", exe.to_string_lossy(), args);
|
||||
|
||||
let mut cmd = RunAsCommand::new(&exe);
|
||||
cmd.gui(true);
|
||||
cmd.force_prompt(false);
|
||||
cmd.args(&args);
|
||||
cmd.status().map_err(|z| anyhow!("Failed to restart elevated ({}).", z))?;
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ pub fn detect_current_manifest() -> Result<(PathBuf, Manifest)> {
|
||||
|
||||
let root_dir = &my_path[..app_idx.unwrap()];
|
||||
let root_dir = root_dir.to_owned() + ".app";
|
||||
|
||||
|
||||
debug!("Detected Root: {}", root_dir);
|
||||
debug!("Detected AppId: {}", manifest.id);
|
||||
Ok((Path::new(&root_dir).to_path_buf(), manifest))
|
||||
|
||||
@@ -28,6 +28,7 @@ fn root_command() -> Command {
|
||||
.arg(arg!(-r --restart "Restart the application after the update"))
|
||||
.arg(arg!(-w --wait "Wait for the parent process to terminate before applying the update"))
|
||||
.arg(arg!(-p --package <FILE> "Update package to apply").value_parser(value_parser!(PathBuf)))
|
||||
.arg(arg!(--noelevate "If the application does not have sufficient privileges, do not elevate to admin"))
|
||||
.arg(arg!([EXE_ARGS] "Arguments to pass to the started executable. Must be preceeded by '--'.").required(false).last(true).num_args(0..))
|
||||
)
|
||||
.subcommand(Command::new("patch")
|
||||
@@ -139,6 +140,7 @@ fn patch(matches: &ArgMatches) -> Result<()> {
|
||||
}
|
||||
|
||||
fn apply(matches: &ArgMatches) -> Result<()> {
|
||||
let noelevate = matches.get_flag("noelevate");
|
||||
let restart = matches.get_flag("restart");
|
||||
let wait_for_parent = matches.get_flag("wait");
|
||||
let package = matches.get_one::<PathBuf>("package");
|
||||
@@ -149,8 +151,9 @@ fn apply(matches: &ArgMatches) -> Result<()> {
|
||||
info!(" Wait: {:?}", wait_for_parent);
|
||||
info!(" Package: {:?}", package);
|
||||
info!(" Exe Args: {:?}", exe_args);
|
||||
info!(" No Elevate: {}", noelevate);
|
||||
|
||||
commands::apply(restart, wait_for_parent, package, exe_args)
|
||||
commands::apply(restart, wait_for_parent, package, exe_args, noelevate)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -126,7 +126,7 @@ public class HelperExe : HelperFile
|
||||
distXml.Insert(2, $"<title>{SecurityElement.Escape(appTitle)}</title>");
|
||||
|
||||
// disable local system installation (install to home dir)
|
||||
distXml.Insert(2, "<domains enable_anywhere=\"false\" enable_currentUserHome=\"true\" enable_localSystem=\"false\" />");
|
||||
distXml.Insert(2, "<domains enable_anywhere=\"false\" enable_currentUserHome=\"true\" enable_localSystem=\"true\" />");
|
||||
|
||||
// add extra landing content (eg. license, readme)
|
||||
foreach (var kvp in extraContent) {
|
||||
|
||||
Reference in New Issue
Block a user