From 9eb0daa5eba22ae2a5b053e05f7f9b94f46b8666 Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Sat, 13 Jun 2026 18:48:41 -0500 Subject: [PATCH] fixes --- examples/Covenant-SetupSampleApp-install.toml | 10 +++++++++- examples/README.md | 12 ++++++++---- src/bundle.rs | 4 ++-- src/install.rs | 6 +++--- src/progress.rs | 1 + src/sys.rs | 1 + src/uninstall.rs | 15 ++++++++++----- src/win/fs_ops.rs | 16 ++++++++-------- 8 files changed, 42 insertions(+), 23 deletions(-) diff --git a/examples/Covenant-SetupSampleApp-install.toml b/examples/Covenant-SetupSampleApp-install.toml index feff4a2..747f482 100644 --- a/examples/Covenant-SetupSampleApp-install.toml +++ b/examples/Covenant-SetupSampleApp-install.toml @@ -10,6 +10,10 @@ paths = [ source = 'payload\sample_app.cmd' destination = '{LocalAppData}\CovenantSetupSample\bin\sample_app.cmd' +[[files]] +source = 'payload\post_install.ps1' +destination = '{LocalAppData}\CovenantSetupSample\post_install.ps1' + [[registry]] key = 'HKCU\Software\CovenantSetupSample' name = 'InstallRoot' @@ -20,13 +24,17 @@ path = '{Desktop}\CovenantSetupSample.lnk' target = '{LocalAppData}\CovenantSetupSample\bin\sample_app.cmd' description = 'Launch the Covenant-Setup sample payload' +# The post-install script is copied into the install root above, so it is +# invoked by name with the working directory already pointing there. This keeps +# the script locatable for both direct and packaged (temp-extracted) installs, +# where the manifest's own directory is not the process working directory. [[scripts]] command = 'powershell' args = [ '-ExecutionPolicy', 'Bypass', '-File', - 'payload\post_install.ps1' + 'post_install.ps1' ] working_directory = '{LocalAppData}\CovenantSetupSample' diff --git a/examples/README.md b/examples/README.md index e9c4304..c73c20a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -35,8 +35,12 @@ cargo run -- uninstall examples/journal.json --json Expected effects: -- Creates `%LOCALAPPDATA%\CovenantSetupExample` -- Copies `sample_app.cmd` into the `bin` directory -- Writes `HKCU\Software\CovenantSetupExample\InstallRoot` +- Creates `%LOCALAPPDATA%\CovenantSetupSample` +- Copies `sample_app.cmd` into the `bin` directory and `post_install.ps1` into the install root +- Writes `HKCU\Software\CovenantSetupSample\InstallRoot` - Creates a desktop shortcut -- Runs an inline PowerShell post-install command and records only the script execution in the journal +- Runs `post_install.ps1` from the install root, which writes a timestamped marker under `logs\` + +Every directory, file, registry value, shortcut, and script execution is recorded in the +journal, so uninstall reverses all of them (and the `purge` spec removes the whole install +root, including the `logs\` directory the script created). diff --git a/src/bundle.rs b/src/bundle.rs index 71d447c..d48666f 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -82,7 +82,7 @@ pub(crate) fn build_packaged_installer( manifest: &InstallManifest, logger: &Logger, ) -> Result<(), AppError> { - fs::copy(current_exe, &exe_target)?; + fs::copy(current_exe, exe_target)?; let bundled_manifest_name = expected_manifest_file_name(&manifest.app_name); let bundle = EmbeddedBundle { metadata: PackagedApp { @@ -643,7 +643,7 @@ mod tests { temp.path(), &manifest, &bundled_manifest_name, - &[generated_installer.clone()], + std::slice::from_ref(&generated_installer), ) .unwrap(); files.sort_by(|left, right| left.relative_path.cmp(&right.relative_path)); diff --git a/src/install.rs b/src/install.rs index a96cc2b..7bfcdda 100644 --- a/src/install.rs +++ b/src/install.rs @@ -14,6 +14,7 @@ pub(crate) struct InstallRuntime { pub(crate) uninstall_registry_root: RegistryRoot, pub(crate) uninstall_registry_key: String, } +#[allow(clippy::too_many_arguments)] pub(crate) fn install( manifest_path: &Path, journal_path: Option, @@ -285,8 +286,8 @@ pub(crate) fn install( ); let cancelled = matches!(err, AppError::Cancelled); let mut rollback_error_msg = None; - if let Some(runtime) = &runtime_opt { - if !journal.actions.is_empty() { + if let Some(runtime) = &runtime_opt + && !journal.actions.is_empty() { let write_res = (|| -> Result<(), AppError> { write_journal(&runtime.journal_path, &journal)?; let read_back = fs::read_to_string(&runtime.journal_path)?; @@ -328,7 +329,6 @@ pub(crate) fn install( } } } - } if cancelled && rollback_error_msg.is_none() { let _ = finish_gui_progress( diff --git a/src/progress.rs b/src/progress.rs index 0aacbc3..46d9815 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -58,6 +58,7 @@ pub(crate) fn start_tui_progress(ui_mode: UiMode, label: String) -> Option Result; // (3) cleanup helper self-delete + #[allow(clippy::too_many_arguments)] fn spawn_cleanup_helper( &self, target_exe: &Path, diff --git a/src/uninstall.rs b/src/uninstall.rs index bbf2c4f..d784575 100644 --- a/src/uninstall.rs +++ b/src/uninstall.rs @@ -6,6 +6,7 @@ use std::os::windows::process::CommandExt; use std::path::{Path, PathBuf}; use std::process::Command; +#[allow(clippy::too_many_arguments)] pub(crate) fn uninstall( journal_path: &Path, elevate: bool, @@ -267,10 +268,11 @@ pub(crate) fn cleanup( sys.remove_file_with_fallback(&target_exe, &effective_logger)?; reboot_required = target_exe.exists(); } - if let Some(install_root) = install_root { - if install_root.exists() && fs::read_dir(&install_root)?.next().is_none() { - win::remove_directory_if_exists(&install_root, &effective_logger)?; - } + if let Some(install_root) = install_root + && install_root.exists() + && fs::read_dir(&install_root)?.next().is_none() + { + win::remove_directory_if_exists(&install_root, &effective_logger)?; } reboot_required |= sys.schedule_helper_self_cleanup(&effective_logger)?; if ui_mode == UiMode::Gui && !automation { @@ -383,7 +385,10 @@ pub(crate) fn journal_requires_admin( return Ok(true); } } - JournalAction::WriteRegistry { root, .. } if matches!(root, RegistryRoot::Hklm) => { + JournalAction::WriteRegistry { + root: RegistryRoot::Hklm, + .. + } => { return Ok(true); } _ => {} diff --git a/src/win/fs_ops.rs b/src/win/fs_ops.rs index 9445eaf..650c53b 100644 --- a/src/win/fs_ops.rs +++ b/src/win/fs_ops.rs @@ -4,18 +4,18 @@ pub fn create_directory_recursive(path: &Path, logger: &Logger) -> Result<(), Ap if path.as_os_str().is_empty() || path.exists() { return Ok(()); } - if let Some(parent) = path.parent() { - if parent != path { - create_directory_recursive(parent, logger)?; - } + if let Some(parent) = path.parent() + && parent != path + { + create_directory_recursive(parent, logger)?; } logger.unsafe_enter("CreateDirectoryW", json!({"path": path})); let result = unsafe { CreateDirectoryW(PCWSTR(Utf16Arg::from_path(path).as_ptr()), None) }; logger.unsafe_exit("CreateDirectoryW", json!({"ok": result.is_ok()})); - if let Err(err) = result { - if !path.exists() { - return Err(err.into()); - } + if let Err(err) = result + && !path.exists() + { + return Err(err.into()); } Ok(()) }