Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/v2/specs/rvpm-dist.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `..`
Expand Down Expand Up @@ -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.
5 changes: 5 additions & 0 deletions src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -412,6 +415,7 @@ fn validate_dist(raw: RawDist, package: &Package) -> Result<Dist, ManifestError>
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),
},
})
}
Expand Down Expand Up @@ -558,6 +562,7 @@ struct RawDistLinux {
struct RawDistWindows {
icon: Option<String>,
upgrade_code: Option<String>,
add_to_path: Option<bool>,
}

impl Manifest {
Expand Down
47 changes: 47 additions & 0 deletions src/ops/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,17 @@ fn wix_source(ctx: &DistContext, stage: &Path, upgrade_code: &str) -> String {
));
refs.push_str(&format!(" <ComponentRef Id=\"{}\" />\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!(
" <Component Id=\"PathEntry\" Guid=\"*\">\n <RegistryValue Root=\"HKLM\" Key=\"Software\\{key}\" Name=\"Installed\" Type=\"integer\" Value=\"1\" KeyPath=\"yes\" />\n <Environment Id=\"UpdatePath\" Name=\"PATH\" Value=\"[APPDIR]\" Permanent=\"no\" Part=\"last\" Action=\"set\" System=\"yes\" />\n </Component>\n",
key = xml_escape(&ctx.name),
));
refs.push_str(" <ComponentRef Id=\"PathEntry\" />\n");
}
format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
Expand Down Expand Up @@ -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("<Environment"));
assert!(!wxs.contains("PathEntry"));
}

#[test]
fn wix_source_adds_path_when_requested() {
let manifest = r#"
[package]
name = "demo"
version = "1.2.0"
authors = ["Ada <ada@example.com>"]

[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("<Component Id=\"PathEntry\""));
assert!(wxs.contains("<ComponentRef Id=\"PathEntry\" />"));
assert!(wxs.contains(
"<Environment Id=\"UpdatePath\" Name=\"PATH\" Value=\"[APPDIR]\" Permanent=\"no\" Part=\"last\" Action=\"set\" System=\"yes\" />"
));
// 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");
Expand Down
Loading