use std::fs;
use std::path::{Path, PathBuf};
use crate::provider_v3::plan::SoftwareArtifact;
use crate::provider_v3::{Error, Operation, Result, WireReason};
use crate::setup_core::platform_of_this_host;
use crate::setup_core::software::{self, Delivery, Software};
use crate::harness_runtime::facts::{Harness, LaunchBinding};
fn declared(harness: &Harness) -> Result<Software> {
match harness.software {
Some(
found @ Software {
delivery: Delivery::Artifacts(_),
..
},
) => Ok(found),
Some(Software {
delivery: Delivery::Manager { tool, reason },
command,
..
}) => Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{command} is installed by {tool}, which resolves a dependency closure: {reason}"
),
)),
None => Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{} does not implement the software lifecycle",
harness.provider_id
),
)),
}
}
fn program_directory(prefix: Option<&Path>, operation: Operation) -> Result<PathBuf> {
prefix.map(Path::to_path_buf).ok_or_else(|| {
Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{operation} installs a program, which lives under --prefix, not under --target; \
name an absolute --prefix"
),
)
})
}
fn version_for(declared: &Software, asked: Option<&str>) -> Result<Software> {
declared.at(asked).ok_or_else(|| {
let wanted = asked.unwrap_or_default();
Error::refuse(
WireReason::UnsupportedOperation,
format!(
"this build names {} {}; it cannot install {wanted}, and installing one it does \
name instead would be answering a question nobody asked",
declared.command,
declared.versions().join(" and "),
),
)
})
}
pub(crate) fn plan(
harness: &Harness,
prefix: Option<&Path>,
operation: Operation,
software_version: Option<&str>,
) -> Result<(Vec<SoftwareArtifact>, Vec<String>, &'static str)> {
let declared = version_for(&declared(harness)?, software_version)?;
let root = program_directory(prefix, operation)?;
let (os, arch) = platform_of_this_host();
let member = declared.member_on(os, arch);
let entry_point = format!(
"bin/{}",
crate::setup_core::software::exposed_name(declared.command, member)
);
let exposed = root.join(&entry_point);
let present = software::Present::under_named(&root, declared.command, member);
if operation == Operation::SoftwareRemove {
let mut effects = vec![
format!(
"remove {}, the {} tree this provider installed",
root.join(declared.version).display(),
declared.version
),
format!("remove {}", exposed.display()),
];
let kept: Vec<&str> = present
.versions
.iter()
.map(String::as_str)
.filter(|version| *version != declared.version)
.collect();
if !kept.is_empty() {
effects.push(format!(
"leave {} in place: this build pins {} and does not decide about versions it \
does not pin",
kept.join(", "),
declared.version
));
}
return Ok((Vec::new(), effects, declared.version));
}
if operation == Operation::SoftwareUpdate && present.versions.is_empty() {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"there is no {} under {} to update; software_install is the operation that \
puts one there",
declared.command,
root.display()
),
));
}
let artifact = declared.artifact_for(os, arch)?;
let mut effects = Vec::new();
if present.holds(declared.version) {
effects.push(format!(
"replace {}, which is already installed",
declared.version
));
} else if let Some(running) = present.exposed.as_deref() {
effects.push(format!(
"move {} from {running} to {}, keeping {running} where it is",
declared.command, declared.version
));
} else if !present.versions.is_empty() {
effects.push(format!(
"install {} beside {}, which no entry point names",
declared.version,
present.versions.join(", ")
));
}
effects.extend([
format!(
"download {} ({} bytes) in the operation's own download phase",
artifact.url, artifact.bytes
),
format!("check those bytes against {}", artifact.sha256),
match artifact.shape {
software::Shape::Raw => format!(
"place them as {}",
root.join(declared.version).join(declared.command).display()
),
software::Shape::GzipTar | software::Shape::Zip => format!(
"extract them into {}, whose {} is the program",
root.join(declared.version).display(),
artifact.member
),
},
format!("point {} at it", exposed.display()),
]);
Ok((
vec![SoftwareArtifact {
platform: artifact.platform.to_owned(),
url: artifact.url.to_owned(),
sha256: artifact.sha256.to_owned(),
byte_length: artifact.bytes,
entry_point,
}],
effects,
declared.version,
))
}
pub(crate) fn apply(
harness: &Harness,
prefix: Option<&Path>,
operation: Operation,
planned_version: &str,
planned_artifacts: &[SoftwareArtifact],
downloaded: &[PathBuf],
) -> Result<serde_json::Value> {
let declared = version_for(&declared(harness)?, Some(planned_version))?;
let root = program_directory(prefix, operation)?;
let control = root.join(harness.control_directory);
std::fs::create_dir_all(&control).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("--prefix {} cannot be created: {error}", root.display()),
)
})?;
let mut guard = crate::setup_core::lock::TargetLock::acquire(&control)?;
guard.annotate(&format!("{} {operation}", harness.provider_id))?;
let recovered = crate::setup_core::software::recover(&root)?;
if operation == Operation::SoftwareRemove {
if !downloaded.is_empty() {
return Err(Error::refuse(
WireReason::UnsupportedOperation,
"software_remove downloads nothing, so it takes no --software-artifact",
));
}
let removed = software::remove(&declared, &root)?;
return Ok(serde_json::json!({
"state": "verified",
"recovered": recovered,
"operation": operation.as_str(),
"command": declared.command,
"version": declared.version,
"removed": removed,
}));
}
let [path] = downloaded else {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{operation} installs the 1 artifact the plan named, and {} were given; \
pass one --software-artifact per entry, in the plan's order",
downloaded.len()
),
));
};
let [planned] = planned_artifacts else {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{operation} installs the 1 artifact the plan named, and the plan listed {}",
planned_artifacts.len()
),
));
};
if operation == Operation::SoftwareUpdate
&& software::Present::under_named(&root, declared.command, declared.member_here())
.versions
.is_empty()
{
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"there is no {} under {} to update any more; software_install is the operation \
that puts one there",
declared.command,
root.display()
),
));
}
let digest = crate::setup_core::digest::of_file(path)?;
if digest != planned.sha256 {
return Err(Error::refuse(
WireReason::DigestMismatch,
format!(
"the artifact given hashes to {digest}; this plan named {} for {planned_version}",
planned.sha256
),
));
}
let (os, arch) = platform_of_this_host();
let artifact = declared.artifact_for(os, arch)?;
if artifact.sha256 != planned.sha256 {
return Err(Error::refuse(
WireReason::DigestMismatch,
format!(
"this plan binds {planned_version} but names {}; {} publishes {}",
planned.sha256, declared.command, artifact.sha256
),
));
}
let installed = software::install(&declared, artifact, path, &root)?;
Ok(serde_json::json!({
"state": "verified",
"recovered": recovered,
"operation": operation.as_str(),
"command": declared.command,
"version": installed.version,
"entry_point": format!(
"bin/{}",
crate::setup_core::software::exposed_name(declared.command, artifact.member)
),
"executable": installed.executable.to_string_lossy(),
"files": installed.files,
}))
}
pub(crate) fn launch(
harness: &Harness,
target: &Path,
prefix: Option<&Path>,
arguments: &[String],
) -> Result<serde_json::Value> {
if !harness.can_launch() {
return Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{} does not declare launch: {}",
harness.provider_id,
harness.why_no_launch()
),
));
}
if harness.config_home_env.is_empty() {
refuse_unless_documented_home(harness, target)?;
}
let declared = declared(harness)?;
let root = program_directory(prefix, Operation::Launch)?;
let executable = root.join("bin").join(crate::setup_core::software::exposed_name(
declared.command,
declared.member_here(),
));
let found = executable.metadata().map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is not installed under {}: {error}; run software_install first",
declared.command,
root.display()
),
)
})?;
if !found.is_file() {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!("{} is not a regular file", executable.display()),
));
}
if !is_executable(&found) {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} exists but this host cannot execute it",
executable.display()
),
));
}
verify_installed(&root, declared.command, &executable)?;
let overlay = prepare_launch_overlay(harness, target)?;
#[allow(
clippy::disallowed_types,
reason = "launch starts the product, and is declared as doing so"
)]
let mut command = std::process::Command::new(&executable);
command.args(arguments);
for (name, value) in launch_environment(harness, target, overlay.as_deref()) {
command.env(name, value);
}
Err(replace_this_process(command, &executable))
}
fn refuse_unless_documented_home(harness: &Harness, target: &Path) -> Result<()> {
let expected = documented_home_path(harness)?;
if same_directory(target, &expected) {
return Ok(());
}
Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{} documents no environment variable for its configuration home, so launch \
is honest only when --target is {}; this target is not that home",
harness.product, harness.documented_config_home
),
))
}
fn documented_home_path(harness: &Harness) -> Result<PathBuf> {
let Some(leaf) = harness.documented_config_home.strip_prefix("~/") else {
return Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{} documented configuration home is not under ~/",
harness.provider_id
),
));
};
let home = std::env::var_os("HOME")
.or_else(|| std::env::var_os("USERPROFILE"))
.ok_or_else(|| {
Error::refuse(
WireReason::ProviderUnavailable,
format!(
"cannot resolve {} without HOME or USERPROFILE",
harness.documented_config_home
),
)
})?;
Ok(PathBuf::from(home).join(leaf))
}
fn same_directory(left: &Path, right: &Path) -> bool {
match (fs::canonicalize(left), fs::canonicalize(right)) {
(Ok(a), Ok(b)) => a == b,
_ => match (std::path::absolute(left), std::path::absolute(right)) {
(Ok(a), Ok(b)) => a == b,
_ => left == right,
},
}
}
fn verify_installed(root: &Path, command: &str, exposed: &Path) -> Result<()> {
let manifest = match crate::setup_core::software::Manifest::inspect(root, command) {
crate::setup_core::software::ManifestState::Missing => {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is missing; the installed chain is unresolved. \
Nothing has been started.",
crate::setup_core::software::Manifest::path(root, command).display()
),
));
}
crate::setup_core::software::ManifestState::Unreadable => {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} exists and could not be read; the installed chain is unresolved. \
Nothing has been started.",
crate::setup_core::software::Manifest::path(root, command).display()
),
));
}
crate::setup_core::software::ManifestState::Malformed => {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is not a usable launch receipt; the installed chain is unresolved. \
Nothing has been started.",
crate::setup_core::software::Manifest::path(root, command).display()
),
));
}
crate::setup_core::software::ManifestState::Unsupported { schema_version } => {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} names schema {schema_version}, which this build does not verify; \
the installed chain is unresolved. Nothing has been started.",
crate::setup_core::software::Manifest::path(root, command).display()
),
));
}
crate::setup_core::software::ManifestState::Present(found) => found,
};
let executable = root.join(&manifest.executable);
let found = crate::setup_core::digest::of_file(&executable).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} names {} and it could not be read: {error}",
crate::setup_core::software::Manifest::path(root, command).display(),
manifest.executable
),
)
})?;
if found != manifest.executable_sha256 {
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is not the {} this provider installed: recorded {}, found {}. \
Nothing has been started. Re-run software_install to put the pinned \
bytes back, or software_remove to take the prefix off.",
manifest.executable, manifest.version, manifest.executable_sha256, found
),
));
}
verify_exposed_chain(exposed, &executable, &found)
}
fn verify_exposed_chain(exposed: &Path, payload: &Path, payload_digest: &str) -> Result<()> {
if let Ok(target) = fs::read_link(exposed) {
let resolved = if target.is_absolute() {
target
} else {
exposed.parent().unwrap_or(Path::new(".")).join(target)
};
let expected = payload.canonicalize().ok();
let found = resolved.canonicalize().ok();
if expected.is_some() && expected == found {
return Ok(());
}
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} no longer names {}; nothing has been started",
exposed.display(),
payload.display()
),
));
}
if exposed
.extension()
.is_some_and(|ext| ext == "cmd" || ext == "bat")
{
let wrapper = fs::read_to_string(exposed).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("{} could not be read: {error}", exposed.display()),
)
})?;
let needle = payload.to_string_lossy();
if wrapper.contains(needle.as_ref()) {
return Ok(());
}
return Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is a launcher that does not name {}; nothing has been started",
exposed.display(),
payload.display()
),
));
}
let exposed_digest = crate::setup_core::digest::of_file(exposed).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("{} could not be read: {error}", exposed.display()),
)
})?;
if exposed_digest == payload_digest {
return Ok(());
}
Err(Error::refuse(
WireReason::ProviderUnavailable,
format!(
"{} is not the payload recorded at {}; nothing has been started",
exposed.display(),
payload.display()
),
))
}
fn launch_environment(
harness: &Harness,
target: &Path,
overlay: Option<&Path>,
) -> Vec<(&'static str, String)> {
let mut pairs = Vec::new();
if !harness.config_home_env.is_empty() {
pairs.push((
harness.config_home_env,
target.to_string_lossy().into_owned(),
));
}
if !harness.updates_off_env.is_empty() {
pairs.push((harness.updates_off_env, "1".to_owned()));
}
if let Some(home) = overlay {
let home = home.to_string_lossy().into_owned();
pairs.push(("HOME", home.clone()));
if cfg!(windows) {
pairs.push(("USERPROFILE", home));
}
}
pairs
}
pub(crate) fn prepare_launch_overlay(
harness: &Harness,
target: &Path,
) -> Result<Option<std::path::PathBuf>> {
let LaunchBinding::Partial { home_rooted, .. } = harness.launch_binding else {
return Ok(None);
};
if home_rooted.is_empty() {
return Ok(None);
}
let Some(leaf) = harness.documented_config_home.strip_prefix("~/") else {
return Err(Error::refuse(
WireReason::UnsupportedOperation,
format!(
"{} names process-home surfaces but its documented config home is not under ~/",
harness.provider_id
),
));
};
let overlay = target.join(harness.control_directory).join("launch-home");
let rooted = overlay.join(leaf);
if rooted.exists() {
crate::setup_core::lock::remove_dir_all(&rooted).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("cannot reset {}: {error}", rooted.display()),
)
})?;
}
fs::create_dir_all(&rooted).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("cannot create {}: {error}", rooted.display()),
)
})?;
for relative in home_rooted {
let from = target.join(relative);
if !from.exists() {
continue;
}
let to = rooted.join(relative);
copy_owned(&from, &to)?;
}
Ok(Some(overlay))
}
fn copy_owned(from: &Path, to: &Path) -> Result<()> {
if from.is_dir() {
fs::create_dir_all(to).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("cannot create {}: {error}", to.display()),
)
})?;
let read = fs::read_dir(from).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("cannot list {}: {error}", from.display()),
)
})?;
for entry in read.flatten() {
copy_owned(&entry.path(), &to.join(entry.file_name()))?;
}
return Ok(());
}
if let Some(parent) = to.parent() {
fs::create_dir_all(parent).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!("cannot create {}: {error}", parent.display()),
)
})?;
}
fs::copy(from, to).map_err(|error| {
Error::refuse(
WireReason::ProviderUnavailable,
format!(
"cannot copy {} to {}: {error}",
from.display(),
to.display()
),
)
})?;
Ok(())
}
#[cfg(unix)]
fn is_executable(found: &std::fs::Metadata) -> bool {
use std::os::unix::fs::MetadataExt;
found.mode() & 0o111 != 0
}
#[cfg(not(unix))]
fn is_executable(_found: &std::fs::Metadata) -> bool {
true
}
#[cfg(unix)]
#[allow(
clippy::disallowed_types,
reason = "the command `launch` built, handed to exec"
)]
fn replace_this_process(mut command: std::process::Command, executable: &Path) -> Error {
use std::os::unix::process::CommandExt;
let failure = command.exec();
Error::refuse(
WireReason::ProviderUnavailable,
format!("{} could not be started: {failure}", executable.display()),
)
}
#[cfg(not(unix))]
fn replace_this_process(mut command: std::process::Command, executable: &Path) -> Error {
match command.status() {
Ok(status) => std::process::exit(status.code().unwrap_or(1)),
Err(failure) => Error::refuse(
WireReason::ProviderUnavailable,
format!("{} could not be started: {failure}", executable.display()),
),
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::panic)]
use std::fs;
use super::*;
#[test]
fn a_launch_refuses_an_executable_that_is_not_the_one_installed() {
let root = std::env::temp_dir().join(format!(
"harness-runtime-verify-{}-{}",
std::process::id(),
line!()
));
let _ = fs::remove_dir_all(&root);
let version = root.join("1.2.3");
fs::create_dir_all(&version).unwrap();
fs::create_dir_all(root.join("bin")).unwrap();
let executable = version.join("program");
fs::write(&executable, b"the installed bytes\n").unwrap();
let exposed = root.join("bin").join("program");
#[cfg(unix)]
std::os::unix::fs::symlink(&executable, &exposed).unwrap();
#[cfg(not(unix))]
fs::copy(&executable, &exposed).unwrap();
let missing = verify_installed(&root, "program", &exposed).unwrap_err();
assert!(
missing.detail().contains("is missing"),
"a prefix with no record must fail as missing, not as a silent skip: {}",
missing.detail()
);
let manifest = crate::setup_core::software::Manifest {
schema_version: 1,
version: "1.2.3".to_owned(),
executable: "1.2.3/program".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&executable).unwrap(),
};
fs::write(
crate::setup_core::software::Manifest::path(&root, "program"),
serde_json::to_vec(&manifest).unwrap(),
)
.unwrap();
assert!(
verify_installed(&root, "program", &exposed).is_ok(),
"the bytes named by the record were refused"
);
fs::write(&executable, b"the installed bytes?\n").unwrap();
let refused = verify_installed(&root, "program", &exposed).unwrap_err();
assert_eq!(refused.reason(), Some(WireReason::ProviderUnavailable));
assert!(
refused.detail().contains("recorded") && refused.detail().contains("found"),
"the refusal does not say which digest was expected: {}",
refused.detail()
);
let _ = fs::remove_dir_all(&root);
}
#[test]
fn a_corrupt_or_unsupported_manifest_is_not_a_verified_launch() {
let root = std::env::temp_dir().join(format!(
"harness-runtime-verify-corrupt-{}-{}",
std::process::id(),
line!()
));
let _ = fs::remove_dir_all(&root);
let version = root.join("1.2.3");
fs::create_dir_all(&version).unwrap();
fs::create_dir_all(root.join("bin")).unwrap();
let executable = version.join("program");
fs::write(&executable, b"the installed bytes\n").unwrap();
let exposed = root.join("bin").join("program");
#[cfg(unix)]
std::os::unix::fs::symlink(&executable, &exposed).unwrap();
#[cfg(not(unix))]
fs::copy(&executable, &exposed).unwrap();
let path = crate::setup_core::software::Manifest::path(&root, "program");
fs::write(&path, b"{not a manifest").unwrap();
let malformed = verify_installed(&root, "program", &exposed).unwrap_err();
assert!(
malformed.detail().contains("not a usable launch receipt"),
"{}",
malformed.detail()
);
fs::write(
&path,
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 2,
version: "1.2.3".to_owned(),
executable: "1.2.3/program".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&executable).unwrap(),
})
.unwrap(),
)
.unwrap();
let unsupported = verify_installed(&root, "program", &exposed).unwrap_err();
assert!(
unsupported.detail().contains("schema 2"),
"{}",
unsupported.detail()
);
fs::write(
&path,
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 1,
version: "1.2.3".to_owned(),
executable: "1.2.3/program".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&executable).unwrap(),
})
.unwrap(),
)
.unwrap();
#[cfg(unix)]
{
let other = version.join("other");
fs::write(&other, b"the installed bytes\n").unwrap();
fs::remove_file(&exposed).unwrap();
std::os::unix::fs::symlink(&other, &exposed).unwrap();
let drifted = verify_installed(&root, "program", &exposed).unwrap_err();
assert!(
drifted.detail().contains("no longer names"),
"{}",
drifted.detail()
);
}
let _ = fs::remove_dir_all(&root);
}
#[test]
fn a_launch_sets_the_updater_switch_only_where_the_product_has_one() {
let target = Path::new("/tmp/nddev-launch-environment");
let with = Harness {
config_home_env: "PRODUCT_CONFIG_DIR",
updates_off_env: "DISABLE_UPDATES",
..crate::harness_runtime::wire::tests_support::TEST
};
assert_eq!(
launch_environment(&with, target, None),
vec![
("PRODUCT_CONFIG_DIR", target.display().to_string()),
("DISABLE_UPDATES", "1".to_owned()),
]
);
let without = Harness {
config_home_env: "PRODUCT_CONFIG_DIR",
updates_off_env: "",
..crate::harness_runtime::wire::tests_support::TEST
};
assert_eq!(
launch_environment(&without, target, None),
vec![("PRODUCT_CONFIG_DIR", target.display().to_string())],
"a product with no such variable had its environment written to anyway"
);
let documented = Harness {
config_home_env: "",
updates_off_env: "",
launch_binding: LaunchBinding::DocumentedHome { how: "measured" },
..crate::harness_runtime::wire::tests_support::TEST
};
assert_eq!(
launch_environment(&documented, target, None),
Vec::<(&str, String)>::new(),
"an empty config-home variable must not be written into the child environment"
);
}
#[test]
fn documented_home_launch_refuses_an_alternate_root_by_name() {
let harness = Harness {
config_home_env: "",
launch_binding: LaunchBinding::DocumentedHome {
how: "the product always reads ~/.test",
},
documented_config_home: "~/.test",
..crate::harness_runtime::wire::tests_support::TEST
};
assert!(harness.can_launch());
let elsewhere = std::env::temp_dir().join(format!(
"harness-runtime-not-documented-home-{}",
std::process::id()
));
let _ = fs::create_dir_all(&elsewhere);
let error = launch(&harness, &elsewhere, None, &[]).unwrap_err();
let _ = fs::remove_dir_all(&elsewhere);
assert_eq!(error.reason(), Some(WireReason::UnsupportedOperation));
assert!(
error.detail().contains("this target is not that home"),
"{}",
error.detail()
);
assert!(error.detail().contains("~/.test"), "{}", error.detail());
}
#[test]
fn documented_home_launch_accepts_the_resolved_home() {
let harness = Harness {
config_home_env: "",
launch_binding: LaunchBinding::DocumentedHome {
how: "the product always reads ~/.test",
},
documented_config_home: "~/.test",
..crate::harness_runtime::wire::tests_support::TEST
};
let target = documented_home_path(&harness).unwrap();
refuse_unless_documented_home(&harness, &target).unwrap();
}
fn planted_prefix(tag: &str) -> (std::path::PathBuf, std::path::PathBuf, std::path::PathBuf) {
let root = std::env::temp_dir().join(format!(
"harness-runtime-launch-{tag}-{}",
std::process::id(),
));
let _ = fs::remove_dir_all(&root);
let version = root.join("1.2.3");
fs::create_dir_all(&version).unwrap();
fs::create_dir_all(root.join("bin")).unwrap();
let payload = version.join("test-harness");
fs::write(&payload, crate::harness_runtime::wire::tests_support::TEST_PAYLOAD).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&payload, fs::Permissions::from_mode(0o755)).unwrap();
}
let exposed = root.join("bin").join("test-harness");
#[cfg(unix)]
std::os::unix::fs::symlink(&payload, &exposed).unwrap();
#[cfg(not(unix))]
fs::copy(&payload, &exposed).unwrap();
(root, payload, exposed)
}
#[test]
fn launch_refuses_missing_unreadable_malformed_and_unsupported_receipts_as_distinct_states() {
let target = Path::new("/tmp/nddev-launch-verify-target");
let (root, payload, exposed) = planted_prefix("receipts");
let path = crate::setup_core::software::Manifest::path(&root, "test-harness");
let missing =
launch(&crate::harness_runtime::wire::tests_support::TEST, target, Some(&root), &[]).unwrap_err();
assert!(
missing.detail().contains("is missing"),
"{}",
missing.detail()
);
fs::write(&path, b"{not a manifest").unwrap();
let malformed =
launch(&crate::harness_runtime::wire::tests_support::TEST, target, Some(&root), &[]).unwrap_err();
assert!(
malformed.detail().contains("not a usable launch receipt"),
"{}",
malformed.detail()
);
fs::write(
&path,
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 2,
version: "1.2.3".to_owned(),
executable: "1.2.3/test-harness".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&payload).unwrap(),
})
.unwrap(),
)
.unwrap();
let unsupported =
launch(&crate::harness_runtime::wire::tests_support::TEST, target, Some(&root), &[]).unwrap_err();
assert!(
unsupported.detail().contains("schema 2"),
"{}",
unsupported.detail()
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::write(
&path,
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 1,
version: "1.2.3".to_owned(),
executable: "1.2.3/test-harness".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&payload).unwrap(),
})
.unwrap(),
)
.unwrap();
fs::set_permissions(&path, fs::Permissions::from_mode(0o000)).unwrap();
let unreadable =
launch(&crate::harness_runtime::wire::tests_support::TEST, target, Some(&root), &[]).unwrap_err();
fs::set_permissions(&path, fs::Permissions::from_mode(0o644)).unwrap();
assert!(
unreadable.detail().contains("could not be read"),
"{}",
unreadable.detail()
);
}
let _ = fs::remove_dir_all(&root);
let _ = exposed;
}
#[test]
fn launch_refuses_another_valid_pin_substituted_for_the_recorded_payload() {
let target = Path::new("/tmp/nddev-launch-pin-swap-target");
let (root, payload, exposed) = planted_prefix("pin-swap");
let digest = crate::setup_core::digest::of_file(&payload).unwrap();
fs::write(
crate::setup_core::software::Manifest::path(&root, "test-harness"),
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 1,
version: "1.2.3".to_owned(),
executable: "1.2.3/test-harness".to_owned(),
executable_sha256: digest,
})
.unwrap(),
)
.unwrap();
fs::write(&payload, crate::harness_runtime::wire::tests_support::TEST_EARLIER_PAYLOAD).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(&payload, fs::Permissions::from_mode(0o755)).unwrap();
}
let swapped =
launch(&crate::harness_runtime::wire::tests_support::TEST, target, Some(&root), &[]).unwrap_err();
assert!(
swapped.detail().contains("recorded") && swapped.detail().contains("found"),
"{}",
swapped.detail()
);
let _ = fs::remove_dir_all(&root);
let _ = exposed;
}
#[test]
fn launch_refuses_a_windows_wrapper_that_does_not_name_the_recorded_payload() {
let root = std::env::temp_dir().join(format!(
"harness-runtime-wrapper-{}-{}",
std::process::id(),
line!()
));
let _ = fs::remove_dir_all(&root);
let version = root.join("1.2.3");
fs::create_dir_all(&version).unwrap();
fs::create_dir_all(root.join("bin")).unwrap();
let payload = version.join("test-harness.exe");
fs::write(&payload, b"payload-bytes\n").unwrap();
let wrapper = root.join("bin").join("test-harness.cmd");
fs::write(&wrapper, "@call \"C:\\other\\test-harness.exe\" %*\r\n").unwrap();
fs::write(
crate::setup_core::software::Manifest::path(&root, "test-harness"),
serde_json::to_vec(&crate::setup_core::software::Manifest {
schema_version: 1,
version: "1.2.3".to_owned(),
executable: "1.2.3/test-harness.exe".to_owned(),
executable_sha256: crate::setup_core::digest::of_file(&payload).unwrap(),
})
.unwrap(),
)
.unwrap();
let refused = verify_installed(&root, "test-harness", &wrapper).unwrap_err();
assert!(
refused.detail().contains("does not name"),
"{}",
refused.detail()
);
let _ = fs::remove_dir_all(&root);
}
fn cursor_like() -> Harness {
Harness {
documented_config_home: "~/.cursor",
config_home_env: "CURSOR_CONFIG_DIR",
control_directory: ".cursor-setup-system",
launch_binding: LaunchBinding::Partial {
unbound: "rules, hooks.json",
home_rooted: &["rules", "hooks.json", "skills"],
},
..crate::harness_runtime::wire::tests_support::TEST
}
}
#[test]
fn launch_overlay_copies_home_rooted_surfaces_from_the_target_not_the_caller() {
let root = std::env::temp_dir().join(format!(
"harness-runtime-overlay-{}-{}",
std::process::id(),
line!()
));
let _ = fs::remove_dir_all(&root);
let target = root.join("target");
let caller = root.join("caller-home");
fs::create_dir_all(target.join("rules")).unwrap();
fs::write(target.join("rules").join("a.mdc"), "from-target\n").unwrap();
fs::write(target.join("hooks.json"), "{\"from\":\"target\"}\n").unwrap();
fs::write(
target.join("cli-config.json"),
"{\"approvalMode\":\"unrestricted\"}\n",
)
.unwrap();
fs::create_dir_all(caller.join(".cursor").join("rules")).unwrap();
fs::write(
caller.join(".cursor").join("rules").join("a.mdc"),
"from-caller\n",
)
.unwrap();
let harness = cursor_like();
let overlay = prepare_launch_overlay(&harness, &target).unwrap().unwrap();
assert_eq!(
overlay,
target.join(".cursor-setup-system").join("launch-home")
);
let cursor_leaf = overlay.join(".cursor");
assert_eq!(
fs::read_to_string(cursor_leaf.join("rules").join("a.mdc")).unwrap(),
"from-target\n"
);
assert_eq!(
fs::read_to_string(cursor_leaf.join("hooks.json")).unwrap(),
"{\"from\":\"target\"}\n"
);
assert!(
!cursor_leaf.join("cli-config.json").exists(),
"bound cli-config.json was copied into the process-home leaf"
);
assert_eq!(
fs::read_to_string(caller.join(".cursor").join("rules").join("a.mdc")).unwrap(),
"from-caller\n",
"the caller's home was written"
);
let env = launch_environment(&harness, &target, Some(&overlay));
assert!(env.contains(&("CURSOR_CONFIG_DIR", target.display().to_string())));
assert!(env.contains(&("HOME", overlay.display().to_string())));
assert!(
!env.iter()
.any(|(name, value)| *name == "HOME" && Path::new(value) == caller)
);
let _ = fs::remove_dir_all(&root);
}
}