2025-12-23 19:29:32 -08:00
|
|
|
use assert_cmd::Command;
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
use std::fs;
|
|
|
|
|
use tempfile::tempdir;
|
|
|
|
|
|
2025-12-23 19:29:32 -08:00
|
|
|
fn apply_patch_command() -> anyhow::Result<Command> {
|
|
|
|
|
Ok(Command::new(codex_utils_cargo_bin::cargo_bin(
|
|
|
|
|
"apply_patch",
|
|
|
|
|
)?))
|
|
|
|
|
}
|
|
|
|
|
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
#[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"#
|
|
|
|
|
);
|
2025-12-23 19:29:32 -08:00
|
|
|
apply_patch_command()?
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
.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"#
|
|
|
|
|
);
|
2025-12-23 19:29:32 -08:00
|
|
|
apply_patch_command()?
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
.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"#
|
|
|
|
|
);
|
2025-12-23 19:29:32 -08:00
|
|
|
apply_patch_command()?
|
|
|
|
|
.current_dir(tmp.path())
|
|
|
|
|
.write_stdin(add_patch)
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
.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"#
|
|
|
|
|
);
|
2025-12-23 19:29:32 -08:00
|
|
|
apply_patch_command()?
|
|
|
|
|
.current_dir(tmp.path())
|
|
|
|
|
.write_stdin(update_patch)
|
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`.
2025-08-24 14:35:51 -07:00
|
|
|
.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stdout(format!("Success. Updated the following files:\nM {file}\n"));
|
|
|
|
|
assert_eq!(fs::read_to_string(&absolute_path)?, "world\n");
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|