pub mod allocation;
mod cleanup;
mod exposure;
mod input;
mod launch;
pub mod operation;
mod ownership;
mod preparation;
mod records;
mod removal;
mod staging;
mod store;
mod switch;
pub(crate) mod writer;
pub use input::VerifiedArtifact;
pub use writer::Writer;
#[cfg(test)]
use exposure::expose;
use std::fs;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::setup_core::archive::{self, Limits};
#[cfg(test)]
use crate::setup_core::digest;
use crate::setup_core::error::{Error, ReasonCode, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shape {
Raw,
GzipTar,
Zip,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Artifact {
pub platform: &'static str,
pub url: &'static str,
pub bytes: u64,
pub sha256: &'static str,
pub shape: Shape,
pub member: &'static str,
}
#[derive(Debug, Clone, Copy)]
pub enum Delivery {
Artifacts(&'static [Artifact]),
Manager {
tool: &'static str,
reason: &'static str,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Previous {
pub version: &'static str,
pub artifacts: &'static [Artifact],
}
#[derive(Debug, Clone, Copy)]
pub struct Software {
pub version: &'static str,
pub command: &'static str,
pub delivery: Delivery,
pub unsupported: &'static [&'static str],
pub previous: Option<Previous>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Present {
pub versions: Vec<String>,
pub exposed: Option<String>,
}
impl Present {
#[must_use]
fn marker(root: &Path, command: &str) -> PathBuf {
root.join("bin").join(format!(".{command}.version"))
}
#[must_use]
pub fn holds(&self, version: &str) -> bool {
self.versions.iter().any(|found| found == version)
}
#[must_use]
pub fn under_named(root: &Path, command: &str, member: &str) -> Self {
Self::under_on(root, command, member, cfg!(windows))
}
#[must_use]
pub fn under_on(root: &Path, command: &str, member: &str, windows: bool) -> Self {
let mut versions: Vec<String> = fs::read_dir(root)
.into_iter()
.flatten()
.flatten()
.filter(|entry| entry.path().is_dir())
.filter_map(|entry| entry.file_name().into_string().ok())
.filter(|name| name != "bin" && !name.starts_with('.'))
.collect();
versions.sort();
let exposed_as = exposed_name_on(command, member, windows);
let usable = fs::metadata(root.join("bin").join(&exposed_as)).is_ok();
let marker = Self::marker(root, command);
let exposed = fs::read_to_string(&marker)
.ok()
.filter(|_| usable)
.map(|held| held.trim().to_owned())
.filter(|name| versions.contains(name))
.or_else(|| {
fs::canonicalize(root.join("bin").join(&exposed_as))
.ok()
.zip(fs::canonicalize(root).ok())
.and_then(|(to, base)| {
to.strip_prefix(&base).ok().and_then(|rest| {
rest.components()
.next()
.map(|first| first.as_os_str().to_string_lossy().into_owned())
})
})
.filter(|name| versions.contains(name))
});
Self { versions, exposed }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Installed {
pub version: String,
pub root: PathBuf,
pub executable: PathBuf,
pub files: usize,
}
impl Software {
#[must_use]
pub fn versions(&self) -> Vec<&'static str> {
let mut named = vec![self.version];
if let Some(earlier) = self.previous {
named.push(earlier.version);
}
named
}
#[must_use]
pub fn at(&self, asked: Option<&str>) -> Option<Self> {
match asked {
None => Some(*self),
Some(wanted) if wanted == self.version => Some(*self),
Some(wanted) => self
.previous
.filter(|earlier| earlier.version == wanted)
.map(|earlier| Self {
version: earlier.version,
delivery: Delivery::Artifacts(earlier.artifacts),
..*self
}),
}
}
#[must_use]
pub fn for_bytes(&self, os: &str, arch: &str, digest: &str) -> Option<Self> {
self.versions()
.into_iter()
.filter_map(|version| self.at(Some(version)))
.find(|candidate| {
candidate
.artifact_for(os, arch)
.is_ok_and(|artifact| artifact.sha256 == digest)
})
}
#[must_use]
fn member_hint(&self) -> &'static str {
match self.delivery {
Delivery::Artifacts(artifacts) => {
artifacts.first().map_or(self.command, |entry| entry.member)
}
Delivery::Manager { .. } => self.command,
}
}
#[must_use]
pub fn member_on(&self, os: &str, arch: &str) -> &'static str {
self.artifact_for(os, arch)
.map_or_else(|_| self.member_hint(), |artifact| artifact.member)
}
#[must_use]
pub fn member_here(&self) -> &'static str {
let (os, arch) = crate::setup_core::platform_of_this_host();
self.member_on(os, arch)
}
pub fn artifact_for(&self, os: &str, arch: &str) -> Result<&'static Artifact> {
let artifacts = match self.delivery {
Delivery::Artifacts(artifacts) => artifacts,
Delivery::Manager { tool, reason } => {
return Err(Error::new(
ReasonCode::UnsupportedOperation,
format!("{} is delivered by {tool}: {reason}", self.command),
));
}
};
let wanted = format!("{os}/{arch}");
if let Some(found) = artifacts.iter().find(|entry| entry.platform == wanted) {
return Ok(found);
}
let prefix = format!("{os}/");
let system_is_published = artifacts
.iter()
.any(|entry| entry.platform.starts_with(&prefix));
let reason = if system_is_published {
ReasonCode::UnsupportedArchitecture
} else {
ReasonCode::UnsupportedPlatform
};
Err(Error::new(
reason,
format!(
"{} publishes no build for {wanted}; it publishes {}",
self.command,
artifacts
.iter()
.map(|entry| entry.platform)
.collect::<Vec<_>>()
.join(", ")
),
))
}
}
impl Artifact {
pub fn verify(&self, downloaded: &Path) -> Result<()> {
VerifiedArtifact::open(self, downloaded).map(|_| ())
}
#[must_use]
pub const fn limits(&self) -> Limits {
Limits {
entries: 65_536,
bytes: match self.bytes.checked_mul(16) {
Some(scaled) if scaled > 64 * 1024 * 1024 => scaled,
_ => 64 * 1024 * 1024,
},
}
}
}
pub fn install(
software: &Software,
artifact: &Artifact,
downloaded: &Path,
root: &Path,
) -> Result<Installed> {
let source = VerifiedArtifact::open(artifact, downloaded)?;
install_verified(software, artifact, source, root)
}
pub fn install_verified(
software: &Software,
artifact: &Artifact,
mut source: VerifiedArtifact,
root: &Path,
) -> Result<Installed> {
require_idle(software, root)?;
source.begin(artifact)?;
records::io(fs::create_dir_all(root))?;
Writer::acquire(root)?.install_verified(software, artifact, source)
}
fn install_locked(
software: &Software,
artifact: &Artifact,
source: VerifiedArtifact,
root: &Path,
) -> Result<Installed> {
install_for(software, artifact, source, root, None)
}
fn install_for(
software: &Software,
artifact: &Artifact,
mut source: VerifiedArtifact,
root: &Path,
operation: Option<&operation::Binding>,
) -> Result<Installed> {
require_idle_for(software, root, operation)?;
source.begin(artifact)?;
let version_root = root.join(software.version);
let member = if artifact.shape == Shape::Raw {
software.command
} else {
artifact.member
};
let mut transaction = staging::Staging::begin_for(
root,
software.command,
software.version,
member,
artifact.sha256,
operation,
)?;
let staging = transaction.stage_path();
let output = transaction.destination()?;
let files = match artifact.shape {
Shape::Raw => {
archive::place_executable_into(source.reader(), Path::new(software.command), &output)?;
1
}
Shape::GzipTar | Shape::Zip => {
let entries = if artifact.shape == Shape::Zip {
archive::extract_zip_into(source.reader(), &staging, artifact.limits(), &output)?
} else {
archive::extract_gzip_tar_into(
source.reader(),
&staging,
artifact.limits(),
&output,
)?
};
let found = entries
.iter()
.any(|entry| entry.path == artifact.member && entry.kind == archive::Kind::File);
if !found {
return Err(Error::new(
ReasonCode::IntegrityMismatch,
format!(
"the archive does not contain {}, which the plan named as the executable",
artifact.member
),
));
}
entries.len()
}
};
source.finish()?;
drop(output);
transaction.record_files(files)?;
transaction.promote()?;
let exposed = root
.join("bin")
.join(exposed_name(software.command, artifact.member));
transaction.expose()?;
transaction.complete()?;
Ok(Installed {
version: software.version.to_owned(),
root: version_root,
executable: exposed,
files,
})
}
pub fn recover(software: &Software, root: &Path) -> Result<Vec<String>> {
if !records::leaf(software.command) {
return Err(records::refuse());
}
if require_idle(software, root).is_ok() {
return Ok(Vec::new());
}
Writer::acquire(root)?.recover(software)
}
fn recover_locked(software: &Software, root: &Path) -> Result<Vec<String>> {
let installation = staging::Staging::load(root, software.command)?;
let removal = removal::Removal::load(root, software.command)?;
let switch = switch::Switch::load(root, software.command)?;
if usize::from(installation.is_some())
+ usize::from(removal.is_some())
+ usize::from(switch.is_some())
> 1
{
return Err(records::refuse());
}
if let Some(switch) = switch {
let version = switch.version().to_owned();
switch.complete()?;
return Ok(vec![format!(
"completed the recorded launch switch to {version}"
)]);
}
if let Some(removal) = removal {
let version = removal.version().to_owned();
removal.complete()?;
return Ok(vec![format!("completed the recorded removal of {version}")]);
}
let Some(transaction) = installation else {
require_idle(software, root)?;
return Ok(Vec::new());
};
let version = transaction.version().to_owned();
if transaction.recover()? {
transaction.expose()?;
transaction.complete()?;
Ok(vec![format!(
"completed the recorded installation of {version}"
)])
} else {
Ok(vec![format!(
"discarded the recorded incomplete installation of {version}; the previous tree is intact"
)])
}
}
pub fn require_idle(software: &Software, root: &Path) -> Result<()> {
require_idle_for(software, root, None)
}
fn require_idle_for(
software: &Software,
root: &Path,
operation: Option<&operation::Binding>,
) -> Result<()> {
if !records::leaf(software.command) {
return Err(records::refuse());
}
if let Some(root) = records::optional_root(root)?
&& if operation.is_none() {
store::pending(&root)?
} else {
store::pending_for(&root, operation)?
}
{
return Err(Error::new(
ReasonCode::RecoveryRequired,
"a software operation is pending; a new plan cannot recover or replace its recorded operation",
));
}
Ok(())
}
pub fn remove(software: &Software, root: &Path) -> Result<bool> {
require_idle(software, root)?;
if fs::symlink_metadata(root).is_err_and(|error| error.kind() == std::io::ErrorKind::NotFound) {
return Ok(false);
}
Writer::acquire(root)?.remove(software)
}
fn remove_locked(software: &Software, root: &Path) -> Result<bool> {
require_idle(software, root)?;
let Some(removal) = removal::Removal::begin(root, software)? else {
return Ok(false);
};
removal.complete()?;
Ok(true)
}
pub fn rollback(software: &Software, root: &Path, to: &str) -> Result<Installed> {
require_idle(software, root)?;
Writer::acquire(root)?.rollback(software, to)
}
fn rollback_locked(software: &Software, root: &Path, to: &str) -> Result<Installed> {
let present = Present::under_named(root, software.command, software.member_here());
if !present.versions.iter().any(|found| found == to) {
return Err(Error::new(
ReasonCode::InvalidTarget,
if present.versions.is_empty() {
format!(
"{} holds no installed version of {}",
root.display(),
software.command
)
} else {
format!(
"{to} is not installed under {}; it holds {}",
root.display(),
present.versions.join(", ")
)
},
));
}
let switch = switch::Switch::begin(root, software.command, to)?;
let exposed = switch.executable();
switch.complete()?;
Ok(Installed {
version: to.to_owned(),
root: root.join(to),
executable: exposed,
files: 0,
})
}
#[must_use]
pub fn exposed_name(command: &str, member: &str) -> String {
exposed_name_on(command, member, cfg!(windows))
}
#[must_use]
pub fn exposed_name_on(command: &str, member: &str, windows: bool) -> String {
match member_kind(member, windows) {
MemberKind::JavaScript | MemberKind::CommandScript => format!("{command}.cmd"),
MemberKind::Native => command.to_owned(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemberKind {
Native,
JavaScript,
CommandScript,
}
#[must_use]
pub fn member_kind(member: &str, windows: bool) -> MemberKind {
if !windows {
return MemberKind::Native;
}
match Path::new(member)
.extension()
.map(|kind| kind.to_string_lossy().to_ascii_lowercase())
.as_deref()
{
Some("js") => MemberKind::JavaScript,
Some("cmd" | "bat") => MemberKind::CommandScript,
_ => MemberKind::Native,
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Manifest {
pub schema_version: u32,
pub version: String,
pub executable: String,
pub executable_sha256: String,
}
impl Manifest {
#[must_use]
pub fn path(root: &Path, command: &str) -> PathBuf {
root.join("bin").join(format!(".{command}.manifest.json"))
}
#[must_use]
pub fn inspect(path: &Path, command: &str) -> ManifestState {
let read = || -> Result<Option<Vec<u8>>> {
if !records::leaf(command) {
return Err(records::refuse());
}
let root = match records::open_root(path) {
Ok(root) => root,
Err(error) => match fs::symlink_metadata(path) {
Err(missing) if missing.kind() == std::io::ErrorKind::NotFound => {
return Ok(None);
}
_ => return Err(error),
},
};
let Some(bin) = records::present(&root, "bin")? else {
return Ok(None);
};
records::read_bytes(&bin, &format!(".{command}.manifest.json"), 16 * 1024)
};
match read() {
Ok(None) => ManifestState::Missing,
Err(_) => ManifestState::Unreadable,
Ok(Some(raw)) => match serde_json::from_slice::<Self>(&raw) {
Err(_) => ManifestState::Malformed,
Ok(found) if found.schema_version != 1 => ManifestState::Unsupported {
schema_version: found.schema_version,
},
Ok(found) => ManifestState::Present(found),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ManifestState {
Missing,
Unreadable,
Malformed,
Unsupported {
schema_version: u32,
},
Present(Manifest),
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::panic)]
use super::*;
use crate::setup_core::archive::build::{Dialect, Item, gzip_tar};
const CODEX_MEMBER: &str = "package/vendor/x86_64-unknown-linux-musl/bin/codex";
const ARTIFACTS: &[Artifact] = &[
Artifact {
platform: "linux/x86_64",
url: "https://example.invalid/linux-x86_64.tgz",
bytes: 0,
sha256: "sha256:0",
shape: Shape::GzipTar,
member: CODEX_MEMBER,
},
Artifact {
platform: "linux/arm64",
url: "https://example.invalid/linux-arm64.tgz",
bytes: 0,
sha256: "sha256:0",
shape: Shape::GzipTar,
member: CODEX_MEMBER,
},
];
fn software() -> Software {
Software {
version: "1.2.3",
command: "codex",
delivery: Delivery::Artifacts(ARTIFACTS),
unsupported: &["windows/x86_64"],
previous: None,
}
}
const EARLIER_ARTIFACTS: &[Artifact] = &[
Artifact {
platform: "linux/x86_64",
url: "https://example.invalid/linux-x86_64-1.2.2.tgz",
bytes: 0,
sha256: "sha256:earlier",
shape: Shape::GzipTar,
member: CODEX_MEMBER,
},
Artifact {
platform: "linux/arm64",
url: "https://example.invalid/linux-arm64-1.2.2.tgz",
bytes: 0,
sha256: "sha256:earlier",
shape: Shape::GzipTar,
member: CODEX_MEMBER,
},
];
fn bumped() -> Software {
Software {
previous: Some(Previous {
version: "1.2.2",
artifacts: EARLIER_ARTIFACTS,
}),
..software()
}
}
const MIXED_ARTIFACTS: &[Artifact] = &[
Artifact {
platform: "linux/arm64",
url: "https://example.invalid/linux-arm64.tgz",
bytes: 0,
sha256: "sha256:0",
shape: Shape::GzipTar,
member: "dist-package/cursor-agent",
},
Artifact {
platform: "windows/x86_64",
url: "https://example.invalid/windows-x86_64.zip",
bytes: 0,
sha256: "sha256:0",
shape: Shape::Zip,
member: "dist-package/cursor-agent.cmd",
},
];
#[test]
fn the_windows_entry_point_names_the_member_windows_actually_gets() {
let sw = Software {
command: "agent",
delivery: Delivery::Artifacts(MIXED_ARTIFACTS),
..software()
};
let planned = exposed_name_on(sw.command, sw.member_on("windows", "x86_64"), true);
let written = exposed_name_on(
sw.command,
sw.artifact_for("windows", "x86_64").unwrap().member,
true,
);
assert_eq!(planned, written, "the plan and the apply name one file");
assert_eq!(planned, "agent.cmd");
assert_eq!(exposed_name_on(sw.command, sw.member_hint(), true), "agent");
}
#[test]
fn a_platform_without_an_artifact_falls_back_to_the_hint() {
let sw = software();
assert_eq!(sw.member_on("windows", "x86_64"), sw.member_hint());
}
#[test]
fn a_build_with_no_second_pin_names_one_version_and_refuses_the_rest() {
let only = software();
assert_eq!(only.versions(), vec!["1.2.3"]);
assert!(only.at(None).is_some());
assert!(only.at(Some("1.2.3")).is_some());
assert!(only.at(Some("1.2.2")).is_none());
}
#[test]
fn naming_the_earlier_version_selects_the_earlier_bytes() {
let both = bumped();
assert_eq!(both.versions(), vec!["1.2.3", "1.2.2"]);
let earlier = both.at(Some("1.2.2")).unwrap();
assert_eq!(earlier.version, "1.2.2");
assert_eq!(
earlier.artifact_for("linux", "x86_64").unwrap().url,
"https://example.invalid/linux-x86_64-1.2.2.tgz"
);
let current = both.at(None).unwrap();
assert_eq!(current.version, "1.2.3");
assert_eq!(
current.artifact_for("linux", "x86_64").unwrap().url,
"https://example.invalid/linux-x86_64.tgz"
);
}
#[test]
fn the_release_a_file_belongs_to_is_read_from_its_digest() {
let both = bumped();
assert_eq!(
both.for_bytes("linux", "x86_64", "sha256:0")
.map(|found| found.version),
Some("1.2.3")
);
assert_eq!(
both.for_bytes("linux", "x86_64", "sha256:earlier")
.map(|found| found.version),
Some("1.2.2")
);
assert!(
both.for_bytes("linux", "x86_64", "sha256:someone-elses")
.is_none()
);
assert!(both.for_bytes("windows", "x86_64", "sha256:0").is_none());
}
#[test]
fn rollback_points_the_command_at_a_version_already_on_disk() {
let (at, artifact) = staged("rollback", b"#!/bin/sh\necho new\n", CODEX_MEMBER);
let root = at.join("prefix");
let previous = Software {
version: "1.2.2",
..software()
};
install(&previous, &artifact, &at.join("artifact.tgz"), &root).unwrap();
let installed = install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
assert_eq!(installed.version, "1.2.3");
let entry = root.join("bin/codex");
let retained = at.join("retained-launcher");
fs::rename(&entry, &retained).unwrap();
fs::write(&entry, b"foreign launcher").unwrap();
assert!(rollback(&software(), &root, "1.2.2").is_err());
assert_eq!(fs::read(&entry).unwrap(), b"foreign launcher");
fs::remove_file(&entry).unwrap();
fs::rename(retained, entry).unwrap();
let older = root.join("1.2.2").join(CODEX_MEMBER);
fs::write(&older, b"changed retained version").unwrap();
assert!(rollback(&software(), &root, "1.2.2").is_err());
assert_eq!(fs::read(&older).unwrap(), b"changed retained version");
fs::write(older, b"#!/bin/sh\necho new\n").unwrap();
let rolled = rollback(&software(), &root, "1.2.2").unwrap();
assert_eq!(rolled.version, "1.2.2");
let present = Present::under_named(&root, "codex", CODEX_MEMBER);
assert_eq!(present.exposed.as_deref(), Some("1.2.2"));
assert_eq!(present.versions, vec!["1.2.2", "1.2.3"]);
assert!(root.join("1.2.3").join(CODEX_MEMBER).is_file());
assert_eq!(
rollback(&software(), &root, "1.2.3").unwrap().version,
"1.2.3"
);
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
drop(switch::Switch::begin(&root, "codex", "1.2.2").unwrap());
assert!(rollback(&software(), &root, "1.2.3").is_err());
assert!(remove(&software(), &root).is_err());
assert!(install(&software(), &artifact, &at.join("artifact.tgz"), &root).is_err());
let limit = crate::setup_core::software_prefix::INVENTORY_LIMIT;
let directory = records::open_root(&root).unwrap();
let receipt = store::Key::Receipt {
command: "codex".into(),
version: "1.2.2".into(),
};
let original: serde_json::Value =
store::read(&directory, &receipt, limit).unwrap().unwrap();
let mut changed = original.clone();
changed["artifact_sha256"] = format!("sha256:{}", "0".repeat(64)).into();
store::write(&directory, &receipt, &changed, limit).unwrap();
assert!(recover(&software(), &root).is_err());
store::write(&directory, &receipt, &original, limit).unwrap();
let marker = root.join("bin/.codex.version");
fs::write(&marker, b"foreign marker").unwrap();
let before = crate::setup_core::software_prefix::observe(&root, ".codex-setup-system")
.unwrap()
.digest;
assert!(recover(&software(), &root).is_err());
assert_eq!(
crate::setup_core::software_prefix::observe(&root, ".codex-setup-system")
.unwrap()
.digest,
before
);
fs::write(&marker, b"1.2.2").unwrap();
assert!(recover(&software(), &root).is_err());
fs::write(marker, b"1.2.3").unwrap();
assert_eq!(recover(&software(), &root).unwrap().len(), 1);
assert!(recover(&software(), &root).unwrap().is_empty());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.2")
);
assert!(root.join("1.2.3").join(CODEX_MEMBER).is_file());
assert!(
!store::exists(
&records::open_root(&root).unwrap(),
&switch::journal("codex")
)
.unwrap()
);
drop(directory);
fs::remove_dir_all(at).unwrap();
}
#[test]
fn removing_an_inactive_version_leaves_the_active_one_exposed() {
let (at, artifact) = staged("remove-inactive", b"#!/bin/sh\necho new\n", CODEX_MEMBER);
let root = at.join("prefix");
let previous = Software {
version: "1.2.2",
..software()
};
install(&previous, &artifact, &at.join("artifact.tgz"), &root).unwrap();
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
rollback(&software(), &root, "1.2.2").unwrap();
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.2"),
"the rollback did not take"
);
assert!(remove(&software(), &root).unwrap());
assert!(
!root.join("1.2.3").exists(),
"the removed version's tree is still here"
);
assert!(
root.join("1.2.2").join(CODEX_MEMBER).is_file(),
"removing one version took another version's files"
);
let after = Present::under_named(&root, "codex", CODEX_MEMBER);
assert_eq!(
after.exposed.as_deref(),
Some("1.2.2"),
"removing an inactive version took the command that was running the active one"
);
assert_eq!(after.versions, vec!["1.2.2"]);
}
#[test]
fn a_reinstall_that_fails_leaves_the_installation_that_was_working() {
let (at, artifact) = staged("reinstall-fails", b"#!/bin/sh\necho good\n", CODEX_MEMBER);
let root = at.join("prefix");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let good = fs::read(root.join("1.2.3").join(CODEX_MEMBER)).unwrap();
let raw = gzip_tar(
&[
Item::directory("package"),
Item::file("package/README.md", b"no executable here", 0o644),
],
Dialect::Gnu,
);
let broken_at = at.join("broken.tgz");
fs::write(&broken_at, &raw).unwrap();
let broken = Artifact {
bytes: raw.len() as u64,
sha256: Box::leak(digest::of_bytes(&raw).into_boxed_str()),
..artifact
};
let refused = install(&software(), &broken, &broken_at, &root).unwrap_err();
assert_eq!(
refused.reason(),
ReasonCode::IntegrityMismatch,
"the refusal is not the one this test drives: {refused:?}"
);
assert_eq!(
fs::read(root.join("1.2.3").join(CODEX_MEMBER))
.ok()
.as_deref(),
Some(good.as_slice()),
"a failed reinstall destroyed the installation that was working"
);
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3"),
"the exposed command no longer names an installed version"
);
}
#[test]
fn a_command_script_member_keeps_an_extension_windows_can_run() {
assert_eq!(
exposed_name_on("agent", "dist-package/cursor-agent", true),
"agent"
);
assert_eq!(
exposed_name_on("agent", "dist-package/cursor-agent", false),
"agent"
);
assert_eq!(
exposed_name_on("pi", "package/dist/bundle/cli.js", true),
"pi.cmd"
);
assert_eq!(
exposed_name_on("pi", "package/dist/bundle/cli.js", false),
"pi"
);
assert_eq!(
exposed_name_on("agent", "dist-package/cursor-agent.cmd", true),
"agent.cmd",
"a .cmd member was exposed as a name Windows cannot run"
);
assert_eq!(
exposed_name_on("agent", "dist-package/cursor-agent.cmd", false),
"agent"
);
assert_eq!(
exposed_name_on("codex", "package/bin/codex.exe", true),
"codex"
);
assert_eq!(
exposed_name_on("codex", "package/bin/codex.exe", false),
"codex"
);
}
#[test]
fn a_marker_truncated_onto_a_sibling_version_is_not_believed() {
let (at, artifact) = staged("truncated-marker", b"#!/bin/sh\necho new\n", CODEX_MEMBER);
let root = at.join("prefix");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let sibling = root.join("1.2");
fs::create_dir_all(sibling.join("package/vendor/x86_64-unknown-linux-musl/bin")).unwrap();
fs::write(sibling.join(CODEX_MEMBER), b"#!/bin/sh\necho sibling\n").unwrap();
fs::write(root.join("bin").join(".codex.version"), "1.2").unwrap();
let read = Present::under_named(&root, "codex", CODEX_MEMBER);
assert_eq!(
read.exposed.as_deref(),
Some("1.2"),
"this test drives the state; if it stops reproducing, say why here"
);
let marker = root.join("bin").join(".codex.version");
fs::write(&marker, "1.2.3").unwrap();
let staging = marker.with_extension("version.incoming");
fs::write(&staging, b"unrelated temporary file").unwrap();
let predecessor = launch::Snapshot::prepare(&root, "codex", CODEX_MEMBER).unwrap();
let manifest = Manifest::path(&root, "codex");
fs::remove_file(&manifest).unwrap();
fs::create_dir(&manifest).unwrap(); let refused = expose(
&root.join("1.2").join(CODEX_MEMBER),
&root.join("bin").join("codex"),
"1.2",
"codex",
&predecessor,
);
assert!(
refused.is_err(),
"the marker write did not stage: {refused:?}"
);
assert_eq!(
fs::read_to_string(&marker).unwrap(),
"1.2.3",
"a failed marker write replaced the marker that was there"
);
assert_eq!(fs::read(&staging).unwrap(), b"unrelated temporary file");
assert_eq!(
fs::read(root.join("bin/codex")).unwrap(),
b"#!/bin/sh\necho new\n"
);
}
#[test]
fn a_javascript_entry_point_is_exposed_as_something_the_platform_can_run() {
assert_eq!(exposed_name("codex", CODEX_MEMBER), "codex");
assert_eq!(exposed_name("grok", ""), "grok");
let js = "package/dist/bundle/cli.js";
assert_eq!(exposed_name_on("pi", js, true), "pi.cmd");
assert_eq!(exposed_name_on("pi", js, false), "pi");
assert_eq!(exposed_name_on("codex", CODEX_MEMBER, true), "codex");
assert_eq!(exposed_name_on("codex", CODEX_MEMBER, false), "codex");
assert_eq!(
exposed_name("pi", js),
exposed_name_on("pi", js, cfg!(windows))
);
}
#[test]
fn a_windows_shaped_prefix_is_read_back_by_the_member_and_not_by_the_command() {
let member = "package/dist/bundle/cli.js";
let at = scratch("windows-readback");
let root = at.join("prefix");
fs::create_dir_all(root.join("0.84.4").join("package/dist/bundle")).unwrap();
fs::write(root.join("0.84.4").join(member), b"// the bundle").unwrap();
fs::create_dir_all(root.join("bin")).unwrap();
let launcher = exposed_name_on("pi", member, true);
assert_eq!(launcher, "pi.cmd");
fs::write(root.join("bin").join(&launcher), b"@echo off\r\n").unwrap();
fs::write(root.join("bin").join(".pi.version"), b"0.84.4\n").unwrap();
assert_eq!(
Present::under_on(&root, "pi", member, true)
.exposed
.as_deref(),
Some("0.84.4"),
"the reading told the member did not find the launcher beside it"
);
assert_eq!(
Present::under_on(&root, "pi", "", true).exposed,
None,
"a member-blind reading found something, so this test proves nothing"
);
assert_eq!(
Present::under_on(&root, "pi", "", true).versions,
vec!["0.84.4".to_owned()]
);
}
#[test]
#[cfg(unix)]
fn a_javascript_program_installs_from_one_archive_and_runs() {
let member = "package/dist/bundle/cli.js";
let (at, artifact) = staged(
"js-entry",
b"#!/usr/bin/env sh\necho 'js-stand-in 9.9.9'\n",
member,
);
let software = Software {
version: "9.9.9",
command: "jsprog",
delivery: Delivery::Artifacts(&[]),
unsupported: &[],
previous: None,
};
let root = at.join("prefix");
let installed = install(&software, &artifact, &at.join("artifact.tgz"), &root).unwrap();
assert_eq!(
installed.executable,
root.join("bin").join(exposed_name("jsprog", member))
);
assert_eq!(
fs::canonicalize(&installed.executable).unwrap(),
fs::canonicalize(root.join("9.9.9").join(member)).unwrap()
);
assert_eq!(
Present::under_named(&root, "jsprog", member)
.exposed
.as_deref(),
Some("9.9.9")
);
}
#[test]
fn the_exposed_version_is_readable_without_a_link_to_resolve() {
let (at, artifact) = staged("exposed-marker", b"#!/bin/sh\necho hi\n", CODEX_MEMBER);
let root = at.join("prefix");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
let exposed = root.join("bin").join("codex");
let bytes = fs::read(root.join("1.2.3").join(CODEX_MEMBER)).unwrap();
fs::remove_file(&exposed).unwrap();
fs::write(&exposed, &bytes).unwrap();
assert!(!exposed.symlink_metadata().unwrap().is_symlink());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3"),
"the exposed version was unreadable without a link to resolve"
);
fs::write(root.join("bin").join(".codex.version"), "9.9.9").unwrap();
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER).exposed,
None
);
fs::write(root.join("bin").join(".codex.version"), "1.2.3").unwrap();
remove(&software(), &root).unwrap();
assert!(!root.join("bin").join(".codex.version").exists());
}
#[test]
fn rollback_to_a_version_that_is_not_installed_names_the_ones_that_are() {
let (at, artifact) = staged("rollback-missing", b"x", CODEX_MEMBER);
let root = at.join("prefix");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let error = rollback(&software(), &root, "9.9.9").unwrap_err();
assert!(error.detail().contains("9.9.9"), "{}", error.detail());
assert!(error.detail().contains("1.2.3"), "{}", error.detail());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
}
#[test]
fn rollback_refuses_an_unrecorded_version_without_changing_the_launcher() {
let (at, artifact) = staged("rollback-empty", b"x", CODEX_MEMBER);
let root = at.join("prefix");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
fs::create_dir_all(root.join("1.2.2")).unwrap();
let error = rollback(&software(), &root, "1.2.2").unwrap_err();
assert_eq!(error.reason(), ReasonCode::RecoveryRequired);
assert!(root.join("1.2.2").is_dir());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
}
fn scratch(name: &str) -> PathBuf {
let path =
std::env::temp_dir().join(format!("setup-core-software-{name}-{}", std::process::id()));
let _ = fs::remove_dir_all(&path);
path
}
fn staged(name: &str, body: &[u8], member: &'static str) -> (PathBuf, Artifact) {
let raw = gzip_tar(
&[
Item::directory("package"),
Item::file(member, body, 0o755),
Item::file("package/README.md", b"read me", 0o644),
],
Dialect::Gnu,
);
let at = scratch(name);
fs::create_dir_all(&at).unwrap();
let file = at.join("artifact.tgz");
fs::write(&file, &raw).unwrap();
let artifact = Artifact {
platform: "linux/x86_64",
url: "https://example.invalid/artifact.tgz",
bytes: raw.len() as u64,
sha256: Box::leak(digest::of_bytes(&raw).into_boxed_str()),
shape: Shape::GzipTar,
member,
};
(at, artifact)
}
#[test]
fn the_artifact_for_this_platform_is_the_one_named_for_it() {
let found = software().artifact_for("linux", "x86_64").unwrap();
assert_eq!(found.url, "https://example.invalid/linux-x86_64.tgz");
}
#[test]
fn a_system_the_vendor_does_not_build_for_is_an_unsupported_platform() {
let error = software().artifact_for("windows", "x86_64").unwrap_err();
assert_eq!(error.reason(), ReasonCode::UnsupportedPlatform);
assert!(
error.detail().contains("linux/x86_64"),
"{}",
error.detail()
);
}
#[test]
fn a_machine_the_vendor_does_not_build_for_is_an_unsupported_architecture() {
let error = software().artifact_for("linux", "riscv64").unwrap_err();
assert_eq!(error.reason(), ReasonCode::UnsupportedArchitecture);
}
#[test]
fn a_product_delivered_by_a_package_manager_says_so_rather_than_pretending() {
let pi = Software {
version: "0.84.3",
command: "pi",
delivery: Delivery::Manager {
tool: "npm",
reason: "its dependency closure is resolved at install time",
},
unsupported: &[],
previous: None,
};
let error = pi.artifact_for("linux", "x86_64").unwrap_err();
assert_eq!(error.reason(), ReasonCode::UnsupportedOperation);
assert!(error.detail().contains("npm"), "{}", error.detail());
}
#[test]
fn an_archive_installs_and_exposes_one_stable_command() {
let (at, artifact) = staged("install", b"#!/bin/sh\necho hi\n", CODEX_MEMBER);
let root = at.join("software");
let path = at.join("artifact.tgz");
let source = VerifiedArtifact::open(&artifact, &path).unwrap();
fs::rename(&path, at.join("retained.tgz")).unwrap();
fs::write(&path, b"a different file at the original name").unwrap();
let installed = install_verified(&software(), &artifact, source, &root).unwrap();
assert_eq!(installed.version, "1.2.3");
assert_eq!(installed.files, 3);
assert_eq!(installed.executable, root.join("bin/codex"));
assert!(root.join("1.2.3").join(CODEX_MEMBER).is_file());
assert!(root.join("1.2.3/package/README.md").is_file());
assert_eq!(
fs::read(&installed.executable).unwrap(),
b"#!/bin/sh\necho hi\n"
);
fs::remove_dir_all(&at).unwrap();
}
fn admission_preserves_foreign_paths(root: &Path, writer: &Writer, intent: &operation::Intent) {
fs::write(root.join("foreign"), b"preserve unrelated bytes").unwrap();
assert!(writer.resume_operation(intent).is_err());
assert_eq!(
fs::read(root.join("foreign")).unwrap(),
b"preserve unrelated bytes"
);
fs::remove_file(root.join("foreign")).unwrap();
fs::create_dir(root.join("bin")).unwrap();
assert_eq!(writer.resume_operation(intent).unwrap(), None);
fs::write(root.join("bin/foreign"), b"not a planned launch entry").unwrap();
assert!(writer.resume_operation(intent).is_err());
assert!(root.join("bin/foreign").exists());
fs::remove_file(root.join("bin/foreign")).unwrap();
}
#[test]
fn caller_intent_survives_preparation_and_returns_history_without_replaying_effects() {
use operation::{Intent, Kind, Outcome, State};
let (at, artifact) = staged("caller-intent", b"generated program", CODEX_MEMBER);
let root = at.join("software");
fs::create_dir(&root).unwrap();
let declared = software();
let control = ".codex-setup-system";
let expected = crate::setup_core::software_prefix::observe(&root, control)
.unwrap()
.digest;
let writer = Writer::acquire(&root).unwrap();
let intent = Intent::new(
"original-request",
&digest::of_bytes(b"complete original plan"),
&declared,
Kind::Install,
)
.unwrap();
assert_eq!(writer.operation_state(&intent).unwrap(), None);
writer
.admit_operation(&intent, &declared, control, &expected)
.unwrap();
assert!(!root.join("bin").exists());
assert_eq!(
writer.operation_state(&intent).unwrap(),
Some(State::Pending)
);
let conflict = Intent::new(
"original-request",
&digest::of_bytes(b"different intent"),
&declared,
Kind::Install,
)
.unwrap();
assert!(writer.operation_state(&conflict).is_err());
assert!(writer.remove(&declared).is_err());
assert!(writer.recover(&declared).is_err());
admission_preserves_foreign_paths(&root, &writer, &intent);
let stage = staging::Staging::begin_for(
&root,
declared.command,
declared.version,
CODEX_MEMBER,
artifact.sha256,
Some(&intent.binding),
)
.unwrap();
let incoming = stage.stage_path();
fs::write(incoming.join("partial"), b"incomplete generated bytes").unwrap();
drop(stage);
assert_eq!(writer.resume_operation(&intent).unwrap(), None);
assert!(!incoming.exists());
let input = VerifiedArtifact::open(&artifact, &at.join("artifact.tgz")).unwrap();
let original = writer
.install_operation(&intent, &declared, &artifact, input)
.unwrap();
assert_eq!(original, Outcome::Installed { files: 3 });
drop(writer);
let writer = Writer::acquire(&root).unwrap();
assert_eq!(
writer.operation_state(&intent).unwrap(),
Some(State::Completed(original.clone()))
);
fs::remove_file(at.join("artifact.tgz")).unwrap();
let removal = Intent::new(
"remove-request",
&digest::of_bytes(b"later removal plan"),
&declared,
Kind::Remove,
)
.unwrap();
let expected = crate::setup_core::software_prefix::observe(&root, control)
.unwrap()
.digest;
writer
.admit_operation(&removal, &declared, control, &expected)
.unwrap();
assert_eq!(
writer.remove_operation(&removal, &declared).unwrap(),
Outcome::Removed { removed: true }
);
let after = crate::setup_core::software_prefix::observe(&root, control)
.unwrap()
.digest;
assert_eq!(writer.resume_operation(&intent).unwrap(), Some(original));
assert_eq!(
writer.resume_operation(&removal).unwrap(),
Some(Outcome::Removed { removed: true })
);
assert_eq!(
crate::setup_core::software_prefix::observe(&root, control)
.unwrap()
.digest,
after
);
assert!(!root.join(declared.version).exists());
assert!(writer.operation_state(&conflict).is_err());
drop(writer);
fs::remove_dir_all(at).unwrap();
}
#[test]
fn held_input_checks_seeked_blocks_before_partial_bytes_can_be_promoted() {
use std::io::{Read, Seek, SeekFrom};
let (at, artifact) = staged("held-input", b"initial", CODEX_MEMBER);
let path = at.join("artifact.tgz");
let root = at.join("software");
let bytes: Vec<u8> = (0_u8..251).cycle().take(150_001).collect();
fs::write(&path, &bytes).unwrap();
let artifact = Artifact {
bytes: bytes.len() as u64,
sha256: Box::leak(digest::of_bytes(&bytes).into_boxed_str()),
shape: Shape::Raw,
member: "",
..artifact
};
let mut source = VerifiedArtifact::open(&artifact, &path).unwrap();
source.reader().seek(SeekFrom::Start(65_532)).unwrap();
let mut crossing = [0; 16];
source.reader().read_exact(&mut crossing).unwrap();
assert_eq!(&crossing, &bytes[65_532..65_548]);
source.reader().seek(SeekFrom::End(-16)).unwrap();
source.reader().read_exact(&mut crossing).unwrap();
assert_eq!(&crossing, &bytes[bytes.len() - 16..]);
assert!(source.reader().seek(SeekFrom::Current(-200_000)).is_err());
let installed = install_verified(&software(), &artifact, source, &root).unwrap();
assert_eq!(fs::read(&installed.executable).unwrap(), bytes);
let source = VerifiedArtifact::open(&artifact, &path).unwrap();
let modified = fs::metadata(&path).unwrap().modified().unwrap();
let mut changed = bytes.clone();
changed[70_000] ^= 1;
fs::write(&path, changed).unwrap();
fs::File::options()
.write(true)
.open(&path)
.unwrap()
.set_times(fs::FileTimes::new().set_modified(modified))
.unwrap();
assert_eq!(fs::metadata(&path).unwrap().modified().unwrap(), modified);
let error = install_verified(&software(), &artifact, source, &root).unwrap_err();
assert_eq!(error.reason(), ReasonCode::StateUnavailable);
assert_eq!(fs::read(&installed.executable).unwrap(), bytes);
assert_eq!(
fs::metadata(
staging::Staging::load(&root, "codex")
.unwrap()
.unwrap()
.stage_path()
.join("codex")
)
.unwrap()
.len(),
65_536
);
assert_eq!(recover(&software(), &root).unwrap().len(), 1);
assert!(remove(&software(), &root).unwrap());
assert!(!root.join("1.2.3").exists());
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn reinstallation_preserves_modified_trees_and_replaces_unchanged_ones() {
let (at, artifact) = staged("twice", b"first", CODEX_MEMBER);
let root = at.join("software");
fs::create_dir_all(root.join("bin")).unwrap();
let entry = root.join("bin/codex");
fs::write(&entry, b"foreign launcher").unwrap();
assert!(install(&software(), &artifact, &at.join("artifact.tgz"), &root).is_err());
assert_eq!(fs::read(&entry).unwrap(), b"foreign launcher");
assert!(!root.join("1.2.3").exists());
assert!(
!store::exists(
&records::open_root(&root).unwrap(),
&store::Key::Installation("codex".into())
)
.unwrap()
);
fs::remove_file(&entry).unwrap();
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let stray = root.join("1.2.3/package/left-over");
fs::write(&stray, b"unrecorded content").unwrap();
assert!(install(&software(), &artifact, &at.join("artifact.tgz"), &root).is_err());
assert_eq!(fs::read(&stray).unwrap(), b"unrecorded content");
fs::remove_file(&stray).unwrap();
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
assert_eq!(fs::read(root.join("bin/codex")).unwrap(), b"first");
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn bytes_that_are_not_the_ones_the_plan_named_are_refused() {
let (at, mut artifact) = staged("digest", b"payload", CODEX_MEMBER);
artifact.sha256 = "sha256:0000000000000000000000000000000000000000000000000000000000000000";
let error = install(
&software(),
&artifact,
&at.join("artifact.tgz"),
&at.join("s"),
)
.unwrap_err();
assert_eq!(error.reason(), ReasonCode::IntegrityMismatch);
assert!(error.detail().contains("hashes to"), "{}", error.detail());
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn a_truncated_download_is_named_as_a_length_problem_not_a_digest_one() {
let (at, artifact) = staged("length", b"payload", CODEX_MEMBER);
let file = at.join("artifact.tgz");
let mut bytes = fs::read(&file).unwrap();
bytes.truncate(bytes.len() - 4);
fs::write(&file, &bytes).unwrap();
let error = install(&software(), &artifact, &file, &at.join("s")).unwrap_err();
assert!(
error.detail().contains("bytes; the plan named"),
"{}",
error.detail()
);
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn an_archive_without_the_member_the_plan_named_is_refused() {
let (at, mut artifact) = staged("member", b"payload", CODEX_MEMBER);
artifact.member = "package/vendor/somewhere-else/bin/codex";
let error = install(
&software(),
&artifact,
&at.join("artifact.tgz"),
&at.join("s"),
)
.unwrap_err();
assert!(
error.detail().contains("does not contain"),
"{}",
error.detail()
);
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn removing_takes_the_tree_and_the_exposed_command_with_it() {
let (at, artifact) = staged("remove", b"payload", CODEX_MEMBER);
let root = at.join("software");
let installed = install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
assert!(installed.executable.symlink_metadata().is_ok());
let retained_entry = at.join("retained-entry");
fs::rename(&installed.executable, &retained_entry).unwrap();
fs::write(&installed.executable, b"foreign launcher").unwrap();
assert!(remove(&software(), &root).is_err());
assert_eq!(
fs::read(&installed.executable).unwrap(),
b"foreign launcher"
);
assert!(root.join("1.2.3").is_dir());
fs::remove_file(&installed.executable).unwrap();
fs::rename(&retained_entry, &installed.executable).unwrap();
let marker = Present::marker(&root, "codex");
fs::write(&marker, b"another-version").unwrap();
assert!(remove(&software(), &root).is_err());
assert!(root.join("1.2.3").is_dir());
fs::write(&marker, b"1.2.3").unwrap();
let manifest = Manifest::path(&root, "codex");
let manifest_bytes = fs::read(&manifest).unwrap();
fs::write(&manifest, b"{").unwrap();
assert!(remove(&software(), &root).is_err());
assert!(root.join("1.2.3").is_dir());
fs::write(&manifest, &manifest_bytes).unwrap();
let linked_manifest = at.join("manifest-link");
fs::hard_link(&manifest, &linked_manifest).unwrap();
assert_eq!(Manifest::inspect(&root, "codex"), ManifestState::Unreadable);
assert!(remove(&software(), &root).is_err());
assert!(root.join("1.2.3").is_dir());
fs::remove_file(&linked_manifest).unwrap();
let foreign = root.join("1.2.3/personal-file");
fs::write(&foreign, b"preserve").unwrap();
assert!(remove(&software(), &root).is_err());
assert!(install(&software(), &artifact, &at.join("artifact.tgz"), &root).is_err());
assert_eq!(fs::read(&foreign).unwrap(), b"preserve");
assert!(installed.executable.symlink_metadata().is_ok());
fs::remove_file(&foreign).unwrap();
let directory = records::open_root(&root).unwrap();
let receipt = store::Key::Receipt {
command: "codex".into(),
version: "1.2.3".into(),
};
store::remove(&directory, &receipt).unwrap();
assert!(remove(&software(), &root).is_err());
assert!(root.join("1.2.3").is_dir());
fs::write(&foreign, b"preserve legacy data").unwrap();
assert!(install(&software(), &artifact, &at.join("artifact.tgz"), &root).is_err());
assert_eq!(fs::read(&foreign).unwrap(), b"preserve legacy data");
recover(&software(), &root).unwrap();
fs::remove_file(&foreign).unwrap();
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let saved: serde_json::Value = store::read(
&directory,
&receipt,
crate::setup_core::software_prefix::INVENTORY_LIMIT,
)
.unwrap()
.unwrap();
let mut record = saved.clone();
record["entries"].as_array_mut().unwrap().pop();
store::write(
&directory,
&receipt,
&record,
crate::setup_core::software_prefix::INVENTORY_LIMIT,
)
.unwrap();
assert!(remove(&software(), &root).is_err());
assert!(installed.executable.symlink_metadata().is_ok());
store::write(
&directory,
&receipt,
&saved,
crate::setup_core::software_prefix::INVENTORY_LIMIT,
)
.unwrap();
assert!(remove(&software(), &root).unwrap());
assert!(!store::exists(&directory, &receipt).unwrap());
assert!(!root.join("1.2.3").exists());
assert!(installed.executable.symlink_metadata().is_err());
assert!(!Present::marker(&root, "codex").exists());
assert!(!Manifest::path(&root, "codex").exists());
assert!(!remove(&software(), &root).unwrap());
drop(directory);
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn an_empty_or_absent_prefix_reads_as_holding_nothing() {
let nowhere = scratch("present-absent");
assert_eq!(
Present::under_named(&nowhere, "codex", CODEX_MEMBER),
Present::default()
);
}
#[test]
fn what_is_under_a_prefix_is_read_including_which_version_is_exposed() {
let (at, artifact) = staged("present-read", b"payload", CODEX_MEMBER);
let root = at.join("software");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
fs::create_dir_all(root.join("9.9.9")).unwrap();
let found = Present::under_named(&root, "codex", CODEX_MEMBER);
assert_eq!(found.versions, vec!["1.2.3".to_owned(), "9.9.9".to_owned()]);
assert!(found.holds("1.2.3"));
assert!(!found.holds("0.0.1"));
fs::remove_dir_all(&at).unwrap();
}
#[test]
#[cfg(unix)]
fn the_exposed_version_is_read_through_the_link_however_it_was_written() {
let (at, artifact) = staged("present-link", b"payload", CODEX_MEMBER);
let root = at.join("software");
install(&software(), &artifact, &at.join("artifact.tgz"), &root).unwrap();
let link = root.join("bin").join("codex");
assert!(fs::read_link(&link).unwrap().is_absolute());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
fs::remove_file(&link).unwrap();
std::os::unix::fs::symlink(
std::path::Path::new("..").join("1.2.3").join(CODEX_MEMBER),
&link,
)
.unwrap();
assert!(!fs::read_link(&link).unwrap().is_absolute());
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER)
.exposed
.as_deref(),
Some("1.2.3")
);
fs::remove_file(&link).unwrap();
std::os::unix::fs::symlink(root.join("9.9.9").join("codex"), &link).unwrap();
assert_eq!(
Present::under_named(&root, "codex", CODEX_MEMBER).exposed,
None
);
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn bin_and_the_control_directory_are_not_versions() {
let at = scratch("present-notversions");
fs::create_dir_all(at.join("bin")).unwrap();
fs::create_dir_all(at.join(".codex-setup-system")).unwrap();
fs::create_dir_all(at.join("1.2.3")).unwrap();
assert_eq!(
Present::under_named(&at, "codex", CODEX_MEMBER).versions,
vec!["1.2.3".to_owned()]
);
fs::remove_dir_all(&at).unwrap();
}
#[test]
fn the_inflation_limit_scales_with_the_artifact_but_never_below_a_floor() {
let small = Artifact {
bytes: 10,
..ARTIFACTS[0]
};
assert_eq!(small.limits().bytes, 64 * 1024 * 1024);
let large = Artifact {
bytes: 121_422_431,
..ARTIFACTS[0]
};
assert!(large.limits().bytes > 391_948_592);
}
}