- 1
//! Shared install-root resolution (docs/design/32-release-engineering.md). - 2
//! - 3
//! `vak self install/status/verify` (crate `vak`, via - 4
//! `install::layout::InstallRoot`) and `vak_core::health`'s "self version - 5
//! parity" check must agree on where the installed-release manifest - 6
//! lives. `vak-core` cannot depend on the `vak` binary crate (the - 7
//! dependency runs the other way), so this module is the single source - 8
//! of truth for manifest-path resolution and `vak::install::layout` - 9
//! delegates to it instead of re-deriving the platform default. - 10
//! - 11
//! Before this module existed, `vak_core::health::install_manifest_path` - 12
//! hardcoded the Linux `~/.local/share/vak/...`-style layout - 13
//! (`home.join("local/release/install.json")`) for every platform, - 14
//! including macOS, where `self install` actually writes the manifest - 15
//! into the app bundle at `<prefix>/Contents/Resources/install.json`. - 16
//! `vak doctor` reported "no installed release manifest" unconditionally - 17
//! on macOS even for a correctly installed app. - 18
- 19
use std::path::{Path, PathBuf}; - 20
- 21
/// Application bundle name on macOS. Matches `productName` in - 22
/// `tauri.conf.json`; the desktop bundle and the managed install are the - 23
/// same directory, so the two spellings must never diverge. - 24
pub const BUNDLE_NAME: &str = "Vakyartha.app"; - 25
- 26
/// Overrides the install prefix without threading `--prefix` through - 27
/// every invocation. Useful for staging and for tests. - 28
pub const PREFIX_ENV: &str = "VAK_PREFIX"; - 29
- 30
const MANIFEST_FILE: &str = "install.json"; - 31
- 32
/// True when `root` is a macOS application bundle, which puts the - 33
/// manifest (and executables) under `Contents/...` rather than beside - 34
/// the prefix. - 35
pub fn is_bundle(root: &Path) -> bool { - 36
root.extension().and_then(|e| e.to_str()) == Some("app") - 37
} - 38
- 39
/// Where the install manifest lives under a given prefix, accounting for - 40
/// the bundle layout. - 41
pub fn manifest_path_for_prefix(prefix: &Path) -> PathBuf { - 42
if is_bundle(prefix) { - 43
prefix - 44
.join("Contents") - 45
.join("Resources") - 46
.join(MANIFEST_FILE) - 47
} else { - 48
prefix.join(MANIFEST_FILE) - 49
} - 50
} - 51
- 52
/// Platform default install root. macOS gets a real application bundle - 53
/// so the app is launchable from Finder; other platforms get a managed - 54
/// prefix under the XDG/canonical data home. - 55
pub fn platform_default_prefix() -> PathBuf { - 56
#[cfg(target_os = "macos")] - 57
{ - 58
let system = PathBuf::from("/Applications").join(BUNDLE_NAME); - 59
if system.exists() || dir_writable(Path::new("/Applications")) { - 60
return system; - 61
} - 62
if let Some(h) = std::env::var_os("HOME") { - 63
return PathBuf::from(h).join("Applications").join(BUNDLE_NAME); - 64
} - 65
system - 66
} - 67
#[cfg(not(target_os = "macos"))] - 68
{ - 69
vak_config::paths::data_home().join("local").join("release") - 70
} - 71
} - 72
- 73
#[cfg(target_os = "macos")] - 74
fn dir_writable(dir: &Path) -> bool { - 75
let probe = dir.join(format!(".vak-write-probe-{}", std::process::id())); - 76
match std::fs::write(&probe, b"") { - 77
Ok(()) => { - 78
let _ = std::fs::remove_file(&probe); - 79
true - 80
} - 81
Err(_) => false, - 82
} - 83
} - 84
- 85
/// Resolve the install manifest path the same way `InstallRoot::resolve` - 86
/// resolves the prefix: explicit prefix, then `VAK_PREFIX`, then the - 87
/// platform default. - 88
pub fn resolve_manifest_path(explicit_prefix: Option<PathBuf>) -> PathBuf { - 89
let prefix = explicit_prefix - 90
.or_else(|| { - 91
std::env::var_os(PREFIX_ENV) - 92
.filter(|v| !v.is_empty()) - 93
.map(PathBuf::from) - 94
}) - 95
.unwrap_or_else(platform_default_prefix); - 96
manifest_path_for_prefix(&prefix) - 97
} - 98
- 99
#[cfg(test)] - 100
#[allow(clippy::unwrap_used, clippy::expect_used)] - 101
mod tests { - 102
use super::*; - 103
- 104
#[test] - 105
fn plain_prefix_puts_manifest_beside_it() { - 106
let path = manifest_path_for_prefix(Path::new("/opt/vak")); - 107
assert_eq!(path, PathBuf::from("/opt/vak/install.json")); - 108
} - 109
- 110
#[test] - 111
fn bundle_prefix_puts_manifest_inside_contents_resources() { - 112
let path = manifest_path_for_prefix(Path::new("/Applications/Vakyartha.app")); - 113
assert_eq!( - 114
path, - 115
PathBuf::from("/Applications/Vakyartha.app/Contents/Resources/install.json") - 116
); - 117
} - 118
- 119
#[test] - 120
fn explicit_prefix_wins_over_default() { - 121
let path = resolve_manifest_path(Some(PathBuf::from("/tmp/vak-explicit"))); - 122
assert_eq!(path, PathBuf::from("/tmp/vak-explicit/install.json")); - 123
} - 124
} - 125
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.