mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
## What changed - Run the daemon update loop on Windows using the non-interactive PowerShell installer. - Restart the managed app server after updates and hand updater ownership to a changed managed executable through a readiness handshake. - Contain installer descendants in a Windows job while allowing app-server and successor updater processes to break away. - Preserve or restore PID ownership when successor startup fails, without terminating a process that reused the PID. ## Testing - Cover non-interactive installer success and script failure on Windows. - Cover updater handoff, readiness, rollback, inaccessible processes, and PID reuse. GitOrigin-RevId: 5178c01124b4dd86b507b53f5093ec9a9d48e12c
107 lines
3.2 KiB
Rust
107 lines
3.2 KiB
Rust
//! Resolves both package and legacy standalone layouts and compares installed executables.
|
|
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
|
|
use anyhow::Context;
|
|
use anyhow::Result;
|
|
use anyhow::anyhow;
|
|
use sha2::Digest;
|
|
use sha2::Sha256;
|
|
use tokio::fs;
|
|
use tokio::process::Command;
|
|
|
|
/// Returns the packaged executable when present, otherwise an existing legacy executable.
|
|
/// If neither exists, returns the expected packaged path on Windows and preserves the
|
|
/// historical legacy fallback on Unix. This is path selection, not existence validation:
|
|
/// launch operations reject a missing executable separately, while commands such as
|
|
/// stop can still run after the managed install has been removed.
|
|
pub(crate) fn managed_codex_bin(codex_home: &Path) -> PathBuf {
|
|
let current = codex_home
|
|
.join("packages")
|
|
.join("standalone")
|
|
.join("current");
|
|
let packaged = current.join("bin").join(managed_codex_file_name());
|
|
let legacy = current.join(managed_codex_file_name());
|
|
if packaged.is_file() || (cfg!(windows) && !legacy.is_file()) {
|
|
packaged
|
|
} else {
|
|
legacy
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn resolved_managed_codex_bin(codex_bin: &Path) -> Result<PathBuf> {
|
|
fs::canonicalize(codex_bin).await.with_context(|| {
|
|
format!(
|
|
"failed to resolve managed Codex binary {}",
|
|
codex_bin.display()
|
|
)
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn managed_codex_version(codex_bin: &Path) -> Result<String> {
|
|
let mut command = Command::new(codex_bin);
|
|
#[cfg(windows)]
|
|
command.creation_flags(windows_sys::Win32::System::Threading::CREATE_NO_WINDOW);
|
|
let output = command.arg("--version").output().await.with_context(|| {
|
|
format!(
|
|
"failed to invoke managed Codex binary {}",
|
|
codex_bin.display()
|
|
)
|
|
})?;
|
|
if !output.status.success() {
|
|
return Err(anyhow!(
|
|
"managed Codex binary {} exited with status {}",
|
|
codex_bin.display(),
|
|
output.status
|
|
));
|
|
}
|
|
|
|
let stdout = String::from_utf8(output.stdout).with_context(|| {
|
|
format!(
|
|
"managed Codex version was not utf-8: {}",
|
|
codex_bin.display()
|
|
)
|
|
})?;
|
|
parse_codex_version(&stdout)
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub(crate) struct ExecutableIdentity {
|
|
digest: [u8; 32],
|
|
}
|
|
|
|
pub(crate) async fn executable_identity(executable: &Path) -> Result<ExecutableIdentity> {
|
|
let bytes = fs::read(executable)
|
|
.await
|
|
.with_context(|| format!("failed to read executable {}", executable.display()))?;
|
|
Ok(executable_identity_from_bytes(&bytes))
|
|
}
|
|
|
|
pub(crate) fn executable_identity_from_bytes(bytes: &[u8]) -> ExecutableIdentity {
|
|
ExecutableIdentity {
|
|
digest: Sha256::digest(bytes).into(),
|
|
}
|
|
}
|
|
|
|
fn managed_codex_file_name() -> &'static str {
|
|
if cfg!(windows) { "codex.exe" } else { "codex" }
|
|
}
|
|
|
|
fn parse_codex_version(output: &str) -> Result<String> {
|
|
let version = output
|
|
.split_whitespace()
|
|
.nth(1)
|
|
.filter(|version| !version.is_empty())
|
|
.ok_or_else(|| anyhow!("managed Codex version output was malformed"))?;
|
|
Ok(version.to_string())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "managed_install_tests.rs"]
|
|
mod tests;
|
|
|
|
#[cfg(test)]
|
|
#[path = "managed_install_path_tests.rs"]
|
|
mod path_tests;
|