mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Prune stale curated plugin caches (#26934)
Curated plugin startup refresh now removes cached plugins whose names no longer appear in the raw openai-curated marketplace. This prevents users with the old standalone Google Sheets plugin selected locally from continuing to load its stale cache after the curated repo drops it. Existing config is left untouched, and plugins still present in the marketplace continue to refresh from local curated sources. Validation: - `just fmt` - `just test -p codex-core-plugins` - `git diff --check`
This commit is contained in:
committed by
GitHub
Unverified
parent
b89d91f6ff
commit
daf76a57d2
@@ -5,6 +5,7 @@ use crate::manifest::load_plugin_manifest;
|
||||
use crate::marketplace::MarketplacePluginSource;
|
||||
use crate::marketplace::list_marketplaces;
|
||||
use crate::marketplace::load_marketplace;
|
||||
use crate::marketplace::load_raw_marketplace_plugin_names;
|
||||
use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;
|
||||
use crate::remote::RemoteInstalledPlugin;
|
||||
use crate::store::PluginStore;
|
||||
@@ -307,6 +308,10 @@ pub fn refresh_curated_plugin_cache(
|
||||
.join(".agents/plugins/marketplace.json"),
|
||||
)
|
||||
.map_err(|_| "local curated marketplace is not available".to_string())?;
|
||||
let marketplace_plugin_names = load_raw_marketplace_plugin_names(&curated_marketplace_path)
|
||||
.map_err(|err| {
|
||||
format!("failed to load curated marketplace plugin names for cache refresh: {err}")
|
||||
})?;
|
||||
let curated_marketplace = load_marketplace(&curated_marketplace_path)
|
||||
.map_err(|err| format!("failed to load curated marketplace for cache refresh: {err}"))?;
|
||||
|
||||
@@ -321,36 +326,40 @@ pub fn refresh_curated_plugin_cache(
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let source_path = match plugin.source {
|
||||
MarketplacePluginSource::Local { path } => path,
|
||||
MarketplacePluginSource::Git { .. } => {
|
||||
warn!(
|
||||
plugin = plugin_name,
|
||||
marketplace = OPENAI_CURATED_MARKETPLACE_NAME,
|
||||
"skipping remote curated plugin source during cache refresh"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
plugin_sources.insert(plugin_name, source_path);
|
||||
if let MarketplacePluginSource::Local { path } = plugin.source {
|
||||
plugin_sources.insert(plugin_name, path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut cache_refreshed = false;
|
||||
for plugin_id in configured_curated_plugin_ids {
|
||||
if store.active_plugin_version(plugin_id).as_deref() == Some(cache_plugin_version.as_str())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source_path) = plugin_sources.get(&plugin_id.plugin_name).cloned() else {
|
||||
if !marketplace_plugin_names.contains(&plugin_id.plugin_name) {
|
||||
warn!(
|
||||
plugin = plugin_id.plugin_name,
|
||||
marketplace = OPENAI_CURATED_MARKETPLACE_NAME,
|
||||
"configured curated plugin no longer exists in curated marketplace during cache refresh"
|
||||
);
|
||||
if store.plugin_base_root(plugin_id).as_path().exists() {
|
||||
store.uninstall(plugin_id).map_err(|err| {
|
||||
format!(
|
||||
"failed to remove stale curated plugin cache for {}: {err}",
|
||||
plugin_id.as_key()
|
||||
)
|
||||
})?;
|
||||
cache_refreshed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(source_path) = plugin_sources.get(&plugin_id.plugin_name).cloned() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if store.active_plugin_version(plugin_id).as_deref() == Some(cache_plugin_version.as_str())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
store
|
||||
.install_with_version(source_path, plugin_id.clone(), cache_plugin_version.clone())
|
||||
.map_err(|err| {
|
||||
|
||||
@@ -3039,6 +3039,33 @@ fn refresh_curated_plugin_cache_reinstalls_missing_configured_plugin_with_curren
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refresh_curated_plugin_cache_removes_cache_for_plugin_removed_from_marketplace() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let curated_root = curated_plugins_repo_path(tmp.path());
|
||||
write_openai_curated_marketplace(&curated_root, &[]);
|
||||
let plugin_id = PluginId::new(
|
||||
"google-sheets".to_string(),
|
||||
OPENAI_CURATED_MARKETPLACE_NAME.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let plugin_cache_root = tmp
|
||||
.path()
|
||||
.join("plugins/cache/openai-curated/google-sheets");
|
||||
write_plugin(
|
||||
&tmp.path().join("plugins/cache/openai-curated"),
|
||||
&format!("google-sheets/{TEST_CURATED_PLUGIN_CACHE_VERSION}"),
|
||||
"google-sheets",
|
||||
);
|
||||
|
||||
assert!(
|
||||
refresh_curated_plugin_cache(tmp.path(), TEST_CURATED_PLUGIN_SHA, &[plugin_id])
|
||||
.expect("cache refresh should remove stale configured plugin")
|
||||
);
|
||||
|
||||
assert!(!plugin_cache_root.exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curated_plugin_ids_from_config_keys_reads_latest_codex_home_user_config() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_protocol::protocol::Product;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Component;
|
||||
@@ -326,6 +327,16 @@ pub fn load_marketplace(path: &AbsolutePathBuf) -> Result<Marketplace, Marketpla
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn load_raw_marketplace_plugin_names(
|
||||
path: &AbsolutePathBuf,
|
||||
) -> Result<HashSet<String>, MarketplaceError> {
|
||||
Ok(load_raw_marketplace_manifest(path)?
|
||||
.plugins
|
||||
.into_iter()
|
||||
.map(|plugin| plugin.name)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn list_marketplaces_with_home(
|
||||
additional_roots: &[AbsolutePathBuf],
|
||||
|
||||
Reference in New Issue
Block a user