fix: ensure apply_patch resolves relative paths against workdir or project cwd (#810)
https://github.com/openai/codex/pull/800 kicked off some work to be more disciplined about honoring the `cwd` param passed in rather than assuming `std::env::current_dir()` as the `cwd`. As part of this, we need to ensure `apply_patch` calls honor the appropriate `cwd` as well, which is significant if the paths in the `apply_patch` arg are not absolute paths themselves. Failing that: - The `apply_patch` function call can contain an optional`workdir` param, so: - If specified and is an absolute path, it should be used to resolve relative paths - If specified and is a relative path, should be resolved against `Config.cwd` and then any relative paths will be resolved against the result - If `workdir` is not specified on the function call, relative paths should be resolved against `Config.cwd` Note that we had a similar issue in the TypeScript CLI that was fixed in https://github.com/openai/codex/pull/556. As part of the fix, this PR introduces `ApplyPatchAction` so clients can deal with that instead of the raw `HashMap<PathBuf, ApplyPatchFileChange>`. This enables us to enforce, by construction, that all paths contained in the `ApplyPatchAction` are absolute paths.
This commit is contained in:
@@ -95,7 +95,7 @@ pub enum ApplyPatchFileChange {
|
||||
pub enum MaybeApplyPatchVerified {
|
||||
/// `argv` corresponded to an `apply_patch` invocation, and these are the
|
||||
/// resulting proposed file changes.
|
||||
Body(HashMap<PathBuf, ApplyPatchFileChange>),
|
||||
Body(ApplyPatchAction),
|
||||
/// `argv` could not be parsed to determine whether it corresponds to an
|
||||
/// `apply_patch` invocation.
|
||||
ShellParseError(Error),
|
||||
@@ -106,7 +106,38 @@ pub enum MaybeApplyPatchVerified {
|
||||
NotApplyPatch,
|
||||
}
|
||||
|
||||
pub fn maybe_parse_apply_patch_verified(argv: &[String]) -> MaybeApplyPatchVerified {
|
||||
#[derive(Debug)]
|
||||
/// ApplyPatchAction is the result of parsing an `apply_patch` command. By
|
||||
/// construction, all paths should be absolute paths.
|
||||
pub struct ApplyPatchAction {
|
||||
changes: HashMap<PathBuf, ApplyPatchFileChange>,
|
||||
}
|
||||
|
||||
impl ApplyPatchAction {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.changes.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the changes that would be made by applying the patch.
|
||||
pub fn changes(&self) -> &HashMap<PathBuf, ApplyPatchFileChange> {
|
||||
&self.changes
|
||||
}
|
||||
|
||||
/// Should be used exclusively for testing. (Not worth the overhead of
|
||||
/// creating a feature flag for this.)
|
||||
pub fn new_add_for_test(path: &Path, content: String) -> Self {
|
||||
if !path.is_absolute() {
|
||||
panic!("path must be absolute");
|
||||
}
|
||||
|
||||
let changes = HashMap::from([(path.to_path_buf(), ApplyPatchFileChange::Add { content })]);
|
||||
Self { changes }
|
||||
}
|
||||
}
|
||||
|
||||
/// cwd must be an absolute path so that we can resolve relative paths in the
|
||||
/// patch.
|
||||
pub fn maybe_parse_apply_patch_verified(argv: &[String], cwd: &Path) -> MaybeApplyPatchVerified {
|
||||
match maybe_parse_apply_patch(argv) {
|
||||
MaybeApplyPatch::Body(hunks) => {
|
||||
let mut changes = HashMap::new();
|
||||
@@ -114,14 +145,14 @@ pub fn maybe_parse_apply_patch_verified(argv: &[String]) -> MaybeApplyPatchVerif
|
||||
match hunk {
|
||||
Hunk::AddFile { path, contents } => {
|
||||
changes.insert(
|
||||
path,
|
||||
cwd.join(path),
|
||||
ApplyPatchFileChange::Add {
|
||||
content: contents.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
Hunk::DeleteFile { path } => {
|
||||
changes.insert(path, ApplyPatchFileChange::Delete);
|
||||
changes.insert(cwd.join(path), ApplyPatchFileChange::Delete);
|
||||
}
|
||||
Hunk::UpdateFile {
|
||||
path,
|
||||
@@ -138,17 +169,17 @@ pub fn maybe_parse_apply_patch_verified(argv: &[String]) -> MaybeApplyPatchVerif
|
||||
}
|
||||
};
|
||||
changes.insert(
|
||||
path.clone(),
|
||||
cwd.join(path),
|
||||
ApplyPatchFileChange::Update {
|
||||
unified_diff,
|
||||
move_path,
|
||||
move_path: move_path.map(|p| cwd.join(p)),
|
||||
new_content: contents,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
MaybeApplyPatchVerified::Body(changes)
|
||||
MaybeApplyPatchVerified::Body(ApplyPatchAction { changes })
|
||||
}
|
||||
MaybeApplyPatch::ShellParseError(e) => MaybeApplyPatchVerified::ShellParseError(e),
|
||||
MaybeApplyPatch::PatchParseError(e) => MaybeApplyPatchVerified::CorrectnessError(e.into()),
|
||||
|
||||
Reference in New Issue
Block a user