diff --git a/docs/v2/specs/rvpm-dist.md b/docs/v2/specs/rvpm-dist.md index 8e3a1d5..178d341 100644 --- a/docs/v2/specs/rvpm-dist.md +++ b/docs/v2/specs/rvpm-dist.md @@ -47,6 +47,7 @@ priority = "optional" # deb [dist.windows] icon = "assets/rook.ico" # Inno Setup icon upgrade_code = "9f0c86a1-2b3c-4d5e-8f90-112233445566" # msi upgrade GUID +add_to_path = true # msi appends the install dir to system PATH ``` Asset `source` and `dest` must be relative forward-slash paths with no `..` @@ -79,9 +80,16 @@ install. When `[dist.windows].upgrade_code` is not set, rvpm derives a stable GUID from the package name and prints it with a note; pin it in the manifest so it never changes by accident. +`[dist.windows].add_to_path = true` makes the msi append the install +directory to the system PATH, so a command-line tool is callable from a +terminal after installing (the other installers still leave PATH alone). +The uninstaller removes the entry. + ## What is out of scope Packages are not signed (deb signing, rpm signing, Authenticode are all post-processing on the produced artifacts). Cross-format scripting hooks -(postinst and friends) and desktop shortcuts are not modeled yet. The -installers do not modify PATH. +(postinst and friends) and desktop shortcuts are not modeled yet. Only the +msi modifies PATH, and only when `[dist.windows].add_to_path` is set; the +deb and rpm install to `/usr/bin` (already on PATH) and the archives are +unpacked wherever the user chooses. diff --git a/src/manifest/mod.rs b/src/manifest/mod.rs index 21bda73..bda1fa7 100644 --- a/src/manifest/mod.rs +++ b/src/manifest/mod.rs @@ -209,6 +209,9 @@ pub struct DistWindows { /// The stable GUID that lets an msi upgrade an installed older version. /// Required by the msi backend; generate one once and keep it. pub upgrade_code: String, + /// When set, the msi appends the install directory to the system PATH, so + /// a command-line tool is callable from a terminal after installing. + pub add_to_path: bool, } impl Dist { @@ -412,6 +415,7 @@ fn validate_dist(raw: RawDist, package: &Package) -> Result windows: DistWindows { icon: windows.icon.unwrap_or_default(), upgrade_code: windows.upgrade_code.unwrap_or_default(), + add_to_path: windows.add_to_path.unwrap_or(false), }, }) } @@ -558,6 +562,7 @@ struct RawDistLinux { struct RawDistWindows { icon: Option, upgrade_code: Option, + add_to_path: Option, } impl Manifest { diff --git a/src/ops/dist.rs b/src/ops/dist.rs index bf76b12..6228cbc 100644 --- a/src/ops/dist.rs +++ b/src/ops/dist.rs @@ -560,6 +560,17 @@ fn wix_source(ctx: &DistContext, stage: &Path, upgrade_code: &str) -> String { )); refs.push_str(&format!(" \n", id)); } + // Append the install directory to the system PATH so a command-line tool + // is on PATH after installing. A registry value under HKLM is the + // component key path (an Environment element cannot be one), keyed by the + // package name so it is stable across versions. + if d.windows.add_to_path { + components.push_str(&format!( + " \n \n \n \n", + key = xml_escape(&ctx.name), + )); + refs.push_str(" \n"); + } format!( r#" @@ -948,6 +959,42 @@ depends = ["libc6 (>= 2.31)", "zlib1g"] ); } + #[test] + fn wix_source_omits_path_entry_by_default() { + let app = FakeApp::new("wix-nopath"); + let ctx = app.context(WITH_ASSET); + let stage = ctx.work_dir.join("msi"); + stage_tree(&ctx, &stage, "", "").expect("stage"); + let wxs = wix_source(&ctx, &stage, "9f0c86a1-2b3c-4d5e-8f90-112233445566"); + assert!(!wxs.contains(""] + +[dist.windows] +add_to_path = true +"#; + let app = FakeApp::new("wix-path"); + let ctx = app.context(manifest); + let stage = ctx.work_dir.join("msi"); + stage_tree(&ctx, &stage, "", "").expect("stage"); + let wxs = wix_source(&ctx, &stage, "9f0c86a1-2b3c-4d5e-8f90-112233445566"); + assert!(wxs.contains("")); + assert!(wxs.contains( + "" + )); + // A registry value is the component key path, keyed by the package name. + assert!(wxs.contains("Key=\"Software\\demo\"")); + } + #[test] fn derived_upgrade_code_is_a_stable_guid() { let a = derived_upgrade_code("demo");