mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
fix: preserve plugin cache symlinks
This commit is contained in:
@@ -8,6 +8,10 @@ use serde::Deserialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs as unix_fs;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs as windows_fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -348,21 +352,58 @@ fn copy_dir_recursive(source: &Path, target: &Path) -> Result<(), PluginStoreErr
|
||||
entry.map_err(|err| PluginStoreError::io("failed to enumerate plugin source", err))?;
|
||||
let source_path = entry.path();
|
||||
let target_path = target.join(entry.file_name());
|
||||
let file_type = entry
|
||||
.file_type()
|
||||
let metadata = fs::symlink_metadata(&source_path)
|
||||
.map_err(|err| PluginStoreError::io("failed to inspect plugin source entry", err))?;
|
||||
let file_type = metadata.file_type();
|
||||
|
||||
if file_type.is_dir() {
|
||||
copy_dir_recursive(&source_path, &target_path)?;
|
||||
} else if file_type.is_file() {
|
||||
fs::copy(&source_path, &target_path)
|
||||
.map_err(|err| PluginStoreError::io("failed to copy plugin file", err))?;
|
||||
} else if file_type.is_symlink() {
|
||||
copy_symlink(&source_path, &target_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn copy_symlink(source: &Path, target: &Path) -> Result<(), PluginStoreError> {
|
||||
let link_target = fs::read_link(source)
|
||||
.map_err(|err| PluginStoreError::io("failed to read plugin symlink", err))?;
|
||||
unix_fs::symlink(link_target, target)
|
||||
.map_err(|err| PluginStoreError::io("failed to copy plugin symlink", err))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn copy_symlink(source: &Path, target: &Path) -> Result<(), PluginStoreError> {
|
||||
let link_target = fs::read_link(source)
|
||||
.map_err(|err| PluginStoreError::io("failed to read plugin symlink", err))?;
|
||||
let resolved_target = if link_target.is_absolute() {
|
||||
link_target.clone()
|
||||
} else {
|
||||
source
|
||||
.parent()
|
||||
.map(|parent| parent.join(&link_target))
|
||||
.unwrap_or_else(|| link_target.clone())
|
||||
};
|
||||
let result = if resolved_target.is_dir() {
|
||||
windows_fs::symlink_dir(link_target, target)
|
||||
} else {
|
||||
windows_fs::symlink_file(link_target, target)
|
||||
};
|
||||
result.map_err(|err| PluginStoreError::io("failed to copy plugin symlink", err))
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
fn copy_symlink(_source: &Path, _target: &Path) -> Result<(), PluginStoreError> {
|
||||
Err(PluginStoreError::Invalid(
|
||||
"plugin symlinks are not supported on this platform".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "store_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -59,6 +59,45 @@ fn install_copies_plugin_into_default_marketplace() {
|
||||
assert!(installed_path.join("skills/SKILL.md").is_file());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn install_preserves_plugin_symlinks() {
|
||||
let tmp = tempdir().unwrap();
|
||||
write_plugin(tmp.path(), "sample-plugin", "sample-plugin");
|
||||
let source_plugin = tmp.path().join("sample-plugin");
|
||||
fs::create_dir_all(source_plugin.join("bundle/Versions/A")).unwrap();
|
||||
fs::write(
|
||||
source_plugin.join("bundle/Versions/A/payload.txt"),
|
||||
"payload",
|
||||
)
|
||||
.unwrap();
|
||||
std::os::unix::fs::symlink("A", source_plugin.join("bundle/Versions/Current")).unwrap();
|
||||
std::os::unix::fs::symlink(
|
||||
"Versions/Current/payload.txt",
|
||||
source_plugin.join("bundle/payload.txt"),
|
||||
)
|
||||
.unwrap();
|
||||
let plugin_id = PluginId::new("sample-plugin".to_string(), "debug".to_string()).unwrap();
|
||||
|
||||
PluginStore::new(tmp.path().to_path_buf())
|
||||
.install(AbsolutePathBuf::try_from(source_plugin).unwrap(), plugin_id)
|
||||
.unwrap();
|
||||
|
||||
let installed_path = tmp.path().join("plugins/cache/debug/sample-plugin/local");
|
||||
assert_eq!(
|
||||
fs::read_link(installed_path.join("bundle/Versions/Current")).unwrap(),
|
||||
PathBuf::from("A"),
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_link(installed_path.join("bundle/payload.txt")).unwrap(),
|
||||
PathBuf::from("Versions/Current/payload.txt"),
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_to_string(installed_path.join("bundle/payload.txt")).unwrap(),
|
||||
"payload",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn install_uses_manifest_name_for_destination_and_key() {
|
||||
let tmp = tempdir().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user