mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Add marketplace upgrade command
This commit is contained in:
@@ -15,6 +15,9 @@ use super::marketplace::ResolvedMarketplacePlugin;
|
||||
use super::marketplace::list_marketplaces;
|
||||
use super::marketplace::load_marketplace;
|
||||
use super::marketplace::resolve_marketplace_plugin;
|
||||
use super::marketplace_upgrade::ConfiguredMarketplaceUpgradeError;
|
||||
use super::marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome;
|
||||
use super::marketplace_upgrade::configured_git_marketplace_names;
|
||||
use super::marketplace_upgrade::upgrade_configured_git_marketplaces;
|
||||
use super::read_curated_plugins_sha;
|
||||
use super::remote::RemotePluginFetchError;
|
||||
@@ -1144,16 +1147,24 @@ impl PluginsManager {
|
||||
}
|
||||
|
||||
let manager = Arc::clone(self);
|
||||
let codex_home = self.codex_home.clone();
|
||||
let config = config.clone();
|
||||
if let Err(err) = std::thread::Builder::new()
|
||||
.name("plugins-marketplace-auto-upgrade".to_string())
|
||||
.spawn(move || {
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.as_path(), &config);
|
||||
if !outcome.upgraded_roots.is_empty() {
|
||||
manager.maybe_start_non_curated_plugin_cache_force_reinstall_for_roots(
|
||||
&outcome.upgraded_roots,
|
||||
);
|
||||
let outcome = manager.upgrade_configured_marketplaces_for_config(&config, None);
|
||||
match outcome {
|
||||
Ok(outcome) => {
|
||||
for error in outcome.errors {
|
||||
warn!(
|
||||
marketplace = error.marketplace_name,
|
||||
error = %error.message,
|
||||
"failed to auto-upgrade configured marketplace"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("failed to auto-upgrade configured marketplaces: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = match manager.configured_marketplace_upgrade_state.write() {
|
||||
@@ -1172,6 +1183,37 @@ impl PluginsManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upgrade_configured_marketplaces_for_config(
|
||||
&self,
|
||||
config: &Config,
|
||||
marketplace_name: Option<&str>,
|
||||
) -> Result<ConfiguredMarketplaceUpgradeOutcome, String> {
|
||||
if let Some(marketplace_name) = marketplace_name
|
||||
&& !configured_git_marketplace_names(config)
|
||||
.iter()
|
||||
.any(|name| name == marketplace_name)
|
||||
{
|
||||
return Err(format!(
|
||||
"marketplace `{marketplace_name}` is not configured as a Git marketplace"
|
||||
));
|
||||
}
|
||||
|
||||
let mut outcome = upgrade_configured_git_marketplaces(
|
||||
self.codex_home.as_path(),
|
||||
config,
|
||||
marketplace_name,
|
||||
);
|
||||
if let Err(err) = self.refresh_upgraded_marketplace_plugin_cache(&outcome.upgraded_roots) {
|
||||
outcome.errors.push(ConfiguredMarketplaceUpgradeError {
|
||||
marketplace_name: marketplace_name
|
||||
.unwrap_or("all configured marketplaces")
|
||||
.to_string(),
|
||||
message: err,
|
||||
});
|
||||
}
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
pub fn maybe_start_non_curated_plugin_cache_refresh_for_roots(
|
||||
self: &Arc<Self>,
|
||||
roots: &[AbsolutePathBuf],
|
||||
@@ -1182,16 +1224,6 @@ impl PluginsManager {
|
||||
);
|
||||
}
|
||||
|
||||
fn maybe_start_non_curated_plugin_cache_force_reinstall_for_roots(
|
||||
self: &Arc<Self>,
|
||||
roots: &[AbsolutePathBuf],
|
||||
) {
|
||||
self.maybe_start_non_curated_plugin_cache_refresh_for_roots_with_mode(
|
||||
roots,
|
||||
NonCuratedCacheRefreshMode::ForceReinstall,
|
||||
);
|
||||
}
|
||||
|
||||
fn maybe_start_non_curated_plugin_cache_refresh_for_roots_with_mode(
|
||||
self: &Arc<Self>,
|
||||
roots: &[AbsolutePathBuf],
|
||||
@@ -1256,6 +1288,34 @@ impl PluginsManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_upgraded_marketplace_plugin_cache(
|
||||
&self,
|
||||
roots: &[AbsolutePathBuf],
|
||||
) -> Result<(), String> {
|
||||
if roots.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match refresh_non_curated_plugin_cache(
|
||||
self.codex_home.as_path(),
|
||||
roots,
|
||||
NonCuratedCacheRefreshMode::ForceReinstall,
|
||||
) {
|
||||
Ok(cache_refreshed) => {
|
||||
if cache_refreshed {
|
||||
self.clear_cache();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.clear_cache();
|
||||
Err(format!(
|
||||
"failed to refresh installed plugin cache after marketplace upgrade: {err}"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_curated_repo_sync(self: &Arc<Self>) {
|
||||
if CURATED_REPO_SYNC_STARTED.swap(true, Ordering::SeqCst) {
|
||||
return;
|
||||
|
||||
@@ -23,10 +23,17 @@ use crate::config::Config;
|
||||
|
||||
const MARKETPLACE_UPGRADE_GIT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub(super) struct ConfiguredMarketplaceUpgradeOutcome {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConfiguredMarketplaceUpgradeError {
|
||||
pub marketplace_name: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct ConfiguredMarketplaceUpgradeOutcome {
|
||||
pub selected_marketplaces: Vec<String>,
|
||||
pub upgraded_roots: Vec<AbsolutePathBuf>,
|
||||
pub all_succeeded: bool,
|
||||
pub errors: Vec<ConfiguredMarketplaceUpgradeError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -38,40 +45,58 @@ struct ConfiguredGitMarketplace {
|
||||
last_revision: Option<String>,
|
||||
}
|
||||
|
||||
pub(super) fn upgrade_configured_git_marketplaces(
|
||||
impl ConfiguredMarketplaceUpgradeOutcome {
|
||||
pub fn all_succeeded(&self) -> bool {
|
||||
self.errors.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configured_git_marketplace_names(config: &Config) -> Vec<String> {
|
||||
let mut names = configured_git_marketplaces(config)
|
||||
.into_iter()
|
||||
.map(|marketplace| marketplace.name)
|
||||
.collect::<Vec<_>>();
|
||||
names.sort_unstable();
|
||||
names
|
||||
}
|
||||
|
||||
pub fn upgrade_configured_git_marketplaces(
|
||||
codex_home: &Path,
|
||||
config: &Config,
|
||||
marketplace_name: Option<&str>,
|
||||
) -> ConfiguredMarketplaceUpgradeOutcome {
|
||||
let marketplaces = configured_git_marketplaces(config);
|
||||
let marketplaces = configured_git_marketplaces(config)
|
||||
.into_iter()
|
||||
.filter(|marketplace| marketplace_name.is_none_or(|name| marketplace.name.as_str() == name))
|
||||
.collect::<Vec<_>>();
|
||||
if marketplaces.is_empty() {
|
||||
return ConfiguredMarketplaceUpgradeOutcome {
|
||||
all_succeeded: true,
|
||||
..Default::default()
|
||||
};
|
||||
return ConfiguredMarketplaceUpgradeOutcome::default();
|
||||
}
|
||||
|
||||
let install_root = marketplace_install_root(codex_home);
|
||||
let selected_marketplaces = marketplaces
|
||||
.iter()
|
||||
.map(|marketplace| marketplace.name.clone())
|
||||
.collect();
|
||||
let mut upgraded_roots = Vec::new();
|
||||
let mut all_succeeded = true;
|
||||
let mut errors = Vec::new();
|
||||
for marketplace in marketplaces {
|
||||
match upgrade_configured_git_marketplace(codex_home, &install_root, &marketplace) {
|
||||
Ok(Some(upgraded_root)) => upgraded_roots.push(upgraded_root),
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
all_succeeded = false;
|
||||
warn!(
|
||||
marketplace = marketplace.name,
|
||||
source = marketplace.source,
|
||||
error = %err,
|
||||
"failed to auto-upgrade configured marketplace"
|
||||
);
|
||||
errors.push(ConfiguredMarketplaceUpgradeError {
|
||||
marketplace_name: marketplace.name,
|
||||
message: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
selected_marketplaces,
|
||||
upgraded_roots,
|
||||
all_succeeded,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,18 +314,19 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
selected_marketplaces: vec!["debug".to_string()],
|
||||
upgraded_roots: vec![
|
||||
AbsolutePathBuf::try_from(
|
||||
marketplace_install_root(codex_home.path()).join("debug")
|
||||
)
|
||||
.unwrap()
|
||||
],
|
||||
all_succeeded: true,
|
||||
errors: Vec::new(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -330,13 +356,14 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
selected_marketplaces: vec!["debug".to_string()],
|
||||
upgraded_roots: Vec::new(),
|
||||
all_succeeded: true,
|
||||
errors: Vec::new(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -361,18 +388,19 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
selected_marketplaces: vec!["debug".to_string()],
|
||||
upgraded_roots: vec![
|
||||
AbsolutePathBuf::try_from(
|
||||
marketplace_install_root(codex_home.path()).join("debug")
|
||||
)
|
||||
.unwrap()
|
||||
],
|
||||
all_succeeded: true,
|
||||
errors: Vec::new(),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -395,15 +423,12 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
upgraded_roots: Vec::new(),
|
||||
all_succeeded: false,
|
||||
}
|
||||
);
|
||||
assert_eq!(outcome.selected_marketplaces, vec!["debug".to_string()]);
|
||||
assert!(outcome.upgraded_roots.is_empty());
|
||||
assert_eq!(outcome.errors.len(), 1);
|
||||
assert_eq!(outcome.errors[0].marketplace_name, "debug");
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(installed_root.join("plugins/sample/marker.txt")).unwrap(),
|
||||
"old"
|
||||
@@ -422,15 +447,12 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
upgraded_roots: Vec::new(),
|
||||
all_succeeded: false,
|
||||
}
|
||||
);
|
||||
assert_eq!(outcome.selected_marketplaces, vec!["debug".to_string()]);
|
||||
assert!(outcome.upgraded_roots.is_empty());
|
||||
assert_eq!(outcome.errors.len(), 1);
|
||||
assert_eq!(outcome.errors[0].marketplace_name, "debug");
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(installed_root.join("plugins/sample/marker.txt")).unwrap(),
|
||||
"old"
|
||||
@@ -458,15 +480,12 @@ mod tests {
|
||||
&marketplace_config(changed_source_repo.path(), "changed-revision"),
|
||||
);
|
||||
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
upgraded_roots: Vec::new(),
|
||||
all_succeeded: false,
|
||||
}
|
||||
);
|
||||
assert_eq!(outcome.selected_marketplaces, vec!["debug".to_string()]);
|
||||
assert!(outcome.upgraded_roots.is_empty());
|
||||
assert_eq!(outcome.errors.len(), 1);
|
||||
assert_eq!(outcome.errors[0].marketplace_name, "debug");
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(installed_root.join("plugins/sample/marker.txt")).unwrap(),
|
||||
"old"
|
||||
@@ -488,13 +507,14 @@ plugins = true
|
||||
);
|
||||
|
||||
let config = load_plugins_config(codex_home.path()).await;
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config);
|
||||
let outcome = upgrade_configured_git_marketplaces(codex_home.path(), &config, None);
|
||||
|
||||
assert_eq!(
|
||||
outcome,
|
||||
ConfiguredMarketplaceUpgradeOutcome {
|
||||
selected_marketplaces: Vec::new(),
|
||||
upgraded_roots: Vec::new(),
|
||||
all_succeeded: true,
|
||||
errors: Vec::new(),
|
||||
}
|
||||
);
|
||||
assert!(
|
||||
|
||||
@@ -6,7 +6,6 @@ mod installed_marketplaces;
|
||||
mod manager;
|
||||
mod manifest;
|
||||
mod marketplace;
|
||||
mod marketplace_add;
|
||||
mod marketplace_upgrade;
|
||||
mod mentions;
|
||||
mod remote;
|
||||
@@ -60,10 +59,8 @@ pub use marketplace::MarketplacePluginInstallPolicy;
|
||||
pub use marketplace::MarketplacePluginPolicy;
|
||||
pub use marketplace::MarketplacePluginSource;
|
||||
pub use marketplace::validate_marketplace_root;
|
||||
pub use marketplace_add::MarketplaceAddError;
|
||||
pub use marketplace_add::MarketplaceAddOutcome;
|
||||
pub use marketplace_add::MarketplaceAddRequest;
|
||||
pub use marketplace_add::add_marketplace;
|
||||
pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeError as PluginMarketplaceUpgradeError;
|
||||
pub use marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome as PluginMarketplaceUpgradeOutcome;
|
||||
pub use remote::RemotePluginFetchError;
|
||||
pub use remote::fetch_remote_featured_plugin_ids;
|
||||
pub(crate) use render::render_explicit_plugin_instructions;
|
||||
|
||||
Reference in New Issue
Block a user