mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Use descriptor-safe helpers for executable test fixtures (#50018)
## Why Concurrent Linux test spawns can inherit writable descriptors for executable fixtures, preventing those fixtures from being executed even after the writing test closes its descriptor. ## What changed Replace direct executable fixture writes and copies across the Rust tests with `codex_utils_cargo_bin::write_executable` and `copy_executable`. These existing helpers complete writes and copies in separate processes on Linux. Remove the empty temporary inode before creating fake bubblewrap executables, and stage tampered bubblewrap bytes in a separate data file before copying them into the executable fixture. Add the required test dependencies. GitOrigin-RevId: c875c83088731a4933ef1574e8d79d8cecc12f54
This commit is contained in:
Generated
+11
@@ -2219,6 +2219,7 @@ dependencies = [
|
||||
"codex-http-client",
|
||||
"codex-install-context",
|
||||
"codex-uds",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-home-dir",
|
||||
"futures",
|
||||
"libc",
|
||||
@@ -2368,6 +2369,7 @@ dependencies = [
|
||||
"codex-sandboxing",
|
||||
"codex-shell-escalation",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-home-dir",
|
||||
"codex-utils-pty",
|
||||
"codex-windows-sandbox",
|
||||
@@ -3093,6 +3095,7 @@ dependencies = [
|
||||
"codex-skills",
|
||||
"codex-tools",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-path",
|
||||
"codex-utils-path-uri",
|
||||
"codex-utils-plugins",
|
||||
@@ -3475,6 +3478,7 @@ dependencies = [
|
||||
"codex-file-system",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-path-uri",
|
||||
"codex-utils-process",
|
||||
"codex-utils-pty",
|
||||
@@ -3814,6 +3818,7 @@ dependencies = [
|
||||
"codex-protocol",
|
||||
"codex-secrets",
|
||||
"codex-terminal-detection",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-process",
|
||||
"codex-utils-template",
|
||||
"codex-workload-identity",
|
||||
@@ -4027,6 +4032,7 @@ dependencies = [
|
||||
"codex-protocol",
|
||||
"codex-response-debug-context",
|
||||
"codex-secrets",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-process",
|
||||
"codex-utils-redacted-string",
|
||||
"http 1.4.0",
|
||||
@@ -4330,6 +4336,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"codex-build-info",
|
||||
"codex-install-context",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-pty",
|
||||
"ctor 0.6.3",
|
||||
"futures",
|
||||
@@ -4476,6 +4483,7 @@ dependencies = [
|
||||
"codex-protocol",
|
||||
"codex-uds",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-home-dir",
|
||||
"codex-utils-path-uri",
|
||||
"codex-utils-pty",
|
||||
@@ -5106,6 +5114,7 @@ name = "codex-utils-pty"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"codex-utils-cargo-bin",
|
||||
"ctor 0.6.3",
|
||||
"filedescriptor",
|
||||
"lazy_static",
|
||||
@@ -5291,6 +5300,7 @@ dependencies = [
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-process",
|
||||
"codex-utils-pty",
|
||||
"codex-utils-string",
|
||||
@@ -5355,6 +5365,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"codex-git-utils",
|
||||
"codex-protocol",
|
||||
"codex-utils-cargo-bin",
|
||||
"dunce",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
|
||||
@@ -40,6 +40,7 @@ tokio-tungstenite = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
@@ -225,20 +225,14 @@ async fn stale_record_cleanup_preserves_replacement_record() {
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn pid_record_captures_the_resolved_launch_binary() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let temp = TempDir::new().expect("temp dir");
|
||||
let original = temp.path().join("original-codex");
|
||||
let replacement = temp.path().join("replacement-codex");
|
||||
for (path, bytes) in [
|
||||
(&original, b"#!/bin/sh\nexec sleep 30\n".as_slice()),
|
||||
(
|
||||
&replacement,
|
||||
b"#!/bin/sh\n# replacement\nexec sleep 30\n".as_slice(),
|
||||
),
|
||||
(&original, "#!/bin/sh\nexec sleep 30\n"),
|
||||
(&replacement, "#!/bin/sh\n# replacement\nexec sleep 30\n"),
|
||||
] {
|
||||
std::fs::write(path, bytes).expect("binary");
|
||||
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755)).expect("executable");
|
||||
codex_utils_cargo_bin::write_executable(path, bytes).expect("binary");
|
||||
}
|
||||
let selected = temp.path().join("current-codex");
|
||||
std::os::unix::fs::symlink(&original, &selected).expect("selected binary");
|
||||
|
||||
@@ -1390,19 +1390,14 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn managed_local_backend_counts_as_bootstrapped_without_updater() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let home = TempDir::new().expect("home");
|
||||
let standalone = home.path().join("packages/standalone");
|
||||
let local_bin = standalone.join("local-main/bin/codex");
|
||||
tokio::fs::create_dir_all(local_bin.parent().expect("bin parent"))
|
||||
.await
|
||||
.expect("local bin directory");
|
||||
tokio::fs::write(&local_bin, b"#!/bin/sh\nexec sleep 30\n")
|
||||
.await
|
||||
codex_utils_cargo_bin::write_executable(&local_bin, "#!/bin/sh\nexec sleep 30\n")
|
||||
.expect("local bin");
|
||||
std::fs::set_permissions(&local_bin, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("executable local bin");
|
||||
std::os::unix::fs::symlink("local-main", standalone.join("current"))
|
||||
.expect("current local build");
|
||||
let state = home.path().join("app-server-daemon");
|
||||
|
||||
@@ -114,14 +114,10 @@ fn updater_only_runs_for_stable_installer_owned_releases() {
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn older_managed_binary_does_not_claim_updater_support() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let temp = tempfile::TempDir::new().expect("home");
|
||||
let binary = temp.path().join("codex");
|
||||
std::fs::write(&binary, b"#!/bin/sh\nexit 2\n").expect("older binary");
|
||||
std::fs::set_permissions(&binary, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("executable binary");
|
||||
codex_utils_cargo_bin::write_executable(&binary, "#!/bin/sh\nexit 2\n").expect("older binary");
|
||||
assert!(!super::supports_daemon_update_loop(&binary).await);
|
||||
std::fs::write(&binary, b"#!/bin/sh\nexit 0\n").expect("newer binary");
|
||||
codex_utils_cargo_bin::write_executable(&binary, "#!/bin/sh\nexit 0\n").expect("newer binary");
|
||||
assert!(super::supports_daemon_update_loop(&binary).await);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use super::prepare_from_package;
|
||||
use super::validate_package;
|
||||
use crate::settings::DaemonSettings;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -28,28 +27,17 @@ fn package(root: &Path, version: &str) -> PathBuf {
|
||||
std::fs::create_dir_all(root.join(dir)).expect("package directory");
|
||||
}
|
||||
let bin = root.join("bin/codex");
|
||||
std::fs::write(&bin, format!("#!/bin/sh\necho 'codex {version}'\n")).expect("codex executable");
|
||||
for file in [
|
||||
"bin/codex-code-mode-host",
|
||||
"codex-path/rg",
|
||||
"codex-resources/nested/runtime",
|
||||
] {
|
||||
std::fs::write(root.join(file), b"runtime").expect("package file");
|
||||
if file != "codex-resources/nested/runtime" {
|
||||
std::fs::set_permissions(root.join(file), std::fs::Permissions::from_mode(0o755))
|
||||
.expect("executable helper");
|
||||
}
|
||||
codex_utils_cargo_bin::write_executable(&bin, &format!("#!/bin/sh\necho 'codex {version}'\n"))
|
||||
.expect("codex executable");
|
||||
for file in ["bin/codex-code-mode-host", "codex-path/rg"] {
|
||||
codex_utils_cargo_bin::write_executable(&root.join(file), "runtime")
|
||||
.expect("executable helper");
|
||||
}
|
||||
std::fs::write(root.join("codex-resources/nested/runtime"), b"runtime").expect("package file");
|
||||
if cfg!(target_os = "linux") {
|
||||
std::fs::write(root.join("codex-resources/bwrap"), b"runtime").expect("bwrap");
|
||||
std::fs::set_permissions(
|
||||
root.join("codex-resources/bwrap"),
|
||||
std::fs::Permissions::from_mode(0o755),
|
||||
)
|
||||
.expect("executable bwrap");
|
||||
codex_utils_cargo_bin::write_executable(&root.join("codex-resources/bwrap"), "runtime")
|
||||
.expect("executable bwrap");
|
||||
}
|
||||
std::fs::set_permissions(&bin, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("executable permission");
|
||||
std::fs::write(
|
||||
root.join("codex-package.json"),
|
||||
serde_json::json!({
|
||||
@@ -120,7 +108,8 @@ async fn provisioned_macos_bundle_seeds_from_its_running_executable() {
|
||||
let temp = tempfile::TempDir::new().expect("temp");
|
||||
let source = temp.path().join("package");
|
||||
let launcher = package(&source, "0.1.0-internal-test.202609091200.1");
|
||||
std::fs::write(&launcher, b"#!/bin/sh\necho codex 0.0.0\n").expect("launcher");
|
||||
codex_utils_cargo_bin::write_executable(&launcher, "#!/bin/sh\necho codex 0.0.0\n")
|
||||
.expect("launcher");
|
||||
let bundle = source.join("CodexCLI.app/Contents/MacOS/codex");
|
||||
std::fs::create_dir_all(bundle.parent().expect("bundle parent")).expect("bundle dir");
|
||||
std::fs::write(&bundle, b"provisioned executable").expect("bundle executable");
|
||||
|
||||
@@ -202,7 +202,11 @@ async fn explicit_update_migrates_running_and_stopped_installations() {
|
||||
if local {
|
||||
let package = root.join("releases/local-development");
|
||||
std::fs::create_dir(&package).unwrap();
|
||||
std::fs::copy(&legacy.managed_codex_bin, package.join("codex")).unwrap();
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&legacy.managed_codex_bin,
|
||||
&package.join("codex"),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::remove_file(root.join("current")).unwrap();
|
||||
std::os::unix::fs::symlink(&package, root.join("current")).unwrap();
|
||||
std::fs::remove_file(root.join("auto-update-version")).unwrap();
|
||||
@@ -413,8 +417,6 @@ async fn cancelling_installer_stops_children_and_releases_fallback_lock() {
|
||||
|
||||
#[cfg(unix)]
|
||||
fn manual_update_daemon(home: &TempDir) -> (Daemon, String) {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let target = if cfg!(target_os = "macos") {
|
||||
format!("{}-apple-darwin", std::env::consts::ARCH)
|
||||
} else {
|
||||
@@ -424,13 +426,11 @@ fn manual_update_daemon(home: &TempDir) -> (Daemon, String) {
|
||||
let standalone = home.path().join("packages/standalone");
|
||||
let bin = standalone.join("releases").join(&release).join("codex");
|
||||
std::fs::create_dir_all(bin.parent().expect("binary parent")).expect("release directory");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&bin,
|
||||
b"#!/bin/sh\nif [ \"$1\" = '--version' ]; then echo codex 1.0.0; else exec sleep 30; fi\n",
|
||||
"#!/bin/sh\nif [ \"$1\" = '--version' ]; then echo codex 1.0.0; else exec sleep 30; fi\n",
|
||||
)
|
||||
.expect("managed binary");
|
||||
std::fs::set_permissions(&bin, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("executable binary");
|
||||
std::os::unix::fs::symlink(format!("releases/{release}"), standalone.join("current"))
|
||||
.expect("current release");
|
||||
std::fs::write(standalone.join("auto-update-version"), &release).expect("latest marker");
|
||||
@@ -698,7 +698,7 @@ async fn daemon_start_and_restart_preserve_launch_features() {
|
||||
r#"{"featureOverrides":{"auth_elicitation":true},"updater":{"autoUpdateEnabled":false}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(&daemon.managed_codex_bin, format!(
|
||||
codex_utils_cargo_bin::write_executable(&daemon.managed_codex_bin, &format!(
|
||||
"#!/bin/sh\nif [ \"$1\" = --version ]; then echo codex 1.0.0; exit; fi\nif [ \"$3\" = --help ]; then exit; fi\nprintf '%s\\n' \"$@\" > '{}'\nexec sleep 30\n",
|
||||
args_path.display(),
|
||||
)).unwrap();
|
||||
@@ -974,17 +974,15 @@ async fn check_manual_update_restart(package_directory: &str) {
|
||||
let no_op = FakeInstallerHttp::new(InstallerResponse::Success(
|
||||
b"#!/bin/sh\n# CODEX_INSTALL_IF_LATEST CODEX_INSTALL_DAEMON_ONLY\nexit 0\n".to_vec(),
|
||||
));
|
||||
use std::io::Write;
|
||||
std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.open(
|
||||
daemon
|
||||
.current_managed_codex_bin()
|
||||
.expect("current executable"),
|
||||
)
|
||||
.expect("managed executable")
|
||||
.write_all(b"\n# same-version replacement\n")
|
||||
.expect("replace binary bytes");
|
||||
let bin = daemon
|
||||
.current_managed_codex_bin()
|
||||
.expect("current executable");
|
||||
let contents = std::fs::read_to_string(&bin).expect("managed executable");
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&bin,
|
||||
&format!("{contents}\n# same-version replacement\n"),
|
||||
)
|
||||
.expect("replace binary bytes");
|
||||
let output = manual_update_once(
|
||||
&no_op,
|
||||
&daemon,
|
||||
|
||||
@@ -2121,7 +2121,7 @@ impl TestAppServerBuilder {
|
||||
(&code_mode_host_program, &staged_host),
|
||||
] {
|
||||
std::fs::hard_link(source, destination)
|
||||
.or_else(|_| std::fs::copy(source, destination).map(|_| ()))
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(source, destination))
|
||||
.with_context(|| format!("stage executable {}", source.display()))?;
|
||||
}
|
||||
program = staged_program;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
@@ -111,16 +110,13 @@ impl CuratedMcpSyncFixture {
|
||||
)?;
|
||||
|
||||
let malicious_git_helper = fixture_root.path().join("malicious-git-helper.sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&malicious_git_helper,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nprintf ran > '{}'\nexit 73\n",
|
||||
malicious_git_helper_marker.display()
|
||||
),
|
||||
)?;
|
||||
let mut helper_permissions = std::fs::metadata(&malicious_git_helper)?.permissions();
|
||||
helper_permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&malicious_git_helper, helper_permissions)?;
|
||||
run_git(&real_git, &codex_home, &["init", "-b", "main"])?;
|
||||
run_git(
|
||||
&real_git,
|
||||
@@ -135,7 +131,7 @@ impl CuratedMcpSyncFixture {
|
||||
&["config", &malicious_rewrite_key, GITHUB_PLUGINS_GIT_URL],
|
||||
)?;
|
||||
let git_wrapper = git_wrapper_dir.join("git");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&git_wrapper,
|
||||
r#"#!/bin/sh
|
||||
if [ "$1" = "ls-remote" ]; then
|
||||
@@ -146,9 +142,6 @@ fi
|
||||
exec "$REAL_GIT" "$@"
|
||||
"#,
|
||||
)?;
|
||||
let mut wrapper_permissions = std::fs::metadata(&git_wrapper)?.permissions();
|
||||
wrapper_permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&git_wrapper, wrapper_permissions)?;
|
||||
|
||||
MockResponsesConfig::new(&responses_server.uri())
|
||||
.enable_feature(Feature::Plugins)
|
||||
|
||||
@@ -648,6 +648,7 @@ async fn loaded_session_refreshes_externally_updated_plugin_hooks() -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn automatic_marketplace_upgrade_refreshes_hook_runtime_for_loaded_session() -> Result<()> {
|
||||
skip_if_host_windows!(Ok(()));
|
||||
@@ -705,9 +706,9 @@ async fn automatic_marketplace_upgrade_refreshes_hook_runtime_for_loaded_session
|
||||
.find(|path| path.is_file())
|
||||
.ok_or_else(|| anyhow::anyhow!("git was not found on PATH"))?;
|
||||
let wrapper_path = git_wrapper.path().join("git");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&wrapper_path,
|
||||
format!(
|
||||
&format!(
|
||||
r#"#!/bin/sh
|
||||
if [ "$3" = "ls-remote" ] && [ "$4" = "{marketplace}" ]; then
|
||||
while [ ! -e "{upgrade_gate}" ]; do
|
||||
@@ -721,14 +722,6 @@ exec "{real_git}" "$@"
|
||||
real_git = real_git.display(),
|
||||
),
|
||||
)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let mut permissions = std::fs::metadata(&wrapper_path)?.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&wrapper_path, permissions)?;
|
||||
}
|
||||
let path_with_wrapper = std::env::join_paths(
|
||||
std::iter::once(git_wrapper.path().to_path_buf())
|
||||
.chain(std::env::split_paths(&original_path)),
|
||||
|
||||
@@ -779,11 +779,11 @@ fn create_test_package_app_server(codex_home: &Path, zsh_path: &Path) -> Result<
|
||||
std::fs::write(package_dir.join("codex-package.json"), "{}")?;
|
||||
|
||||
let app_server = bin_dir.join("codex-app-server");
|
||||
copy_with_permissions(
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&codex_utils_cargo_bin::cargo_bin("codex-app-server")?,
|
||||
&app_server,
|
||||
)?;
|
||||
copy_with_permissions(zsh_path, &package_zsh_path)?;
|
||||
codex_utils_cargo_bin::copy_executable(zsh_path, &package_zsh_path)?;
|
||||
Ok(app_server)
|
||||
}
|
||||
|
||||
@@ -801,11 +801,6 @@ fn command_packaged_zsh_path(codex_home: &Path) -> PathBuf {
|
||||
std::fs::canonicalize(&path).unwrap_or(path)
|
||||
}
|
||||
|
||||
fn copy_with_permissions(source: &Path, destination: &Path) -> std::io::Result<()> {
|
||||
std::fs::copy(source, destination)?;
|
||||
std::fs::set_permissions(destination, std::fs::metadata(source)?.permissions())
|
||||
}
|
||||
|
||||
fn create_config_toml(
|
||||
codex_home: &Path,
|
||||
server_uri: &str,
|
||||
|
||||
@@ -33,4 +33,5 @@ codex-windows-sandbox = { workspace = true }
|
||||
pathdiff = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -622,7 +622,7 @@ mod tests {
|
||||
.ok_or_else(|| anyhow::anyhow!("missing Windows system root"))?;
|
||||
let command_shell = PathBuf::from(system_root).join("System32").join("cmd.exe");
|
||||
let executable = executable_directory.join("cmd.exe");
|
||||
fs::copy(&command_shell, &executable)?;
|
||||
codex_utils_cargo_bin::copy_executable(&command_shell, &executable)?;
|
||||
|
||||
let batch_path = alias_directory.join("apply_patch.bat");
|
||||
let executable_path = super::windows_batch_executable_path(&executable, &alias_directory);
|
||||
|
||||
@@ -8,7 +8,7 @@ fn daemon_handoff_uses_selected_executable_and_propagates_failure() -> anyhow::R
|
||||
let dir = tempfile::tempdir()?;
|
||||
let executable = dir.path().join("launching CLI");
|
||||
let receipt = dir.path().join("launching CLI.args");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&executable,
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$0.args\"\n",
|
||||
)?;
|
||||
@@ -26,7 +26,7 @@ fn daemon_handoff_uses_selected_executable_and_propagates_failure() -> anyhow::R
|
||||
run_update_action(UpdateAction::Daemon(source), Some(&executable))?;
|
||||
assert_eq!(std::fs::read_to_string(&receipt)?, expected);
|
||||
}
|
||||
std::fs::write(&executable, "#!/bin/sh\nexit 7\n")?;
|
||||
codex_utils_cargo_bin::write_executable(&executable, "#!/bin/sh\nexit 7\n")?;
|
||||
let error = run_update_action(
|
||||
UpdateAction::Daemon(DaemonUpdateSource::ThisCli),
|
||||
Some(&executable),
|
||||
|
||||
@@ -422,7 +422,8 @@ mod tests {
|
||||
let executable_path = contents_path.join("MacOS/Codex");
|
||||
fs::create_dir_all(executable_path.parent().expect("executable parent"))
|
||||
.expect("create executable directory");
|
||||
fs::copy("/usr/bin/true", &executable_path).expect("copy executable into app bundle");
|
||||
codex_utils_cargo_bin::copy_executable(Path::new("/usr/bin/true"), &executable_path)
|
||||
.expect("copy executable into app bundle");
|
||||
fs::write(
|
||||
contents_path.join("Info.plist"),
|
||||
r#"<?xml version="1.0"?><plist version="1.0"><dict><key>CFBundleIdentifier</key><string>com.openai.codex</string><key>CFBundleExecutable</key><string>Codex</string></dict></plist>"#,
|
||||
|
||||
@@ -547,7 +547,6 @@ enum InitialDaemon {
|
||||
}
|
||||
|
||||
fn packaged_daemon_launch(action: &str, initial: InitialDaemon) -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut daemon = TestDaemon::new()?;
|
||||
let standalone = daemon.home.path().join("packages/standalone");
|
||||
let package = if action == "start" && initial == InitialDaemon::Missing {
|
||||
@@ -565,8 +564,7 @@ fn packaged_daemon_launch(action: &str, initial: InitialDaemon) -> Result<()> {
|
||||
"codex-path/rg",
|
||||
"codex-resources/bwrap",
|
||||
] {
|
||||
std::fs::write(package.join(helper), b"runtime fixture")?;
|
||||
std::fs::set_permissions(package.join(helper), std::fs::Permissions::from_mode(0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&package.join(helper), "runtime fixture")?;
|
||||
}
|
||||
let target = format!(
|
||||
"{}-{}",
|
||||
@@ -670,13 +668,9 @@ fn packaged_daemon_launch(action: &str, initial: InitialDaemon) -> Result<()> {
|
||||
if initial == InitialDaemon::Legacy {
|
||||
assert!(!current.exists());
|
||||
}
|
||||
std::fs::write(
|
||||
package.join("bin/codex-code-mode-host"),
|
||||
b"replacement helper",
|
||||
)?;
|
||||
std::fs::set_permissions(
|
||||
package.join("bin/codex-code-mode-host"),
|
||||
std::fs::Permissions::from_mode(0o755),
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&package.join("bin/codex-code-mode-host"),
|
||||
"replacement helper",
|
||||
)?;
|
||||
let replaced = daemon
|
||||
.command()
|
||||
|
||||
@@ -207,7 +207,8 @@ async fn daemon_startup(command: &str) -> Result<()> {
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(&codex, &managed)?;
|
||||
#[cfg(not(unix))]
|
||||
fs::hard_link(&codex, &managed).or_else(|_| fs::copy(&codex, &managed).map(|_| ()))?;
|
||||
fs::hard_link(&codex, &managed)
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(&codex, &managed))?;
|
||||
if command != "restrictive-job" {
|
||||
fs::create_dir(home.path().join("app-server-daemon"))?;
|
||||
fs::write(
|
||||
|
||||
@@ -86,16 +86,11 @@ wire_api = "responses"
|
||||
] {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let executable = bin.join(name);
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&executable,
|
||||
"#!/bin/sh\nprintf 'helper ran\\n' >> \"$CODEX_TEST_HELPER_MARKER\"\nexit 0\n",
|
||||
)?;
|
||||
std::fs::set_permissions(
|
||||
executable,
|
||||
std::fs::Permissions::from_mode(/*mode*/ 0o755),
|
||||
)?;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
std::fs::write(
|
||||
|
||||
@@ -11,7 +11,6 @@ use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn pid_inheritance_is_startup_only_for_process_and_filesystem_helpers() -> Result<()> {
|
||||
@@ -34,7 +33,7 @@ async fn pid_inheritance_is_startup_only_for_process_and_filesystem_helpers() ->
|
||||
let fixture = TempDir::new()?;
|
||||
let wrapper = fixture.path().join("bwrap");
|
||||
std::os::unix::fs::symlink(bwrap, fixture.path().join("real-bwrap"))?;
|
||||
std::fs::write(&wrapper, r#"#!/bin/sh
|
||||
codex_utils_cargo_bin::write_executable(&wrapper, r#"#!/bin/sh
|
||||
for arg in "$@"; do
|
||||
[ "$arg" = "--" ] && break
|
||||
if [ "$arg" = "--proc" ]; then
|
||||
@@ -44,7 +43,6 @@ for arg in "$@"; do
|
||||
done
|
||||
exec "${0%/*}/real-bwrap" "$@"
|
||||
"#)?;
|
||||
std::fs::set_permissions(&wrapper, std::fs::Permissions::from_mode(0o755))?;
|
||||
let path = format!("{}:{}", fixture.path().display(), std::env::var("PATH")?);
|
||||
// System bubblewrap discovery intentionally excludes executables under the command cwd.
|
||||
let workspace = TempDir::new()?;
|
||||
|
||||
@@ -281,7 +281,8 @@ trust_level = "trusted"
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(program.canonicalize()?, &managed)?;
|
||||
#[cfg(not(unix))]
|
||||
fs::hard_link(&program, &managed).or_else(|_| fs::copy(&program, &managed).map(|_| ()))?;
|
||||
fs::hard_link(&program, &managed)
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(&program, &managed))?;
|
||||
fs::create_dir(home.join("app-server-daemon"))?;
|
||||
fs::write(
|
||||
home.join("app-server-daemon/settings.json"),
|
||||
|
||||
@@ -898,9 +898,9 @@ async fn child_process_loss_cleans_up_and_rebuilds_the_shared_host() {
|
||||
let proxy_dir = tempfile::tempdir().expect("create host proxy directory");
|
||||
let proxy_program = proxy_dir.path().join("host-proxy.sh");
|
||||
let pid_path = proxy_dir.path().join("host.pid");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&proxy_program,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$$\" > '{}'\nexec '{}'\n",
|
||||
pid_path.display(),
|
||||
host_program.display()
|
||||
|
||||
@@ -60,6 +60,7 @@ zip = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-exec-server-test-support = { workspace = true }
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -8,8 +8,6 @@ use std::io::Write;
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn materialize_npm_plugin_source_uses_packed_package_root() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let fake_npm_dir = tempfile::tempdir().expect("create fake npm directory");
|
||||
let archive_bytes =
|
||||
@@ -17,9 +15,9 @@ fn materialize_npm_plugin_source_uses_packed_package_root() {
|
||||
let archive_path = fake_npm_dir.path().join("fixture.tgz");
|
||||
fs::write(&archive_path, &archive_bytes).expect("write fixture archive");
|
||||
let fake_npm = fake_npm_dir.path().join("npm");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&fake_npm,
|
||||
format!(
|
||||
&format!(
|
||||
r#"#!/bin/sh
|
||||
destination=""
|
||||
previous=""
|
||||
@@ -37,11 +35,6 @@ pwd > "$destination/pwd.txt"
|
||||
),
|
||||
)
|
||||
.expect("write fake npm");
|
||||
let mut permissions = fs::metadata(&fake_npm)
|
||||
.expect("read fake npm metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(&fake_npm, permissions).expect("make fake npm executable");
|
||||
|
||||
let (plugin_root, tempdir) = materialize_npm_plugin_source_with_command(
|
||||
codex_home.path(),
|
||||
|
||||
@@ -499,16 +499,7 @@ fn has_plugins_clone_dirs(codex_home: &Path) -> bool {
|
||||
|
||||
#[cfg(unix)]
|
||||
fn write_executable_script(path: &Path, contents: &str) {
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
std::fs::write(path, contents).expect("write script");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut permissions = std::fs::metadata(path).expect("metadata").permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(path, permissions).expect("chmod");
|
||||
}
|
||||
codex_utils_cargo_bin::write_executable(path, contents).expect("write script");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -35,8 +35,6 @@ use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
@@ -589,19 +587,14 @@ async fn test_get_has_changes_ignores_configured_hooks_path() {
|
||||
let marker_path = repo_path.join("hook-ran");
|
||||
|
||||
fs::create_dir_all(&hooks_dir).expect("create hook dir");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&hook_path,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nprintf ran > \"{}\"\n",
|
||||
marker_path.to_string_lossy()
|
||||
),
|
||||
)
|
||||
.expect("write post-index-change hook");
|
||||
let mut permissions = fs::metadata(&hook_path)
|
||||
.expect("read hook metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(&hook_path, permissions).expect("mark hook executable");
|
||||
|
||||
Command::new("git")
|
||||
.args([
|
||||
|
||||
@@ -100,16 +100,13 @@ fn test_get_command_respects_explicit_bash_shell() -> anyhow::Result<()> {
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_get_command_keeps_path_prepends_after_login_startup() -> anyhow::Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
let path_dir = temp_dir.path().join("codex '[*] path");
|
||||
let extra_path_dir = temp_dir.path().join("extra tools");
|
||||
for directory in [&path_dir, &extra_path_dir] {
|
||||
std::fs::create_dir(directory)?;
|
||||
let rg = directory.join("rg");
|
||||
std::fs::write(&rg, "#!/bin/sh\n")?;
|
||||
std::fs::set_permissions(&rg, std::fs::Permissions::from_mode(0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&rg, "#!/bin/sh\n")?;
|
||||
}
|
||||
let rg = path_dir.join("rg");
|
||||
let startup = temp_dir.path().join("startup");
|
||||
|
||||
@@ -1299,7 +1299,7 @@ async fn snapshot_wrapper_replays_dummy_and_preserves_unbrokered_credentials() -
|
||||
let system_startup_dir = dir.path().join("system-startup");
|
||||
std::fs::create_dir(&system_startup_dir)?;
|
||||
let system_startup_zsh = system_startup_dir.join("zsh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&system_startup_zsh,
|
||||
"#!/bin/sh\nexport OPENAI_API_KEY='sk-system-startup-secret'\nexport AUTH_HEADER='Bearer sk-system-startup-secret'\nexec /bin/zsh -ax \"$@\"\n",
|
||||
)?;
|
||||
|
||||
@@ -492,26 +492,15 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
|
||||
.config
|
||||
.codex_home
|
||||
.join("permission_request_hook_log.jsonl");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\ncat > {log_path}\nprintf '%s\\n' '{response}'\n",
|
||||
log_path = shlex::try_quote(log_path.to_string_lossy().as_ref())?,
|
||||
response = "{\"hookSpecificOutput\":{\"hookEventName\":\"PermissionRequest\",\"decision\":{\"behavior\":\"allow\"}}}",
|
||||
),
|
||||
)
|
||||
.with_context(|| format!("write hook script to {}", script_path.display()))?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let mut permissions = std::fs::metadata(&script_path)
|
||||
.with_context(|| format!("read hook script metadata from {}", script_path.display()))?
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&script_path, permissions)
|
||||
.with_context(|| format!("set hook script permissions on {}", script_path.display()))?;
|
||||
}
|
||||
std::fs::write(
|
||||
turn_context.config.codex_home.join("hooks.json"),
|
||||
serde_json::json!({
|
||||
|
||||
@@ -2832,9 +2832,9 @@ async fn shell_startup_credentials_are_brokered(
|
||||
};
|
||||
let zsh_path = zsh.derive_exec_args("", /*use_login_shell*/ false)[0].clone();
|
||||
let path = startup_dir.join("zsh");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&path,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nexport GH_ENTERPRISE_TOKEN='{REAL_GITHUB_TOKEN}'\nexport CUSTOM_API_KEY='{REAL_CUSTOM_TOKEN}'\nexport AUTH_HEADER=\"Bearer $GH_ENTERPRISE_TOKEN\"\nexec {} \"$@\"\n",
|
||||
shlex::try_quote(&zsh_path)?
|
||||
),
|
||||
@@ -3476,15 +3476,12 @@ async fn env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork(
|
||||
.await?;
|
||||
|
||||
let script_path = test.cwd.path().join("runs-under-env-zsh");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/usr/bin/env zsh\ntouch {outside_path_arg}\nprint -r -- nested-env-zsh-complete\n"
|
||||
),
|
||||
)?;
|
||||
let mut script_permissions = fs::metadata(&script_path)?.permissions();
|
||||
script_permissions.set_mode(0o755);
|
||||
fs::set_permissions(&script_path, script_permissions)?;
|
||||
|
||||
let script_literal = serde_json::to_string(script_path.to_string_lossy().as_ref())?;
|
||||
let python_script = format!(
|
||||
@@ -3920,9 +3917,9 @@ async fn zsh_fork_inner_allowed_script_inherits_active_permission_profile() -> R
|
||||
let remote_bash_path = script_dir.path().join("remote_bash.py");
|
||||
let outside_path = script_dir.path().join("remote-bash-unsandboxed-marker");
|
||||
let outside_path_literal = serde_json::to_string(&outside_path.to_string_lossy())?;
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&remote_bash_path,
|
||||
format!(
|
||||
&format!(
|
||||
r#"#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
@@ -3987,19 +3984,14 @@ if __name__ == "__main__":
|
||||
),
|
||||
)?;
|
||||
let remote_bash_exec = shlex::try_join([remote_bash_path.to_string_lossy().as_ref()])?;
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&wrapper_path,
|
||||
format!(
|
||||
&format!(
|
||||
r#"#!/usr/bin/env zsh
|
||||
exec {remote_bash_exec} "$@"
|
||||
"#
|
||||
),
|
||||
)?;
|
||||
for path in [&wrapper_path, &remote_bash_path] {
|
||||
let mut permissions = fs::metadata(path)?.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(path, permissions)?;
|
||||
}
|
||||
|
||||
let remote_bash_pattern = serde_json::to_string(&remote_bash_path.to_string_lossy())?;
|
||||
let rules = format!(r#"prefix_rule(pattern=[{remote_bash_pattern}], decision="allow")"#);
|
||||
|
||||
@@ -832,7 +832,7 @@ impl ProviderAuthCommandFixture {
|
||||
#[cfg(unix)]
|
||||
let (command, args) = {
|
||||
let script_path = tempdir.path().join("print-token.sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
r#"#!/bin/sh
|
||||
if [ -f fail-until-401 ]; then
|
||||
@@ -844,12 +844,6 @@ tail -n +2 tokens.txt > tokens.next
|
||||
mv tokens.next tokens.txt
|
||||
"#,
|
||||
)?;
|
||||
let mut permissions = std::fs::metadata(&script_path)?.permissions();
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
permissions.set_mode(0o755);
|
||||
}
|
||||
std::fs::set_permissions(&script_path, permissions)?;
|
||||
("./print-token.sh".to_string(), Vec::new())
|
||||
};
|
||||
|
||||
|
||||
@@ -245,16 +245,10 @@ fn bedrock_credential_export_config(
|
||||
#[cfg(unix)]
|
||||
let (command, args) = {
|
||||
let script_path = credentials_path.with_extension("sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
"#!/bin/sh\nprintf 'invoked\\n' >> \"$1\"\ncat \"$2\"\n",
|
||||
)?;
|
||||
let mut permissions = std::fs::metadata(&script_path)?.permissions();
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
permissions.set_mode(0o755);
|
||||
}
|
||||
std::fs::set_permissions(&script_path, permissions)?;
|
||||
(
|
||||
script_path.to_string_lossy().into_owned(),
|
||||
vec![
|
||||
@@ -439,8 +433,9 @@ async fn amazon_bedrock_aws_auth_refresh_resigns() -> anyhow::Result<()> {
|
||||
let aws_executable = fixture
|
||||
.path()
|
||||
.join(format!("aws{}", std::env::consts::EXE_SUFFIX));
|
||||
std::fs::hard_link(&test_executable, &aws_executable)
|
||||
.or_else(|_| std::fs::copy(&test_executable, &aws_executable).map(|_| ()))?;
|
||||
std::fs::hard_link(&test_executable, &aws_executable).or_else(|_| {
|
||||
codex_utils_cargo_bin::copy_executable(&test_executable, &aws_executable)
|
||||
})?;
|
||||
let inherited_path = std::env::var_os("PATH").unwrap_or_default();
|
||||
let mut paths = std::env::split_paths(&inherited_path).collect::<Vec<_>>();
|
||||
paths.insert(/*index*/ 0, fixture.path().to_path_buf());
|
||||
|
||||
@@ -82,7 +82,6 @@ use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use serde_json::json;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
@@ -2538,14 +2537,13 @@ async fn guardian_review_session_does_not_inherit_legacy_notify() -> Result<()>
|
||||
|
||||
let notify_dir = TempDir::new()?;
|
||||
let notify_script = notify_dir.path().join("notify.sh");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
¬ify_script,
|
||||
r#"#!/bin/bash
|
||||
set -e
|
||||
payload_path="$(dirname "${0}")/notify.jsonl"
|
||||
printf '%s\n' "${@: -1}" >> "${payload_path}""#,
|
||||
)?;
|
||||
fs::set_permissions(¬ify_script, fs::Permissions::from_mode(0o755))?;
|
||||
let notify_file = notify_dir.path().join("notify.jsonl");
|
||||
let notify_script_str = notify_script.to_str().unwrap().to_string();
|
||||
let sandbox_policy_for_config = sandbox_policy.clone();
|
||||
|
||||
@@ -1415,7 +1415,10 @@ async fn agent_plugin_root_mcp_stdio_tool_round_trip_expands_reserved_paths_and_
|
||||
}
|
||||
};
|
||||
let stdio_server_name = format!("test_stdio_server{}", std::env::consts::EXE_SUFFIX);
|
||||
std::fs::copy(stdio_server, plugin_root.join(&stdio_server_name))?;
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
std::path::Path::new(&stdio_server),
|
||||
&plugin_root.join(&stdio_server_name),
|
||||
)?;
|
||||
let mcp_config = serde_json::json!({
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
||||
"mcpServers": {
|
||||
|
||||
@@ -2081,7 +2081,8 @@ async fn local_stdio_server_uses_runtime_fallback_cwd_when_config_omits_cwd() ->
|
||||
.parent()
|
||||
.expect("relative test server path should include a parent");
|
||||
fs::create_dir_all(target_dir).expect("create relative MCP bin directory");
|
||||
fs::copy(&rmcp_test_server_bin, &target_bin).expect("copy test stdio server");
|
||||
codex_utils_cargo_bin::copy_executable(&rmcp_test_server_bin, &target_bin)
|
||||
.expect("copy test stdio server");
|
||||
|
||||
insert_mcp_server(
|
||||
config,
|
||||
|
||||
@@ -63,8 +63,6 @@ use core_test_support::wait_for_event_match;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tokio::fs;
|
||||
@@ -763,7 +761,7 @@ async fn legacy_snapshot_omits_filtered_and_overridden_exports() -> Result<()> {
|
||||
let home = tempfile::tempdir()?;
|
||||
let shell_path = home.path().join("bash");
|
||||
// Seed the capture shell without changing the test process environment. Replay uses -c.
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&shell_path,
|
||||
r#"#!/bin/sh
|
||||
if [ "$1" = -lc ]; then
|
||||
@@ -777,9 +775,7 @@ second'
|
||||
fi
|
||||
exec /bin/bash --noprofile --norc "$@"
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
fs::set_permissions(&shell_path, std::fs::Permissions::from_mode(/*mode*/ 0o755)).await?;
|
||||
)?;
|
||||
let builder = test_codex()
|
||||
.with_user_shell(
|
||||
codex_shell_command::shell_detect::DetectedShell {
|
||||
@@ -1206,10 +1202,7 @@ async fn macos_unified_exec_resolves_command_from_tied_path_snapshot(
|
||||
.join("bin");
|
||||
fs::create_dir_all(&command_dir).await?;
|
||||
let command_path = command_dir.join("snapshot-only-command");
|
||||
fs::write(&command_path, "#!/bin/sh\nprintf tied-path-command").await?;
|
||||
let mut permissions = fs::metadata(&command_path).await?.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(&command_path, permissions).await?;
|
||||
codex_utils_cargo_bin::write_executable(&command_path, "#!/bin/sh\nprintf tied-path-command")?;
|
||||
|
||||
run_tool_turn_on_harness(
|
||||
&harness,
|
||||
|
||||
@@ -83,8 +83,6 @@ fn write_skill_with_shell_script_contents(
|
||||
script_name: &str,
|
||||
script_contents: &str,
|
||||
) -> Result<PathBuf> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let skill_dir = home.join("skills").join(name);
|
||||
let scripts_dir = skill_dir.join("scripts");
|
||||
fs::create_dir_all(&scripts_dir)?;
|
||||
@@ -100,10 +98,7 @@ description: {name} skill
|
||||
)?;
|
||||
|
||||
let script_path = scripts_dir.join(script_name);
|
||||
fs::write(&script_path, script_contents)?;
|
||||
let mut permissions = fs::metadata(&script_path)?.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(&script_path, permissions)?;
|
||||
codex_utils_cargo_bin::write_executable(&script_path, script_contents)?;
|
||||
Ok(script_path)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![cfg(not(target_os = "windows"))]
|
||||
|
||||
use codex_core::TurnInputRequest;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
@@ -35,7 +34,7 @@ async fn summarize_context_three_requests_and_instructions() -> anyhow::Result<(
|
||||
let notify_dir = TempDir::new()?;
|
||||
// write a script to the notify that touches a file next to it
|
||||
let notify_script = notify_dir.path().join("notify.sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
¬ify_script,
|
||||
r#"#!/bin/bash
|
||||
set -e
|
||||
@@ -44,7 +43,6 @@ tmp_path="${payload_path}.tmp"
|
||||
echo -n "${@: -1}" > "${tmp_path}"
|
||||
mv "${tmp_path}" "${payload_path}""#,
|
||||
)?;
|
||||
std::fs::set_permissions(¬ify_script, std::fs::Permissions::from_mode(0o755))?;
|
||||
|
||||
let notify_file = notify_dir.path().join("notify.txt");
|
||||
let notify_script_str = notify_script.to_str().unwrap().to_string();
|
||||
|
||||
@@ -122,7 +122,7 @@ fn stage_windows_sandbox_helpers() -> anyhow::Result<()> {
|
||||
let helper = codex_utils_cargo_bin::cargo_bin(helper_name)?;
|
||||
let file_name = Path::new(helper_name).with_extension("exe");
|
||||
let destination = resources_dir.join(file_name);
|
||||
if let Err(err) = std::fs::copy(&helper, &destination) {
|
||||
if let Err(err) = codex_utils_cargo_bin::copy_executable(&helper, &destination) {
|
||||
// A sandbox helper can briefly remain alive after the sandboxed
|
||||
// command exits. Bazel may retry the test while that process still
|
||||
// has the staged executable open, so keep the already-staged copy.
|
||||
@@ -152,18 +152,18 @@ fn stage_windows_sandbox_cli(fixture_bin: &Path) -> anyhow::Result<(PathBuf, Pat
|
||||
|
||||
let codex_source = codex_utils_cargo_bin::cargo_bin("codex")?;
|
||||
let codex = fixture_bin.join("codex.exe");
|
||||
std::fs::copy(&codex_source, &codex)
|
||||
codex_utils_cargo_bin::copy_executable(&codex_source, &codex)
|
||||
.with_context(|| format!("copy {} to {}", codex_source.display(), codex.display()))?;
|
||||
for helper_name in ["codex-windows-sandbox-setup", "codex-command-runner"] {
|
||||
let helper = codex_utils_cargo_bin::cargo_bin(helper_name)?;
|
||||
let destination = resources_dir.join(Path::new(helper_name).with_extension("exe"));
|
||||
std::fs::copy(&helper, &destination)
|
||||
codex_utils_cargo_bin::copy_executable(&helper, &destination)
|
||||
.with_context(|| format!("copy {} to {}", helper.display(), destination.display()))?;
|
||||
}
|
||||
|
||||
let probe_source = codex_utils_cargo_bin::cargo_bin("codex-windows-managed-deny-probe")?;
|
||||
let probe = fixture_bin.join("managed-deny-probe.exe");
|
||||
std::fs::copy(&probe_source, &probe)
|
||||
codex_utils_cargo_bin::copy_executable(&probe_source, &probe)
|
||||
.with_context(|| format!("copy {} to {}", probe_source.display(), probe.display()))?;
|
||||
Ok((codex, probe))
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ mod shell_snapshot;
|
||||
mod windows_sandbox;
|
||||
|
||||
use std::collections::HashMap;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
@@ -236,11 +234,10 @@ async fn shell_snapshot_v2_filters_profile_exports_and_stays_in_memory(
|
||||
let runtime_path_entry = home.path().join("runtime-bin");
|
||||
std::fs::create_dir(&profile_path_entry)?;
|
||||
let wc = profile_path_entry.join("wc");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&wc,
|
||||
"#!/bin/sh\nprintf x >> \"$HOME/tool-captures\"\nexec /usr/bin/wc \"$@\"\n",
|
||||
)?;
|
||||
std::fs::set_permissions(&wc, std::fs::Permissions::from_mode(0o755))?;
|
||||
let posix_shell = matches!(shell_name, "sh" | "bash-sh");
|
||||
let padding = if !use_remote && !tty && shell_name == "bash" {
|
||||
format!(
|
||||
@@ -815,10 +812,7 @@ async fn remote_tty_process_uses_configured_sandbox_helper_with_hostile_path() -
|
||||
let file = workspace.path().join("allowed.txt");
|
||||
std::fs::write(&file, b"allowed")?;
|
||||
let hostile_helper = workspace.path().join("codex-linux-sandbox");
|
||||
std::fs::write(&hostile_helper, b"#!/bin/sh\nprintf hostile")?;
|
||||
let mut permissions = std::fs::metadata(&hostile_helper)?.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&hostile_helper, permissions)?;
|
||||
codex_utils_cargo_bin::write_executable(&hostile_helper, "#!/bin/sh\nprintf hostile")?;
|
||||
let path = std::env::var_os("PATH").context("PATH is not set")?;
|
||||
let hostile_path = std::env::join_paths(
|
||||
std::iter::once(workspace.path().to_path_buf()).chain(std::env::split_paths(&path)),
|
||||
|
||||
@@ -1145,11 +1145,11 @@ async fn sandboxed_file_operations_cannot_read_helper_siblings() -> Result<()> {
|
||||
|
||||
let helper = runtime_dir.join("codex-test-helper");
|
||||
std::fs::hard_link(&helper_paths.codex_exe, &helper)
|
||||
.or_else(|_| std::fs::copy(&helper_paths.codex_exe, &helper).map(|_| ()))?;
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(&helper_paths.codex_exe, &helper))?;
|
||||
let linux_sandbox = if helper_paths.codex_linux_sandbox_exe.is_some() {
|
||||
let alias = runtime_dir.join("codex-linux-sandbox");
|
||||
std::fs::hard_link(&helper, &alias)
|
||||
.or_else(|_| std::fs::copy(&helper, &alias).map(|_| ()))?;
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(&helper, &alias))?;
|
||||
Some(alias)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -473,7 +473,7 @@ async fn file_system_elevated_relative_read_denial_uses_policy_cwd(
|
||||
for name in ["codex-windows-sandbox-setup", "codex-command-runner"] {
|
||||
let source = codex_utils_cargo_bin::cargo_bin(name)?;
|
||||
let destination = resources.join(Path::new(name).with_extension("exe"));
|
||||
if let Err(error) = std::fs::copy(&source, &destination)
|
||||
if let Err(error) = codex_utils_cargo_bin::copy_executable(&source, &destination)
|
||||
&& !(error.kind() == std::io::ErrorKind::PermissionDenied && destination.is_file())
|
||||
{
|
||||
return Err(error).with_context(|| format!("stage Windows sandbox helper {name}"));
|
||||
|
||||
@@ -26,7 +26,7 @@ async fn exec_server_accepts_initialize(version: Option<&str>) -> anyhow::Result
|
||||
let bin_dir = package.path().join("bin");
|
||||
std::fs::create_dir(&bin_dir)?;
|
||||
let executable = bin_dir.join(format!("codex{}", std::env::consts::EXE_SUFFIX));
|
||||
std::fs::copy(std::env::current_exe()?, &executable)?;
|
||||
codex_utils_cargo_bin::copy_executable(&std::env::current_exe()?, &executable)?;
|
||||
let manifest = package.path().join("codex-package.json");
|
||||
if let Some(version) = version {
|
||||
std::fs::write(
|
||||
|
||||
@@ -35,6 +35,7 @@ ts-rs = { workspace = true, features = [
|
||||
walkdir = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -587,8 +587,6 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn write_index_ignores_configured_hooks_path() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let home = TempDir::new().expect("tempdir");
|
||||
let root = home.path().join("repo");
|
||||
let hooks_dir = root.join(".git/hooks-path-test");
|
||||
@@ -599,19 +597,14 @@ mod tests {
|
||||
fs::write(root.join("MEMORY.md"), "baseline").expect("write memory");
|
||||
reset_git_repository(&root).await.expect("reset repo");
|
||||
fs::create_dir_all(&hooks_dir).expect("create hook dir");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&hook_path,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nprintf ran > \"{}\"\n",
|
||||
marker_path.to_string_lossy()
|
||||
),
|
||||
)
|
||||
.expect("write post-index-change hook");
|
||||
let mut permissions = fs::metadata(&hook_path)
|
||||
.expect("read hook metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
fs::set_permissions(&hook_path, permissions).expect("mark hook executable");
|
||||
git_stdout(
|
||||
&root,
|
||||
&[
|
||||
|
||||
@@ -836,8 +836,6 @@ pub async fn current_branch_name(cwd: &Path) -> Option<String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::process::Stdio;
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1071,7 +1069,7 @@ mod tests {
|
||||
let temp_dir = tempfile::tempdir().expect("create temp dir");
|
||||
let git = temp_dir.path().join("git");
|
||||
let log = temp_dir.path().join("git.log");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&git,
|
||||
"#!/bin/sh\n\
|
||||
if [ \"$1\" = \"-c\" ] && [ \"$2\" = \"safe.bareRepository=explicit\" ]; then shift 2; fi\n\
|
||||
@@ -1082,11 +1080,6 @@ mod tests {
|
||||
esac\n",
|
||||
)
|
||||
.expect("write fake Git");
|
||||
let mut permissions = std::fs::metadata(&git)
|
||||
.expect("read fake Git metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&git, permissions).expect("mark fake Git executable");
|
||||
|
||||
// The config response mirrors:
|
||||
// git -c core.fsmonitor=/tmp/fsmonitor-helper \
|
||||
@@ -1137,7 +1130,7 @@ mod tests {
|
||||
let git = temp_dir.path().join("git");
|
||||
let global_config = temp_dir.path().join("git.global");
|
||||
let log = temp_dir.path().join("git.log");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&git,
|
||||
"#!/bin/sh\n\
|
||||
if [ \"$1\" = \"-c\" ] && [ \"$2\" = \"safe.bareRepository=explicit\" ]; then shift 2; fi\n\
|
||||
@@ -1151,11 +1144,6 @@ mod tests {
|
||||
esac\n",
|
||||
)
|
||||
.expect("write layered-config Git");
|
||||
let mut permissions = std::fs::metadata(&git)
|
||||
.expect("read layered-config Git metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(0o755);
|
||||
std::fs::set_permissions(&git, permissions).expect("mark layered-config Git executable");
|
||||
|
||||
let global_status = std::process::Command::new("git")
|
||||
.args([
|
||||
|
||||
@@ -311,8 +311,6 @@ mod tests {
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent).expect("create parent dir");
|
||||
}
|
||||
fs::write(path, b"").expect("write executable");
|
||||
fs::set_permissions(path, fs::Permissions::from_mode(0o755))
|
||||
.expect("set executable permissions");
|
||||
codex_utils_cargo_bin::write_executable(path, "").expect("write executable");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,6 @@ fn exec_system_bwrap(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
@@ -308,13 +307,11 @@ mod tests {
|
||||
fn detects_fd_backed_read_only_mount_support_in_system_bwrap_help() {
|
||||
let temp_dir = tempfile::tempdir().expect("temp directory");
|
||||
let fake_bwrap_path = temp_dir.path().join("bwrap");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&fake_bwrap_path,
|
||||
"#!/bin/sh\nprintf '%s\\n' '--as-pid-1' '--perms' '--argv0' '--ro-bind-fd'\n",
|
||||
)
|
||||
.expect("write fake bubblewrap");
|
||||
std::fs::set_permissions(&fake_bwrap_path, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("make fake bubblewrap executable");
|
||||
|
||||
assert_eq!(
|
||||
system_bwrap_capabilities(&fake_bwrap_path),
|
||||
|
||||
@@ -66,7 +66,11 @@ fn bazel_build_rejects_tampered_bundled_bwrap() {
|
||||
std::fs::remove_file(&bundled_bwrap).expect("read-only bundled bwrap should be replaceable");
|
||||
let mut tampered_bwrap_bytes = original_bwrap_bytes;
|
||||
tampered_bwrap_bytes.push(0);
|
||||
std::fs::write(&bundled_bwrap, &tampered_bwrap_bytes)
|
||||
// Write data to a separate inode, then publish it through the descriptor-safe helper.
|
||||
let tampered_data = package.path().join("tampered-bwrap-data");
|
||||
std::fs::write(&tampered_data, &tampered_bwrap_bytes)
|
||||
.expect("modified bwrap data should be written");
|
||||
copy_executable(&tampered_data, &bundled_bwrap)
|
||||
.expect("modified bwrap should be copied into the package");
|
||||
let bwrap_permissions = std::fs::metadata(&bwrap_binary)
|
||||
.expect("built bwrap metadata should be readable")
|
||||
|
||||
@@ -14,8 +14,12 @@ async fn private_tmp_mount_preserves_daemon_socket_isolation() {
|
||||
let private = tempfile::tempdir_in("/tmp").unwrap();
|
||||
let test_executable = std::env::current_exe().unwrap();
|
||||
// Keep both executables available after the original /tmp is hidden.
|
||||
std::fs::copy(&test_executable, private.path().join("test")).unwrap();
|
||||
std::fs::copy(codex_linux_sandbox_exe(), private.path().join("sandbox")).unwrap();
|
||||
codex_utils_cargo_bin::copy_executable(&test_executable, &private.path().join("test")).unwrap();
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&codex_linux_sandbox_exe(),
|
||||
&private.path().join("sandbox"),
|
||||
)
|
||||
.unwrap();
|
||||
let (_, test_module) = module_path!().split_once("::").unwrap();
|
||||
let fixture_test = format!("{test_module}::private_tmp_fixture");
|
||||
// Mount setup needs capabilities, which bubblewrap only accepts as namespace root.
|
||||
|
||||
@@ -27,7 +27,6 @@ use std::net::Shutdown;
|
||||
use std::net::TcpListener;
|
||||
use std::net::TcpStream;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::process::Output;
|
||||
use std::process::Stdio;
|
||||
@@ -322,7 +321,7 @@ async fn proc_mount_denial_preserves_legacy_fallback_and_explicit_pid_inheritanc
|
||||
};
|
||||
let tempdir = tempfile::tempdir().expect("create PID namespace fixture");
|
||||
let wrapper = tempdir.path().join("bwrap");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&wrapper,
|
||||
r#"#!/bin/sh
|
||||
for arg in "$@"; do
|
||||
@@ -336,8 +335,6 @@ exec "$CODEX_TEST_REAL_BWRAP" "$@"
|
||||
"#,
|
||||
)
|
||||
.expect("write proc-denying bubblewrap wrapper");
|
||||
std::fs::set_permissions(&wrapper, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("make bubblewrap wrapper executable");
|
||||
let protected_file = tempdir.path().join("protected");
|
||||
std::fs::write(&protected_file, "original").expect("write protected file");
|
||||
let pid_namespace = std::fs::read_link("/proc/self/ns/pid").expect("read caller PID namespace");
|
||||
@@ -473,7 +470,8 @@ async fn unsupported_system_bwrap_falls_back_to_bundled_bwrap() {
|
||||
let sandbox_executable = tempdir.path().join("codex-linux-sandbox");
|
||||
let original_executable = env!("CARGO_BIN_EXE_codex-linux-sandbox");
|
||||
if std::fs::hard_link(original_executable, &sandbox_executable).is_err() {
|
||||
std::fs::copy(original_executable, &sandbox_executable).expect("copy sandbox executable");
|
||||
codex_utils_cargo_bin::copy_executable(Path::new(original_executable), &sandbox_executable)
|
||||
.expect("copy sandbox executable");
|
||||
}
|
||||
|
||||
let resources_dir = tempdir.path().join("codex-resources");
|
||||
@@ -484,13 +482,11 @@ async fn unsupported_system_bwrap_falls_back_to_bundled_bwrap() {
|
||||
let system_dir = tempdir.path().join("system");
|
||||
std::fs::create_dir(&system_dir).expect("create fake system binary directory");
|
||||
let unsupported_bwrap = system_dir.join("bwrap");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&unsupported_bwrap,
|
||||
"#!/bin/sh\nif [ \"$1\" = \"--help\" ]; then printf '%s\\n' '--perms'; exit 0; fi\nexit 91\n",
|
||||
)
|
||||
.expect("write unsupported system bubblewrap");
|
||||
std::fs::set_permissions(&unsupported_bwrap, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("make unsupported system bubblewrap executable");
|
||||
|
||||
let mut env = create_env_from_core_vars();
|
||||
strip_proxy_env(&mut env);
|
||||
|
||||
@@ -30,7 +30,8 @@ async fn root_metadata_symlinks_cannot_reopen_approved_denials() {
|
||||
fs::write(work.join("public/secret"), "outside").unwrap();
|
||||
fs::write(private.join("reopened/secret"), "private").unwrap();
|
||||
fs::write(private.join("sibling"), "sibling").unwrap();
|
||||
fs::copy(codex_linux_sandbox_exe(), work.join("sandbox")).unwrap();
|
||||
codex_utils_cargo_bin::copy_executable(&codex_linux_sandbox_exe(), &work.join("sandbox"))
|
||||
.unwrap();
|
||||
|
||||
let path_entry = |path: &str, access| {
|
||||
FileSystemSandboxEntry::new(
|
||||
|
||||
@@ -45,6 +45,7 @@ urlencoding = { workspace = true }
|
||||
webbrowser = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
opentelemetry_sdk = { workspace = true, features = ["testing"] }
|
||||
core_test_support = { workspace = true }
|
||||
jsonwebtoken = { workspace = true }
|
||||
|
||||
@@ -1654,7 +1654,7 @@ impl ProviderAuthScript {
|
||||
#[cfg(unix)]
|
||||
let (command, args) = {
|
||||
let script_path = tempdir.path().join("print-token.sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
r#"#!/bin/sh
|
||||
if [ -f fail-once ]; then
|
||||
@@ -1667,12 +1667,6 @@ tail -n +2 tokens.txt > tokens.next
|
||||
mv tokens.next tokens.txt
|
||||
"#,
|
||||
)?;
|
||||
let mut permissions = std::fs::metadata(&script_path)?.permissions();
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
permissions.set_mode(0o755);
|
||||
}
|
||||
std::fs::set_permissions(&script_path, permissions)?;
|
||||
("./print-token.sh".to_string(), Vec::new())
|
||||
};
|
||||
|
||||
@@ -1721,18 +1715,12 @@ move /y tokens.next tokens.txt >nul
|
||||
#[cfg(unix)]
|
||||
let (command, args) = {
|
||||
let script_path = tempdir.path().join("fail.sh");
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
r#"#!/bin/sh
|
||||
exit 1
|
||||
"#,
|
||||
)?;
|
||||
let mut permissions = std::fs::metadata(&script_path)?.permissions();
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
permissions.set_mode(0o755);
|
||||
}
|
||||
std::fs::set_permissions(&script_path, permissions)?;
|
||||
("./fail.sh".to_string(), Vec::new())
|
||||
};
|
||||
|
||||
|
||||
@@ -49,8 +49,6 @@ fn test_get_codex_user_agent() {
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn os_discovery_is_cached_without_freezing_user_agent_overrides() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
const PROBE_DIR: &str = "CODEX_TEST_USER_AGENT_PROBE_DIR";
|
||||
if std::env::var_os(PROBE_DIR).is_some() {
|
||||
// Race the first lookup as well as exercising repeated requests.
|
||||
@@ -79,15 +77,13 @@ fn os_discovery_is_cached_without_freezing_user_agent_overrides() {
|
||||
("getconf", "64\n"),
|
||||
] {
|
||||
let script = temp.path().join(program);
|
||||
std::fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\nprintf '{program}\\n' >> \"${PROBE_DIR}/calls\"\nprintf '{output}'\n"
|
||||
),
|
||||
)
|
||||
.expect("write probe");
|
||||
std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755))
|
||||
.expect("make probe executable");
|
||||
}
|
||||
let output = std::process::Command::new(std::env::current_exe().expect("test executable"))
|
||||
.args([
|
||||
|
||||
@@ -38,6 +38,7 @@ url = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = { workspace = true }
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
codex-utils-redacted-string = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -914,7 +914,7 @@ mod tests {
|
||||
let executable = std::env::current_exe().expect("test executable should be available");
|
||||
let aws = counter.join(format!("aws{}", std::env::consts::EXE_SUFFIX));
|
||||
std::fs::hard_link(&executable, &aws)
|
||||
.or_else(|_| std::fs::copy(&executable, &aws).map(|_| ()))
|
||||
.or_else(|_| codex_utils_cargo_bin::copy_executable(&executable, &aws))
|
||||
.expect("test executable should be installed as aws");
|
||||
let existing_path = std::env::var_os("PATH").unwrap_or_default();
|
||||
let path = std::env::join_paths(
|
||||
|
||||
@@ -22,6 +22,7 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread", "sync", "ti
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
ctor = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
|
||||
@@ -77,8 +77,11 @@ pub fn package(test: &str) -> Result<Option<PathBuf>> {
|
||||
fs::create_dir_all(root.join("bin"))?;
|
||||
fs::create_dir_all(root.join("codex-resources/voice/bin"))?;
|
||||
fs::write(root.join("codex-package.json"), "{}")?;
|
||||
fs::copy(&source, root.join("bin").join(APP))?;
|
||||
fs::copy(&source, root.join("codex-resources/voice/bin").join(HELPER))?;
|
||||
codex_utils_cargo_bin::copy_executable(&source, &root.join("bin").join(APP))?;
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&source,
|
||||
&root.join("codex-resources/voice/bin").join(HELPER),
|
||||
)?;
|
||||
let mut child = Command::new(root.join("bin").join(APP))
|
||||
.args(["--exact", test, "--nocapture"])
|
||||
.current_dir(root)
|
||||
|
||||
@@ -8,7 +8,6 @@ use std::fs;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
@@ -93,8 +92,7 @@ for line in sys.stdin:
|
||||
let wrapper = temporary.path().join("executable-text");
|
||||
// No shebang: exercise Command's shell fallback after native macOS spawn
|
||||
// returns ENOEXEC, including the fallback descriptor cleanup.
|
||||
fs::write(&wrapper, "exec \"$@\"\n")?;
|
||||
fs::set_permissions(&wrapper, fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&wrapper, "exec \"$@\"\n")?;
|
||||
for (launch, program, mut args) in [
|
||||
("absolute", python.clone().into_os_string(), Vec::new()),
|
||||
("relative", OsString::from("./python-fixture"), Vec::new()),
|
||||
@@ -170,8 +168,7 @@ async fn local_stdio_preserves_exec_failure_reporting() -> Result<()> {
|
||||
let temporary = tempfile::tempdir()?;
|
||||
let script = temporary.path().join("missing-interpreter");
|
||||
let interpreter = temporary.path().join("does-not-exist");
|
||||
fs::write(&script, format!("#!{}\n", interpreter.display()))?;
|
||||
fs::set_permissions(&script, fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, &format!("#!{}\n", interpreter.display()))?;
|
||||
|
||||
// Program resolution succeeds, but exec fails. Descriptor cleanup must
|
||||
// preserve the internal error pipe so launch reports the failure directly.
|
||||
|
||||
@@ -37,6 +37,7 @@ codex-utils-home-dir = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "rt", "time"] }
|
||||
|
||||
@@ -171,8 +171,6 @@ fn write_fake_bwrap(contents: &str) -> tempfile::TempPath {
|
||||
}
|
||||
|
||||
fn write_fake_bwrap_in(dir: &Path, contents: &str) -> tempfile::TempPath {
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
// Bazel can mount the OS temp directory `noexec`, so prefer the current
|
||||
@@ -181,21 +179,18 @@ fn write_fake_bwrap_in(dir: &Path, contents: &str) -> tempfile::TempPath {
|
||||
let temp_file = NamedTempFile::new_in(dir)
|
||||
.ok()
|
||||
.unwrap_or_else(|| NamedTempFile::new().expect("temp file"));
|
||||
// Linux rejects exec-ing a file that is still open for writing.
|
||||
let path = temp_file.into_temp_path();
|
||||
fs::write(&path, contents).expect("write fake bwrap");
|
||||
let permissions = fs::Permissions::from_mode(0o755);
|
||||
fs::set_permissions(&path, permissions).expect("chmod fake bwrap");
|
||||
// A sibling spawn may retain the temporary file's writable descriptor.
|
||||
// Remove that inode before the helper creates the executable in its child.
|
||||
std::fs::remove_file(&path).expect("remove empty temporary file");
|
||||
codex_utils_cargo_bin::write_executable(&path, contents).expect("write fake bwrap");
|
||||
path
|
||||
}
|
||||
|
||||
fn write_named_fake_bwrap_in(dir: &Path) -> PathBuf {
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let path = dir.join("bwrap");
|
||||
fs::write(&path, "#!/bin/sh\n").expect("write fake bwrap");
|
||||
let permissions = fs::Permissions::from_mode(0o755);
|
||||
fs::set_permissions(&path, permissions).expect("chmod fake bwrap");
|
||||
codex_utils_cargo_bin::write_executable(&path, "#!/bin/sh\n").expect("write fake bwrap");
|
||||
fs::canonicalize(path).expect("canonicalize fake bwrap")
|
||||
}
|
||||
|
||||
@@ -294,14 +294,13 @@ mod tests {
|
||||
#[tokio::test]
|
||||
#[cfg(unix)]
|
||||
async fn run_editor_returns_updated_content() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let script_path = dir.path().join("edit.sh");
|
||||
fs::write(&script_path, "#!/bin/sh\nprintf \"edited\" > \"$1\"\n").unwrap();
|
||||
let mut perms = fs::metadata(&script_path).unwrap().permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(&script_path, perms).unwrap();
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script_path,
|
||||
"#!/bin/sh\nprintf \"edited\" > \"$1\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let cmd = vec![script_path.to_string_lossy().to_string()];
|
||||
let policy = FileSystemSandboxPolicy::read_only();
|
||||
|
||||
@@ -267,8 +267,6 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
use std::fs;
|
||||
use std::future::Future;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
#[cfg(unix)]
|
||||
@@ -823,13 +821,11 @@ mod tests {
|
||||
|
||||
#[cfg(unix)]
|
||||
fn write_marker_helper(path: &Path) {
|
||||
fs::write(path, "#!/bin/sh\nprintf ran >> \"$0.ran\"\nexit 1\n")
|
||||
.expect("write helper script");
|
||||
let mut permissions = fs::metadata(path)
|
||||
.expect("read helper metadata")
|
||||
.permissions();
|
||||
permissions.set_mode(/*mode*/ 0o755);
|
||||
fs::set_permissions(path, permissions).expect("make helper executable");
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
path,
|
||||
"#!/bin/sh\nprintf ran >> \"$0.ran\"\nexit 1\n",
|
||||
)
|
||||
.expect("write helper script");
|
||||
}
|
||||
|
||||
fn assert_command_metadata(commands: &[WorkspaceCommand], cwd: &Path) {
|
||||
|
||||
@@ -4,8 +4,6 @@ use super::PtyCodex;
|
||||
use super::write_test_config;
|
||||
use anyhow::Result;
|
||||
use anyhow::ensure;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -22,9 +20,9 @@ fn external_editors_keep_the_screen_and_return_the_draft() -> Result<()> {
|
||||
let home = tempfile::tempdir()?;
|
||||
write_test_config(home.path(), &repo_root)?;
|
||||
let editor = home.path().join("editor.sh");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&editor,
|
||||
format!(
|
||||
&format!(
|
||||
"#!/bin/sh\n{editor_output}\n: > \"$0.ready\"\nIFS= read -r ignored\n\
|
||||
{exit_screen}\nprintf 'edited draft' > \"$1\"\n",
|
||||
exit_screen = if editor_output.contains("1049h") {
|
||||
@@ -34,7 +32,6 @@ fn external_editors_keep_the_screen_and_return_the_draft() -> Result<()> {
|
||||
},
|
||||
),
|
||||
)?;
|
||||
fs::set_permissions(&editor, fs::Permissions::from_mode(0o755))?;
|
||||
let args = if fullscreen {
|
||||
vec!["-c", "sandbox_mode=\"read-only\""]
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ portable-pty = { workspace = true }
|
||||
tokio = { workspace = true, features = ["io-util", "macros", "net", "process", "rt-multi-thread", "signal", "sync", "time"] }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
ctor = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -215,12 +215,10 @@ async fn preserved_descriptor_can_use_the_last_slot_below_the_limit() -> anyhow:
|
||||
#[tokio::test]
|
||||
async fn process_mode_is_preserved_by_both_backends() -> anyhow::Result<()> {
|
||||
use crate::ProcessMode;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let root = tempfile::tempdir()?;
|
||||
std::os::unix::fs::symlink("/bin/cat", root.path().join("server"))?;
|
||||
let script = root.path().join("executable-text");
|
||||
std::fs::write(&script, "exec /bin/cat\n")?;
|
||||
std::fs::set_permissions(&script, std::fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, "exec /bin/cat\n")?;
|
||||
for program in ["./server", "/bin/cat", "./executable-text"] {
|
||||
for mode in [
|
||||
ProcessMode::Inherit,
|
||||
@@ -368,8 +366,6 @@ async fn detached_spawn_preserves_child_path_cwd_environment_and_arg0() -> anyho
|
||||
|
||||
#[tokio::test]
|
||||
async fn detached_spawn_preserves_exec_errors_and_executable_text_handling() -> anyhow::Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let root = tempfile::tempdir()?;
|
||||
let program = root.path().join("script");
|
||||
let mut missing = Command::new(&program);
|
||||
@@ -377,8 +373,7 @@ async fn detached_spawn_preserves_exec_errors_and_executable_text_handling() ->
|
||||
let error = missing.spawn().err().expect("missing executable must fail");
|
||||
assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
|
||||
|
||||
std::fs::write(&program, "printf '%s' \"$1\"")?;
|
||||
std::fs::set_permissions(&program, std::fs::Permissions::from_mode(0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&program, "printf '%s' \"$1\"")?;
|
||||
// Match the existing libc behavior: glibc/macOS retry executable text through
|
||||
// a shell, whereas musl returns ENOEXEC.
|
||||
let mut baseline = tokio::process::Command::new(&program);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Linux executable lookup must match the compatibility launcher's PATH semantics.
|
||||
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::Command;
|
||||
@@ -13,8 +11,7 @@ async fn path_search_stops_at_invalid_candidates() -> anyhow::Result<()> {
|
||||
std::fs::create_dir(root.path().join("bin"))?;
|
||||
std::os::unix::fs::symlink("loop", root.path().join("loop"))?;
|
||||
let script = root.path().join("bin/server");
|
||||
std::fs::write(&script, "#!/bin/sh\nprintf later\n")?;
|
||||
std::fs::set_permissions(script, std::fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, "#!/bin/sh\nprintf later\n")?;
|
||||
for candidate in ["loop".to_owned(), "x".repeat(256)] {
|
||||
let mut command = Command::new("server");
|
||||
command
|
||||
@@ -49,8 +46,7 @@ async fn empty_path_entries_preserve_script_argv0() -> anyhow::Result<()> {
|
||||
}
|
||||
let root = tempfile::tempdir()?;
|
||||
let script = root.path().join("server");
|
||||
std::fs::write(&script, "#!/bin/sh\nprintf '%s' \"$0\"\n")?;
|
||||
std::fs::set_permissions(script, std::fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, "#!/bin/sh\nprintf '%s' \"$0\"\n")?;
|
||||
for path in ["", ":missing", "missing:", ".", "./"] {
|
||||
let mut command = Command::new("server");
|
||||
command.current_dir(root.path()).env("PATH", path);
|
||||
|
||||
@@ -10,7 +10,6 @@ use std::os::fd::FromRawFd;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
use std::ptr;
|
||||
@@ -35,11 +34,10 @@ async fn bare_script_search_matches_child_path_and_preserves_script_spelling() -
|
||||
fs::create_dir(&blocked)?;
|
||||
fs::write(blocked.join(&program), "not executable")?;
|
||||
let script = bin.join(&program);
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script,
|
||||
"#!/bin/sh\nprintf '%s\\n' \"$0\" \"$1\" \"$MCP_TEST\"; /bin/pwd; printf diagnostic >&2; exit 23\n",
|
||||
)?;
|
||||
fs::set_permissions(&script, fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
symlink(&script, root.path().join(&program))?;
|
||||
for path in [
|
||||
bin.into_os_string(),
|
||||
@@ -79,11 +77,10 @@ async fn relative_script_preserves_paths_stdio_environment_and_process_group() -
|
||||
fs::create_dir_all(root.path().join("actual/bin"))?;
|
||||
symlink("actual/bin", root.path().join("link"))?;
|
||||
let script = root.path().join("actual/server");
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
&script,
|
||||
"#!/bin/sh\nread -r input\nprintf '%s\\n' \"$0\" \"$1\" \"$2\" \"$MCP_TEST\" \"$input\"\nprintf diagnostic >&2\nexit 23\n",
|
||||
)?;
|
||||
fs::set_permissions(script, fs::Permissions::from_mode(0o755))?;
|
||||
let mut command = Command::new("./link/../server");
|
||||
command
|
||||
.current_dir(root.path())
|
||||
@@ -232,8 +229,7 @@ async fn launch_failures_preserve_os_errors() -> anyhow::Result<()> {
|
||||
async fn executable_text_without_shebang_retains_command_fallback() -> anyhow::Result<()> {
|
||||
let root = tempfile::tempdir()?;
|
||||
let script = root.path().join("server");
|
||||
fs::write(&script, "printf '%s' \"$0\"\n")?;
|
||||
fs::set_permissions(script, fs::Permissions::from_mode(0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, "printf '%s' \"$0\"\n")?;
|
||||
for program in ["./server", "server"] {
|
||||
let mut command = Command::new(program);
|
||||
command
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::io::Seek;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
@@ -85,8 +84,7 @@ async fn socket_stdin_preserves_bidirectional_io_and_custom_argv0() -> anyhow::R
|
||||
async fn native_launch_can_reject_executable_text_without_shell_fallback() -> anyhow::Result<()> {
|
||||
let directory = tempfile::tempdir()?;
|
||||
let script = directory.path().join("no-shebang");
|
||||
fs::write(&script, "printf unexpected-fallback\n")?;
|
||||
fs::set_permissions(&script, fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, "printf unexpected-fallback\n")?;
|
||||
let mut command = Command::new(script);
|
||||
command.fallback(SpawnFallback::ReturnError);
|
||||
let error = command.spawn().err().expect("executable text must fail");
|
||||
@@ -114,8 +112,7 @@ async fn inherited_descriptors_survive_native_and_executable_text_spawns() -> an
|
||||
command
|
||||
} else {
|
||||
let script = directory.path().join("executable-text");
|
||||
fs::write(&script, format!("/bin/cat {descriptor}\n"))?;
|
||||
fs::set_permissions(&script, fs::Permissions::from_mode(/*mode*/ 0o755))?;
|
||||
codex_utils_cargo_bin::write_executable(&script, &format!("/bin/cat {descriptor}\n"))?;
|
||||
Command::new(script)
|
||||
};
|
||||
command
|
||||
|
||||
@@ -42,8 +42,8 @@ fn conpty_ignores_dll_in_current_directory() -> anyhow::Result<()> {
|
||||
let candidate = temp_dir.join("conpty.dll");
|
||||
// A kernel32 copy exports the ConPTY functions, so the old bare-name probe
|
||||
// would successfully load and retain this repository-controlled file.
|
||||
fs::copy(
|
||||
PathBuf::from(system_root)
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&PathBuf::from(system_root)
|
||||
.join("System32")
|
||||
.join("kernel32.dll"),
|
||||
&candidate,
|
||||
|
||||
@@ -91,7 +91,7 @@ fn install_helper(root: &Path) -> Result<CodexPackageLayout> {
|
||||
fs::write(&app, [])?;
|
||||
let source = cargo_bin("codex-voice-host")?;
|
||||
let helper = helper_dir.join(source.file_name().context("helper binary file name")?);
|
||||
fs::copy(&source, &helper)?;
|
||||
codex_utils_cargo_bin::copy_executable(&source, &helper)?;
|
||||
install_startup_libraries(helper_dir.parent().context("helper runtime directory")?)?;
|
||||
InstallContext::from_exe(
|
||||
/*is_macos*/ cfg!(target_os = "macos"),
|
||||
@@ -160,7 +160,7 @@ async fn installed_client_accepts_non_utf8_package_path() -> Result<()> {
|
||||
let app = bin.join("codex");
|
||||
fs::write(&app, [])?;
|
||||
let source = cargo_bin("codex-voice-host")?;
|
||||
fs::copy(&source, helper_dir.join(source.file_name().unwrap()))?;
|
||||
codex_utils_cargo_bin::copy_executable(&source, &helper_dir.join(source.file_name().unwrap()))?;
|
||||
install_startup_libraries(helper_dir.parent().unwrap())?;
|
||||
let package = InstallContext::from_exe(
|
||||
/*is_macos*/ cfg!(target_os = "macos"),
|
||||
|
||||
@@ -48,7 +48,7 @@ async fn relocated_runtime_initializes_closes_and_rejects_duplicate_initializati
|
||||
let helper_source = cargo_bin("codex-voice-host")?;
|
||||
let name = helper_source.file_name().context("helper filename")?;
|
||||
fs::create_dir_all(runtime.join("bin"))?;
|
||||
fs::copy(&helper_source, runtime.join("bin").join(name))?;
|
||||
codex_utils_cargo_bin::copy_executable(&helper_source, &runtime.join("bin").join(name))?;
|
||||
fs::create_dir(root.join("bin"))?;
|
||||
let app_name = if cfg!(windows) { "codex.exe" } else { "codex" };
|
||||
fs::write(root.join("bin").join(app_name), [])?;
|
||||
|
||||
@@ -102,5 +102,6 @@ features = [
|
||||
version = "0.52"
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
codex-windows-sandbox-test-support = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -30,10 +30,10 @@ fn refresh_helper_has_no_console() -> Result<()> {
|
||||
let temporary = tempfile::tempdir()?;
|
||||
let executable = std::env::current_exe()?;
|
||||
let detached = temporary.path().join("detached.exe");
|
||||
fs::copy(&executable, &detached)?;
|
||||
fs::copy(
|
||||
codex_utils_cargo_bin::copy_executable(&executable, &detached)?;
|
||||
codex_utils_cargo_bin::copy_executable(
|
||||
&executable,
|
||||
temporary.path().join("codex-windows-sandbox-setup.exe"),
|
||||
&temporary.path().join("codex-windows-sandbox-setup.exe"),
|
||||
)?;
|
||||
let output = Command::new(detached)
|
||||
.args(["--exact", DETACHED_TEST, "--nocapture"])
|
||||
|
||||
@@ -21,4 +21,5 @@ tempfile = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-utils-cargo-bin = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -156,14 +156,11 @@ fn checkout_does_not_execute_configured_hooks_fsmonitor_or_filters() -> Result<(
|
||||
|
||||
#[cfg(unix)]
|
||||
fn write_marker_script(path: &Path, marker: &Path) -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let marker = marker.to_str().context("marker path is not valid UTF-8")?;
|
||||
fs::write(
|
||||
codex_utils_cargo_bin::write_executable(
|
||||
path,
|
||||
format!("#!/bin/sh\nprintf executed > '{marker}'\ncat\n"),
|
||||
&format!("#!/bin/sh\nprintf executed > '{marker}'\ncat\n"),
|
||||
)?;
|
||||
fs::set_permissions(path, fs::Permissions::from_mode(0o755))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user