feat: use the arg0 trick with apply_patch (#2646)
Historically, Codex CLI has treated `apply_patch` (and its sometimes misspelling, `applypatch`) as a "virtual CLI," intercepting it when it appears as the first arg to `command` for the `"container.exec", `"shell"`, or `"local_shell"` tools. This approach has a known limitation where if, say, the model created a Python script that runs `apply_patch` and then tried to run the Python script, we have no insight as to what the model is trying to do and the Python Script would fail because `apply_patch` was never really on the `PATH`. One way to solve this problem is to require users to install an `apply_patch` executable alongside the `codex` executable (or at least put it someplace where Codex can discover it). Though to keep Codex CLI as a standalone executable, we exploit "the arg0 trick" where we create a temporary directory with an entry named `apply_patch` and prepend that directory to the `PATH` for the duration of the invocation of Codex. - On UNIX, `apply_patch` is a symlink to `codex`, which now changes its behavior to behave like `apply_patch` if arg0 is `apply_patch` (or `applypatch`) - On Windows, `apply_patch.bat` is a batch script that runs `codex --codex-run-as-apply-patch %*`, as Codex also changes its behavior if the first argument is `--codex-run-as-apply-patch`.
This commit is contained in:
3
codex-rs/apply-patch/tests/all.rs
Normal file
3
codex-rs/apply-patch/tests/all.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// Single integration test binary that aggregates all test modules.
|
||||
// The submodules live in `tests/suite/`.
|
||||
mod suite;
|
||||
90
codex-rs/apply-patch/tests/suite/cli.rs
Normal file
90
codex-rs/apply-patch/tests/suite/cli.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use assert_cmd::prelude::*;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_apply_patch_cli_add_and_update() -> anyhow::Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
let file = "cli_test.txt";
|
||||
let absolute_path = tmp.path().join(file);
|
||||
|
||||
// 1) Add a file
|
||||
let add_patch = format!(
|
||||
r#"*** Begin Patch
|
||||
*** Add File: {file}
|
||||
+hello
|
||||
*** End Patch"#
|
||||
);
|
||||
Command::cargo_bin("apply_patch")
|
||||
.expect("should find apply_patch binary")
|
||||
.arg(add_patch)
|
||||
.current_dir(tmp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(format!("Success. Updated the following files:\nA {file}\n"));
|
||||
assert_eq!(fs::read_to_string(&absolute_path)?, "hello\n");
|
||||
|
||||
// 2) Update the file
|
||||
let update_patch = format!(
|
||||
r#"*** Begin Patch
|
||||
*** Update File: {file}
|
||||
@@
|
||||
-hello
|
||||
+world
|
||||
*** End Patch"#
|
||||
);
|
||||
Command::cargo_bin("apply_patch")
|
||||
.expect("should find apply_patch binary")
|
||||
.arg(update_patch)
|
||||
.current_dir(tmp.path())
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(format!("Success. Updated the following files:\nM {file}\n"));
|
||||
assert_eq!(fs::read_to_string(&absolute_path)?, "world\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_patch_cli_stdin_add_and_update() -> anyhow::Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
let file = "cli_test_stdin.txt";
|
||||
let absolute_path = tmp.path().join(file);
|
||||
|
||||
// 1) Add a file via stdin
|
||||
let add_patch = format!(
|
||||
r#"*** Begin Patch
|
||||
*** Add File: {file}
|
||||
+hello
|
||||
*** End Patch"#
|
||||
);
|
||||
let mut cmd =
|
||||
assert_cmd::Command::cargo_bin("apply_patch").expect("should find apply_patch binary");
|
||||
cmd.current_dir(tmp.path());
|
||||
cmd.write_stdin(add_patch)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(format!("Success. Updated the following files:\nA {file}\n"));
|
||||
assert_eq!(fs::read_to_string(&absolute_path)?, "hello\n");
|
||||
|
||||
// 2) Update the file via stdin
|
||||
let update_patch = format!(
|
||||
r#"*** Begin Patch
|
||||
*** Update File: {file}
|
||||
@@
|
||||
-hello
|
||||
+world
|
||||
*** End Patch"#
|
||||
);
|
||||
let mut cmd =
|
||||
assert_cmd::Command::cargo_bin("apply_patch").expect("should find apply_patch binary");
|
||||
cmd.current_dir(tmp.path());
|
||||
cmd.write_stdin(update_patch)
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(format!("Success. Updated the following files:\nM {file}\n"));
|
||||
assert_eq!(fs::read_to_string(&absolute_path)?, "world\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1
codex-rs/apply-patch/tests/suite/mod.rs
Normal file
1
codex-rs/apply-patch/tests/suite/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
mod cli;
|
||||
Reference in New Issue
Block a user