use fd_lock::RwLock as FileRwLock; use flate2::read::GzDecoder; use reqwest::Client; use serde::de::DeserializeOwned; use sha2::Digest; use sha2::Sha256; use std::fs::File; use std::fs::OpenOptions; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::path::Component; use std::path::Path; use std::path::PathBuf; use std::time::Duration; use tar::Archive; use tempfile::tempdir_in; use thiserror::Error; use tokio::fs; use tokio::time::sleep; use url::Url; use zip::ZipArchive; const INSTALL_LOCK_POLL_INTERVAL: Duration = Duration::from_millis(50); #[derive(Clone, Debug, PartialEq, Eq)] pub struct PackageManagerConfig

{ codex_home: PathBuf, package: P, cache_root: Option, } impl

PackageManagerConfig

{ pub fn new(codex_home: PathBuf, package: P) -> Self { Self { codex_home, package, cache_root: None, } } pub fn with_cache_root(mut self, cache_root: PathBuf) -> Self { self.cache_root = Some(cache_root); self } pub fn codex_home(&self) -> &Path { &self.codex_home } pub fn package(&self) -> &P { &self.package } } impl PackageManagerConfig

{ pub fn cache_root(&self) -> PathBuf { self.cache_root.clone().unwrap_or_else(|| { self.codex_home.join( self.package .default_cache_root_relative() .replace('/', std::path::MAIN_SEPARATOR_STR), ) }) } } #[derive(Clone, Debug)] pub struct PackageManager

{ client: Client, config: PackageManagerConfig

, } impl

PackageManager

{ pub fn new(config: PackageManagerConfig

) -> Self { Self { client: Client::new(), config, } } pub fn with_client(config: PackageManagerConfig

, client: Client) -> Self { Self { client, config } } pub fn config(&self) -> &PackageManagerConfig

{ &self.config } } impl PackageManager

{ pub async fn resolve_cached(&self) -> Result, P::Error> { let platform = PackagePlatform::detect_current().map_err(P::Error::from)?; let install_dir = self .config .package() .install_dir(&self.config.cache_root(), platform); self.resolve_cached_at(platform, install_dir).await } async fn resolve_cached_at( &self, platform: PackagePlatform, install_dir: PathBuf, ) -> Result, P::Error> { if !fs::try_exists(&install_dir) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to read {}", install_dir.display()), source, }) .map_err(P::Error::from)? { return Ok(None); } let package = match self.config.package().load_installed(install_dir, platform) { Ok(package) => package, Err(_) => return Ok(None), }; if self.config.package().installed_version(&package) != self.config.package().version() { return Ok(None); } Ok(Some(package)) } pub async fn ensure_installed(&self) -> Result { if let Some(package) = self.resolve_cached().await? { return Ok(package); } let platform = PackagePlatform::detect_current().map_err(P::Error::from)?; let cache_root = self.config.cache_root(); let install_dir = self.config.package().install_dir(&cache_root, platform); if let Some(package) = self .resolve_cached_at(platform, install_dir.clone()) .await? { return Ok(package); } if let Some(parent) = install_dir.parent() { fs::create_dir_all(parent) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", parent.display()), source, }) .map_err(P::Error::from)?; } let lock_path = install_dir.with_extension("lock"); let lock_file = OpenOptions::new() .create(true) .read(true) .write(true) .truncate(false) .open(&lock_path) .map_err(|source| PackageManagerError::Io { context: format!("failed to open {}", lock_path.display()), source, }) .map_err(P::Error::from)?; let mut install_lock = FileRwLock::new(lock_file); let _install_guard = loop { match install_lock.try_write() { Ok(guard) => break guard, Err(source) if source.kind() == std::io::ErrorKind::WouldBlock => { sleep(INSTALL_LOCK_POLL_INTERVAL).await; } Err(source) => { return Err(PackageManagerError::Io { context: format!("failed to lock {}", lock_path.display()), source, } .into()); } } }; if let Some(package) = self .resolve_cached_at(platform, install_dir.clone()) .await? { return Ok(package); } let manifest = self.fetch_release_manifest().await?; if self.config.package().release_version(&manifest) != self.config.package().version() { return Err(PackageManagerError::UnexpectedPackageVersion { expected: self.config.package().version().to_string(), actual: self.config.package().release_version(&manifest).to_string(), } .into()); } fs::create_dir_all(&cache_root) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", cache_root.display()), source, }) .map_err(P::Error::from)?; let staging_root = cache_root.join(".staging"); fs::create_dir_all(&staging_root) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", staging_root.display()), source, }) .map_err(P::Error::from)?; let platform_archive = self .config .package() .platform_archive(&manifest, platform)?; let archive_url = self .config .package() .archive_url(&platform_archive) .map_err(P::Error::from)?; let archive_bytes = self.download_bytes(&archive_url).await?; verify_sha256(&archive_bytes, &platform_archive.sha256).map_err(P::Error::from)?; let staging_dir = tempdir_in(&staging_root) .map_err(|source| PackageManagerError::Io { context: format!( "failed to create staging directory in {}", staging_root.display() ), source, }) .map_err(P::Error::from)?; let archive_path = staging_dir.path().join(&platform_archive.archive); fs::write(&archive_path, &archive_bytes) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to write {}", archive_path.display()), source, }) .map_err(P::Error::from)?; let extraction_root = staging_dir.path().join("extract"); fs::create_dir_all(&extraction_root) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", extraction_root.display()), source, }) .map_err(P::Error::from)?; extract_archive(&archive_path, &extraction_root, platform_archive.format) .map_err(P::Error::from)?; let extracted_root = self .config .package() .detect_extracted_root(&extraction_root)?; let package = self .config .package() .load_installed(extracted_root.clone(), platform)?; if self.config.package().installed_version(&package) != self.config.package().version() { return Err(PackageManagerError::UnexpectedPackageVersion { expected: self.config.package().version().to_string(), actual: self .config .package() .installed_version(&package) .to_string(), } .into()); } if let Some(parent) = install_dir.parent() { fs::create_dir_all(parent) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", parent.display()), source, }) .map_err(P::Error::from)?; } let mut replaced_install_dir = None; if fs::try_exists(&install_dir) .await .map_err(|source| PackageManagerError::Io { context: format!("failed to read {}", install_dir.display()), source, }) .map_err(P::Error::from)? { let install_name = install_dir.file_name().ok_or_else(|| { PackageManagerError::ArchiveExtraction(format!( "install path `{}` has no terminal component", install_dir.display() )) })?; let install_name = install_name.to_string_lossy(); let mut suffix = 0u32; loop { let quarantined_path = install_dir.with_file_name(format!( ".{install_name}.replaced-{}-{suffix}", std::process::id() )); match fs::rename(&install_dir, &quarantined_path).await { Ok(()) => { replaced_install_dir = Some(quarantined_path); break; } Err(source) if source.kind() == std::io::ErrorKind::AlreadyExists => { suffix += 1; } Err(source) => { return Err(PackageManagerError::Io { context: format!( "failed to quarantine {} to {}", install_dir.display(), quarantined_path.display() ), source, } .into()); } } } } match fs::rename(&extracted_root, &install_dir).await { Ok(()) => {} Err(source) if matches!( source.kind(), std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::DirectoryNotEmpty ) => { if let Some(package) = self .resolve_cached_at(platform, install_dir.clone()) .await? { return Ok(package); } return Err(PackageManagerError::Io { context: format!( "failed to move {} to {}", extracted_root.display(), install_dir.display() ), source, } .into()); } Err(source) => { return Err(PackageManagerError::Io { context: format!( "failed to move {} to {}", extracted_root.display(), install_dir.display() ), source, } .into()); } } if let Some(replaced_install_dir) = replaced_install_dir { let _ = fs::remove_dir_all(replaced_install_dir).await; } self.config.package().load_installed(install_dir, platform) } async fn fetch_release_manifest(&self) -> Result { let manifest_url = self .config .package() .manifest_url() .map_err(P::Error::from)?; let response = self .client .get(manifest_url.clone()) .send() .await .map_err(|source| PackageManagerError::Http { context: format!("failed to fetch {manifest_url}"), source, }) .map_err(P::Error::from)? .error_for_status() .map_err(|source| PackageManagerError::Http { context: format!("manifest request failed for {manifest_url}"), source, }) .map_err(P::Error::from)?; response .json::() .await .map_err(|source| PackageManagerError::Http { context: format!("failed to decode manifest from {manifest_url}"), source, }) .map_err(P::Error::from) } async fn download_bytes(&self, url: &Url) -> Result, P::Error> { let response = self .client .get(url.clone()) .send() .await .map_err(|source| PackageManagerError::Http { context: format!("failed to download {url}"), source, }) .map_err(P::Error::from)? .error_for_status() .map_err(|source| PackageManagerError::Http { context: format!("archive request failed for {url}"), source, }) .map_err(P::Error::from)?; let bytes = response .bytes() .await .map_err(|source| PackageManagerError::Http { context: format!("failed to read response body for {url}"), source, }) .map_err(P::Error::from)?; Ok(bytes.to_vec()) } } pub trait ManagedPackage: Clone { type Error: From; type Installed: Clone; type ReleaseManifest: DeserializeOwned; fn default_cache_root_relative(&self) -> &str; fn version(&self) -> &str; fn manifest_url(&self) -> Result; fn archive_url(&self, archive: &PackageReleaseArchive) -> Result; fn release_version<'a>(&self, manifest: &'a Self::ReleaseManifest) -> &'a str; fn platform_archive( &self, manifest: &Self::ReleaseManifest, platform: PackagePlatform, ) -> Result; fn install_dir(&self, cache_root: &Path, platform: PackagePlatform) -> PathBuf; fn installed_version<'a>(&self, package: &'a Self::Installed) -> &'a str; fn load_installed( &self, root_dir: PathBuf, platform: PackagePlatform, ) -> Result; fn detect_extracted_root(&self, extraction_root: &Path) -> Result { detect_single_package_root(extraction_root).map_err(Self::Error::from) } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum PackagePlatform { DarwinArm64, DarwinX64, LinuxArm64, LinuxX64, WindowsArm64, WindowsX64, } impl PackagePlatform { pub fn detect_current() -> Result { match (std::env::consts::OS, std::env::consts::ARCH) { ("macos", "aarch64") | ("macos", "arm64") => Ok(Self::DarwinArm64), ("macos", "x86_64") => Ok(Self::DarwinX64), ("linux", "aarch64") | ("linux", "arm64") => Ok(Self::LinuxArm64), ("linux", "x86_64") => Ok(Self::LinuxX64), ("windows", "aarch64") | ("windows", "arm64") => Ok(Self::WindowsArm64), ("windows", "x86_64") => Ok(Self::WindowsX64), (os, arch) => Err(PackageManagerError::UnsupportedPlatform { os: os.to_string(), arch: arch.to_string(), }), } } pub fn as_str(self) -> &'static str { match self { Self::DarwinArm64 => "darwin-arm64", Self::DarwinX64 => "darwin-x64", Self::LinuxArm64 => "linux-arm64", Self::LinuxX64 => "linux-x64", Self::WindowsArm64 => "windows-arm64", Self::WindowsX64 => "windows-x64", } } } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub struct PackageReleaseArchive { pub archive: String, pub sha256: String, pub format: ArchiveFormat, pub size_bytes: Option, } #[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub enum ArchiveFormat { #[serde(rename = "zip")] Zip, #[serde(rename = "tar.gz")] TarGz, } #[derive(Debug, Error)] pub enum PackageManagerError { #[error("unsupported platform: {os}-{arch}")] UnsupportedPlatform { os: String, arch: String }, #[error("invalid release base url")] InvalidBaseUrl(#[source] url::ParseError), #[error("{context}")] Http { context: String, #[source] source: reqwest::Error, }, #[error("{context}")] Io { context: String, #[source] source: std::io::Error, }, #[error("missing platform entry `{0}` in release manifest")] MissingPlatform(String), #[error("unexpected package version: expected `{expected}`, got `{actual}`")] UnexpectedPackageVersion { expected: String, actual: String }, #[error("checksum mismatch: expected `{expected}`, got `{actual}`")] ChecksumMismatch { expected: String, actual: String }, #[error("archive extraction failed: {0}")] ArchiveExtraction(String), #[error("archive did not contain a package root with manifest.json under {0}")] MissingPackageRoot(PathBuf), } pub fn detect_single_package_root(extraction_root: &Path) -> Result { let direct_manifest = extraction_root.join("manifest.json"); if direct_manifest.exists() { return Ok(extraction_root.to_path_buf()); } let mut directory_candidates = Vec::new(); for entry in std::fs::read_dir(extraction_root).map_err(|source| PackageManagerError::Io { context: format!("failed to read {}", extraction_root.display()), source, })? { let entry = entry.map_err(|source| PackageManagerError::Io { context: format!("failed to read entry in {}", extraction_root.display()), source, })?; let path = entry.path(); if path.is_dir() { directory_candidates.push(path); } } if directory_candidates.len() == 1 { let candidate = &directory_candidates[0]; if candidate.join("manifest.json").exists() { return Ok(candidate.clone()); } } Err(PackageManagerError::MissingPackageRoot( extraction_root.to_path_buf(), )) } fn verify_sha256(bytes: &[u8], expected: &str) -> Result<(), PackageManagerError> { let actual = format!("{:x}", Sha256::digest(bytes)); if actual == expected.to_ascii_lowercase() { return Ok(()); } Err(PackageManagerError::ChecksumMismatch { expected: expected.to_string(), actual, }) } fn extract_archive( archive_path: &Path, destination: &Path, format: ArchiveFormat, ) -> Result<(), PackageManagerError> { match format { ArchiveFormat::Zip => extract_zip_archive(archive_path, destination), ArchiveFormat::TarGz => extract_tar_gz_archive(archive_path, destination), } } fn extract_zip_archive(archive_path: &Path, destination: &Path) -> Result<(), PackageManagerError> { let file = File::open(archive_path).map_err(|source| PackageManagerError::Io { context: format!("failed to open {}", archive_path.display()), source, })?; let mut archive = ZipArchive::new(file) .map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))?; for index in 0..archive.len() { let mut entry = archive .by_index(index) .map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))?; let Some(relative_path) = entry.enclosed_name() else { return Err(PackageManagerError::ArchiveExtraction(format!( "zip entry `{}` escapes extraction root", entry.name() ))); }; let output_path = destination.join(relative_path); if entry.is_dir() { std::fs::create_dir_all(&output_path).map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", output_path.display()), source, })?; continue; } if let Some(parent) = output_path.parent() { std::fs::create_dir_all(parent).map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", parent.display()), source, })?; } let mut output = File::create(&output_path).map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", output_path.display()), source, })?; std::io::copy(&mut entry, &mut output).map_err(|source| PackageManagerError::Io { context: format!("failed to write {}", output_path.display()), source, })?; apply_zip_permissions(&entry, &output_path)?; } Ok(()) } #[cfg(unix)] fn apply_zip_permissions( entry: &zip::read::ZipFile<'_>, output_path: &Path, ) -> Result<(), PackageManagerError> { let Some(mode) = entry.unix_mode() else { return Ok(()); }; std::fs::set_permissions(output_path, std::fs::Permissions::from_mode(mode)).map_err(|source| { PackageManagerError::Io { context: format!("failed to set permissions on {}", output_path.display()), source, } }) } #[cfg(not(unix))] fn apply_zip_permissions( _entry: &zip::read::ZipFile<'_>, _output_path: &Path, ) -> Result<(), PackageManagerError> { Ok(()) } fn extract_tar_gz_archive( archive_path: &Path, destination: &Path, ) -> Result<(), PackageManagerError> { let file = File::open(archive_path).map_err(|source| PackageManagerError::Io { context: format!("failed to open {}", archive_path.display()), source, })?; let decoder = GzDecoder::new(file); let mut archive = Archive::new(decoder); for entry in archive .entries() .map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))? { let mut entry = entry.map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))?; let path = entry .path() .map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))?; let output_path = safe_extract_path(destination, path.as_ref())?; if let Some(parent) = output_path.parent() { std::fs::create_dir_all(parent).map_err(|source| PackageManagerError::Io { context: format!("failed to create {}", parent.display()), source, })?; } entry .unpack(&output_path) .map_err(|error| PackageManagerError::ArchiveExtraction(error.to_string()))?; } Ok(()) } fn safe_extract_path(root: &Path, relative_path: &Path) -> Result { let mut clean_relative = PathBuf::new(); for component in relative_path.components() { match component { Component::Normal(segment) => clean_relative.push(segment), Component::CurDir => {} Component::ParentDir | Component::RootDir | Component::Prefix(_) => { return Err(PackageManagerError::ArchiveExtraction(format!( "entry `{}` escapes extraction root", relative_path.display() ))); } } } if clean_relative.as_os_str().is_empty() { return Err(PackageManagerError::ArchiveExtraction( "archive entry had an empty path".to_string(), )); } Ok(root.join(clean_relative)) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; use serde::Deserialize; use std::collections::BTreeMap; use std::io::Cursor; use std::io::Write; use std::sync::Arc; use tempfile::TempDir; use tokio::sync::Barrier; use wiremock::Mock; use wiremock::MockServer; use wiremock::ResponseTemplate; use wiremock::matchers::method; use wiremock::matchers::path; use zip::ZipWriter; use zip::write::SimpleFileOptions; #[derive(Clone, Debug)] struct TestPackage { base_url: Url, version: String, } #[derive(Clone, Debug, Deserialize)] struct TestReleaseManifest { package_version: String, platforms: BTreeMap, } #[derive(Clone, Debug, PartialEq, Eq)] struct TestInstalledPackage { version: String, platform: PackagePlatform, root_dir: PathBuf, } impl ManagedPackage for TestPackage { type Error = PackageManagerError; type Installed = TestInstalledPackage; type ReleaseManifest = TestReleaseManifest; fn default_cache_root_relative(&self) -> &str { "packages/test-package" } fn version(&self) -> &str { &self.version } fn manifest_url(&self) -> Result { self.base_url .join(&format!("test-package-v{}-manifest.json", self.version)) .map_err(PackageManagerError::InvalidBaseUrl) } fn archive_url(&self, archive: &PackageReleaseArchive) -> Result { self.base_url .join(&archive.archive) .map_err(PackageManagerError::InvalidBaseUrl) } fn release_version<'a>(&self, manifest: &'a Self::ReleaseManifest) -> &'a str { &manifest.package_version } fn platform_archive( &self, manifest: &Self::ReleaseManifest, platform: PackagePlatform, ) -> Result { manifest .platforms .get(platform.as_str()) .cloned() .ok_or_else(|| PackageManagerError::MissingPlatform(platform.as_str().to_string())) } fn install_dir(&self, cache_root: &Path, platform: PackagePlatform) -> PathBuf { cache_root.join(self.version()).join(platform.as_str()) } fn installed_version<'a>(&self, package: &'a Self::Installed) -> &'a str { &package.version } fn load_installed( &self, root_dir: PathBuf, platform: PackagePlatform, ) -> Result { let version = std::fs::read_to_string(root_dir.join("manifest.json")).map_err(|source| { PackageManagerError::Io { context: format!( "failed to read {}", root_dir.join("manifest.json").display() ), source, } })?; Ok(TestInstalledPackage { version: version.trim().to_string(), platform, root_dir, }) } } #[tokio::test] async fn ensure_installed_downloads_and_extracts_zip_package() { let server = MockServer::start().await; let version = "0.1.0"; let platform = PackagePlatform::detect_current().unwrap_or_else(|error| panic!("{error}")); let archive_name = format!("test-package-v{version}-{}.zip", platform.as_str()); let archive_bytes = build_zip_archive(version); let archive_sha = format!("{:x}", Sha256::digest(&archive_bytes)); let manifest = serde_json::json!({ "package_version": version, "platforms": { platform.as_str(): { "archive": archive_name, "sha256": archive_sha, "format": "zip", "size_bytes": archive_bytes.len(), } } }); Mock::given(method("GET")) .and(path(format!("/test-package-v{version}-manifest.json"))) .respond_with(ResponseTemplate::new(200).set_body_json(&manifest)) .mount(&server) .await; Mock::given(method("GET")) .and(path(format!("/{archive_name}"))) .respond_with(ResponseTemplate::new(200).set_body_bytes(archive_bytes)) .mount(&server) .await; let codex_home = TempDir::new().unwrap_or_else(|error| panic!("{error}")); let package = TestPackage { base_url: Url::parse(&format!("{}/", server.uri())) .unwrap_or_else(|error| panic!("{error}")), version: version.to_string(), }; let manager = PackageManager::new(PackageManagerConfig::new( codex_home.path().to_path_buf(), package, )); let installed = manager .ensure_installed() .await .unwrap_or_else(|error| panic!("{error}")); assert_eq!( installed, TestInstalledPackage { version: version.to_string(), platform, root_dir: codex_home .path() .join("packages") .join("test-package") .join(version) .join(platform.as_str()), } ); #[cfg(unix)] { let executable_mode = std::fs::metadata(installed.root_dir.join("bin/tool")) .unwrap_or_else(|error| panic!("{error}")) .permissions() .mode(); assert_eq!(executable_mode & 0o111, 0o111); } } #[tokio::test] async fn ensure_installed_replaces_invalid_cached_install() { let server = MockServer::start().await; let version = "0.1.0"; let platform = PackagePlatform::detect_current().unwrap_or_else(|error| panic!("{error}")); let archive_name = format!("test-package-v{version}-{}.zip", platform.as_str()); let archive_bytes = build_zip_archive(version); let archive_sha = format!("{:x}", Sha256::digest(&archive_bytes)); let manifest = serde_json::json!({ "package_version": version, "platforms": { platform.as_str(): { "archive": archive_name, "sha256": archive_sha, "format": "zip", "size_bytes": archive_bytes.len(), } } }); Mock::given(method("GET")) .and(path(format!("/test-package-v{version}-manifest.json"))) .respond_with(ResponseTemplate::new(200).set_body_json(&manifest)) .mount(&server) .await; Mock::given(method("GET")) .and(path(format!("/{archive_name}"))) .respond_with(ResponseTemplate::new(200).set_body_bytes(archive_bytes)) .mount(&server) .await; let codex_home = TempDir::new().unwrap_or_else(|error| panic!("{error}")); let install_dir = codex_home .path() .join("packages") .join("test-package") .join(version) .join(platform.as_str()); std::fs::create_dir_all(&install_dir).unwrap_or_else(|error| panic!("{error}")); std::fs::write(install_dir.join("broken.txt"), "stale") .unwrap_or_else(|error| panic!("{error}")); let manager = PackageManager::new(PackageManagerConfig::new( codex_home.path().to_path_buf(), TestPackage { base_url: Url::parse(&format!("{}/", server.uri())) .unwrap_or_else(|error| panic!("{error}")), version: version.to_string(), }, )); let installed = manager .ensure_installed() .await .unwrap_or_else(|error| panic!("{error}")); assert_eq!(installed.version, version); assert!(installed.root_dir.join("manifest.json").exists()); assert!(!installed.root_dir.join("broken.txt").exists()); } #[tokio::test] async fn ensure_installed_serializes_concurrent_installs() { let server = MockServer::start().await; let version = "0.1.0"; let platform = PackagePlatform::detect_current().unwrap_or_else(|error| panic!("{error}")); let archive_name = format!("test-package-v{version}-{}.zip", platform.as_str()); let archive_bytes = build_zip_archive(version); let archive_sha = format!("{:x}", Sha256::digest(&archive_bytes)); let manifest = serde_json::json!({ "package_version": version, "platforms": { platform.as_str(): { "archive": archive_name, "sha256": archive_sha, "format": "zip", "size_bytes": archive_bytes.len(), } } }); Mock::given(method("GET")) .and(path(format!("/test-package-v{version}-manifest.json"))) .respond_with(ResponseTemplate::new(200).set_body_json(&manifest)) .expect(1) .mount(&server) .await; Mock::given(method("GET")) .and(path(format!("/{archive_name}"))) .respond_with(ResponseTemplate::new(200).set_body_bytes(archive_bytes)) .expect(1) .mount(&server) .await; let codex_home = TempDir::new().unwrap_or_else(|error| panic!("{error}")); let config = PackageManagerConfig::new( codex_home.path().to_path_buf(), TestPackage { base_url: Url::parse(&format!("{}/", server.uri())) .unwrap_or_else(|error| panic!("{error}")), version: version.to_string(), }, ); let manager_one = PackageManager::new(config.clone()); let manager_two = PackageManager::new(config); let barrier = Arc::new(Barrier::new(2)); let barrier_one = Arc::clone(&barrier); let barrier_two = Arc::clone(&barrier); let (first, second) = tokio::join!( async { barrier_one.wait().await; manager_one.ensure_installed().await }, async { barrier_two.wait().await; manager_two.ensure_installed().await } ); let first = first.unwrap_or_else(|error| panic!("{error}")); let second = second.unwrap_or_else(|error| panic!("{error}")); assert_eq!(first, second); } #[test] fn tar_gz_extraction_supports_default_package_root_detection() { let temp = TempDir::new().unwrap_or_else(|error| panic!("{error}")); let archive_path = temp.path().join("package.tar.gz"); let extraction_root = temp.path().join("extract"); std::fs::create_dir_all(&extraction_root).unwrap_or_else(|error| panic!("{error}")); write_tar_gz_archive(&archive_path, "0.2.0"); extract_archive(&archive_path, &extraction_root, ArchiveFormat::TarGz) .unwrap_or_else(|error| panic!("{error}")); let package_root = detect_single_package_root(&extraction_root).unwrap_or_else(|error| panic!("{error}")); assert!(package_root.join("manifest.json").exists()); } fn build_zip_archive(version: &str) -> Vec { let mut bytes = Cursor::new(Vec::new()); { let mut zip = ZipWriter::new(&mut bytes); let options = SimpleFileOptions::default(); zip.start_file("test-package/manifest.json", options) .unwrap_or_else(|error| panic!("{error}")); zip.write_all(version.as_bytes()) .unwrap_or_else(|error| panic!("{error}")); zip.start_file("test-package/bin/tool", options.unix_permissions(0o755)) .unwrap_or_else(|error| panic!("{error}")); zip.write_all(b"#!/bin/sh\n") .unwrap_or_else(|error| panic!("{error}")); zip.finish().unwrap_or_else(|error| panic!("{error}")); } bytes.into_inner() } fn write_tar_gz_archive(archive_path: &Path, version: &str) { let file = File::create(archive_path).unwrap_or_else(|error| panic!("{error}")); let encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); let mut builder = tar::Builder::new(encoder); append_tar_file( &mut builder, "test-package/manifest.json", version.as_bytes(), ); builder.finish().unwrap_or_else(|error| panic!("{error}")); } fn append_tar_file( builder: &mut tar::Builder>, path: &str, contents: &[u8], ) { let mut header = tar::Header::new_gnu(); header.set_size(contents.len() as u64); header.set_mode(0o755); header.set_cksum(); builder .append_data(&mut header, path, contents) .unwrap_or_else(|error| panic!("{error}")); } }