Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
build out new xplat rust modules
Browse files Browse the repository at this point in the history
  • Loading branch information
caesay committed Dec 25, 2023
1 parent 579aeb1 commit 3ae9844
Show file tree
Hide file tree
Showing 22 changed files with 810 additions and 805 deletions.
3 changes: 3 additions & 0 deletions src/Rust/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.features": ["windows"]
}
10 changes: 5 additions & 5 deletions src/Rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ version = "0.0.0-local"
edition = "2021"

[features]
windows_build = []
windows = []

[[bin]]
name = "setup"
path = "src/setup.rs"
required-features = ["windows_build"]
required-features = ["windows"]

[[bin]]
name = "update"
Expand All @@ -18,12 +18,12 @@ path = "src/update.rs"
[[bin]]
name = "testapp"
path = "src/testapp.rs"
required-features = ["windows_build"]
required-features = ["windows"]

[[bin]]
name = "stub"
path = "src/stub.rs"
required-features = ["windows_build"]
required-features = ["windows"]

[profile.release]
opt-level = "z" # optimize for size
Expand Down Expand Up @@ -60,10 +60,10 @@ native-tls = "0.2"
file-rotate = "0.7"
derivative = "2.2"
simple-stopwatch = "0.1.4"
glob = "0.3"

[target.'cfg(windows)'.dependencies]
fs_extra = "1.2"
glob = "0.3"
memmap2 = "0.9"
winsafe = { git = "https://github.com/caesay/winsafe.git", branch = "cs/only-ipersistfile", features = [
"kernel",
Expand Down
90 changes: 90 additions & 0 deletions src/Rust/src/commands/apply.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use crate::shared::{
self,
bundle::{self, BundleInfo, Manifest},
};
use anyhow::{bail, Result};
use glob::glob;
use std::path::PathBuf;

pub fn apply<'a>(restart: bool, wait_for_parent: bool, package: Option<&PathBuf>, exe_name: Option<&String>, exe_args: Option<Vec<&str>>) -> Result<()> {
if wait_for_parent {
let _ = shared::wait_for_parent_to_exit(60_000); // 1 minute
}

if let Err(e) = apply_package(package) {
error!("Error applying package: {}", e);
if !restart {
return Err(e);
}
}

if restart {
super::start(false, exe_name, exe_args, None)?;
}

Ok(())
}

fn apply_package<'a>(package: Option<&PathBuf>) -> Result<()> {
let mut package_manifest: Option<Manifest> = None;
let mut package_bundle: Option<BundleInfo<'a>> = None;

let (root_path, app) = shared::detect_current_manifest()?;

if let Some(pkg) = package {
info!("Loading package from argument '{}'.", pkg.to_string_lossy());
let bun = bundle::load_bundle_from_file(&pkg)?;
package_manifest = Some(bun.read_manifest()?);
package_bundle = Some(bun);
} else {
info!("No package specified, searching for latest.");
let packages_dir = app.get_packages_path(&root_path);
if let Ok(paths) = glob(format!("{}/*.nupkg", packages_dir).as_str()) {
for path in paths {
if let Ok(path) = path {
trace!("Checking package: '{}'", path.to_string_lossy());
if let Ok(bun) = bundle::load_bundle_from_file(&path) {
if let Ok(mani) = bun.read_manifest() {
if package_manifest.is_none() || mani.version > package_manifest.clone().unwrap().version {
info!("Found {}: '{}'", mani.version, path.to_string_lossy());
package_manifest = Some(mani);
package_bundle = Some(bun);
}
}
}
}
}
}
}

if package_manifest.is_none() || package_bundle.is_none() {
bail!("Unable to find/load suitable package.");
}

let package_manifest = package_manifest.unwrap();

let found_version = package_manifest.clone().version;
if found_version <= app.version {
bail!("Latest package found is {}, which is not newer than current version {}.", found_version, app.version);
}

info!("Applying package to current: {}", found_version);

#[cfg(target_os = "windows")]
crate::windows::run_hook(&app, &root_path, "--squirrel-obsoleted", 15);

let current_dir = app.get_current_path(&root_path);
shared::replace_dir_with_rollback(current_dir.clone(), || {
if let Some(bundle) = package_bundle.take() {
bundle.extract_lib_contents_to_path(&current_dir, |_| {})
} else {
bail!("No bundle could be loaded.");
}
})?;

#[cfg(target_os = "windows")]
crate::windows::run_hook(&package_manifest, &root_path, "--squirrel-updated", 15);

info!("Package applied successfully.");
Ok(())
}
10 changes: 10 additions & 0 deletions src/Rust/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mod apply;
pub use apply::*;

mod start;
pub use start::*;

#[cfg(target_os = "windows")]
mod uninstall;
#[cfg(target_os = "windows")]
pub use uninstall::*;
47 changes: 47 additions & 0 deletions src/Rust/src/commands/start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::shared;
use anyhow::{bail, Result};
use std::path::Path;

#[cfg(target_os = "windows")]
pub fn start(wait_for_parent: bool, exe_name: Option<&String>, exe_args: Option<Vec<&str>>, legacy_args: Option<&String>) -> Result<()> {
if legacy_args.is_some() {
info!(" Legacy Args: {:?}", legacy_args);
warn!("Legacy args format is deprecated and will be removed in a future release. Please update your application to use the new format.");
}

if legacy_args.is_some() && exe_args.is_some() {
bail!("Cannot use both legacy args and new args format.");
}

if wait_for_parent {
shared::wait_for_parent_to_exit(60_000)?; // 1 minute
}

let (root_path, app) = shared::detect_current_manifest()?;

let current = app.get_current_path(&root_path);
let exe_to_execute = if let Some(exe) = exe_name {
Path::new(&current).join(exe)
} else {
let exe = app.get_main_exe_path(&root_path);
Path::new(&exe).to_path_buf()
};

if !exe_to_execute.exists() {
bail!("Unable to find executable to start: '{}'", exe_to_execute.to_string_lossy());
}

crate::windows::assert_can_run_binary_authenticode(&exe_to_execute)?;

info!("About to launch: '{}' in dir '{}'", exe_to_execute.to_string_lossy(), current);

if let Some(args) = exe_args {
crate::shared::run_process(exe_to_execute, args, current)?;
} else if let Some(args) = legacy_args {
crate::windows::run_process_raw_args(exe_to_execute, args, current)?;
} else {
crate::shared::run_process(exe_to_execute, vec![], current)?;
};

Ok(())
}
58 changes: 58 additions & 0 deletions src/Rust/src/commands/uninstall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::shared::{self, bundle::Manifest};
use crate::windows;
use anyhow::Result;
use std::fs::File;
use std::path::PathBuf;

pub fn uninstall(log_file: &PathBuf) -> Result<()> {
info!("Command: Uninstall");
let (root_path, app) = shared::detect_current_manifest()?;

fn _uninstall_impl(app: &Manifest, root_path: &PathBuf) -> bool {
// the real app could be running at the moment
let _ = shared::force_stop_package(&root_path);

let mut finished_with_errors = false;

// run uninstall hook
windows::run_hook(&app, root_path, "--squirrel-uninstall", 60);

if let Err(e) = windows::remove_all_shortcuts_for_root_dir(&root_path) {
error!("Unable to remove shortcuts ({}).", e);
// finished_with_errors = true;
}

info!("Removing directory '{}'", root_path.to_string_lossy());
if let Err(e) = shared::retry_io(|| remove_dir_all::remove_dir_containing_current_executable()) {
error!("Unable to remove directory, some files may be in use ({}).", e);
finished_with_errors = true;
}

if let Err(e) = app.remove_uninstall_entry() {
error!("Unable to remove uninstall registry entry ({}).", e);
// finished_with_errors = true;
}

!finished_with_errors
}

// if it returns true, it was a success.
// if it returns false, it was completed with errors which the user should be notified of.
let result = _uninstall_impl(&app, &root_path);

if result {
info!("Finished successfully.");
shared::dialogs::show_info("The application was successfully uninstalled.", format!("{} Uninstall", app.title));
} else {
error!("Finished with errors.");
shared::dialogs::show_uninstall_complete_with_errors_dialog(&app, &log_file);
}

let dead_path = root_path.join(".dead");
let _ = File::create(dead_path);
if let Err(e) = windows::register_intent_to_delete_self(5, &root_path) {
warn!("Unable to schedule self delete ({}).", e);
}

Ok(())
}
30 changes: 30 additions & 0 deletions src/Rust/src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::path::PathBuf;
use anyhow::Result;
use simplelog::*;

pub fn trace_logger() {
TermLogger::init(LevelFilter::Trace, Config::default(), TerminalMode::Mixed, ColorChoice::Never).unwrap();
}

pub fn setup_logging(file: Option<&PathBuf>, console: bool, verbose: bool, nocolor: bool) -> Result<()> {
let mut loggers: Vec<Box<dyn SharedLogger>> = Vec::new();
let color_choice = if nocolor { ColorChoice::Never } else { ColorChoice::Auto };
if console {
let console_level = if verbose { LevelFilter::Debug } else { LevelFilter::Info };
loggers.push(TermLogger::new(console_level, Config::default(), TerminalMode::Mixed, color_choice));
}

if let Some(f) = file {
let file_level = if verbose { LevelFilter::Trace } else { LevelFilter::Info };
let writer = file_rotate::FileRotate::new(
f.clone(),
file_rotate::suffix::AppendCount::new(1), // keep 1 old log file
file_rotate::ContentLimit::Bytes(1 * 1024 * 1024), // 1MB max log file size
file_rotate::compression::Compression::None,
);
loggers.push(WriteLogger::new(file_level, Config::default(), writer));
}

CombinedLogger::init(loggers)?;
Ok(())
}
Loading

0 comments on commit 3ae9844

Please sign in to comment.