Respect config layers when removing plugin marketplaces (#40683)

## Why

Removing a base-user marketplace must not delete a snapshot that is still
referenced by another enabled configuration layer.

## What changed

- Load the current configuration stack for marketplace commands in the CLI and
  app server.
- Reject removal when a matching marketplace is defined by another enabled
  layer, and identify the configuration source that must be updated instead.
- Allow cleanup of an installed marketplace snapshot even when no base-user
  entry exists.

## Testing

Cover project, enterprise-managed, system, and session-flag configurations, as
well as snapshot-only removal through the CLI and app server.

GitOrigin-RevId: 4f0853a40c2b5fae0410696b79eee4284239c7a9
This commit is contained in:
willwang-openai
2026-08-25 19:38:39 +00:00
committed by copyberry
parent f1a6f7c9b3
commit a6e63f9f32
7 changed files with 253 additions and 47 deletions
+14 -26
View File
@@ -3,7 +3,7 @@ use anyhow::Result;
use anyhow::bail;
use clap::Parser;
use codex_core::config::Config;
use codex_core::config::find_codex_home;
use codex_core::config::LoaderOverrides;
use codex_core::plugins_manager_for_config;
use codex_core_plugins::PluginMarketplaceUpgradeOutcome;
use codex_core_plugins::PluginsConfigInput;
@@ -23,6 +23,7 @@ use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
use crate::cloud_config;
use crate::plugin_cmd::JsonMarketplaceSource;
use crate::plugin_cmd::configured_marketplace_snapshot_issues;
use crate::plugin_cmd::configured_marketplace_sources;
@@ -127,22 +128,21 @@ impl MarketplaceCli {
subcommand,
} = self;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config =
cloud_config::load_config(&config_overrides, LoaderOverrides::default()).await?;
match subcommand {
MarketplaceSubcommand::Add(args) => run_add(overrides, args).await?,
MarketplaceSubcommand::List(args) => run_list(overrides, args).await?,
MarketplaceSubcommand::Upgrade(args) => run_upgrade(overrides, args).await?,
MarketplaceSubcommand::Remove(args) => run_remove(args).await?,
MarketplaceSubcommand::Add(args) => run_add(config, args).await?,
MarketplaceSubcommand::List(args) => run_list(config, args).await?,
MarketplaceSubcommand::Upgrade(args) => run_upgrade(config, args).await?,
MarketplaceSubcommand::Remove(args) => run_remove(config, args).await?,
}
Ok(())
}
}
async fn run_add(overrides: Vec<(String, toml::Value)>, args: AddMarketplaceArgs) -> Result<()> {
async fn run_add(config: Config, args: AddMarketplaceArgs) -> Result<()> {
let AddMarketplaceArgs {
source,
ref_name,
@@ -150,9 +150,6 @@ async fn run_add(overrides: Vec<(String, toml::Value)>, args: AddMarketplaceArgs
json,
} = args;
let config = Config::load_with_cli_overrides(overrides)
.await
.context("failed to load configuration")?;
let outcome = add_marketplace(
config.codex_home.to_path_buf(),
config.config_layer_stack.requirements().clone(),
@@ -207,10 +204,7 @@ impl JsonMarketplaceAddOutput {
}
}
async fn run_list(overrides: Vec<(String, toml::Value)>, args: ListMarketplaceArgs) -> Result<()> {
let config = Config::load_with_cli_overrides(overrides)
.await
.context("failed to load configuration")?;
async fn run_list(config: Config, args: ListMarketplaceArgs) -> Result<()> {
let manager = plugins_manager_for_config(&config, load_cli_auth_manager(&config).await?);
let plugins_input = config.plugins_config_input();
let marketplace_listing = manager
@@ -366,17 +360,11 @@ fn configured_marketplace_sources_by_root(
.collect()
}
async fn run_upgrade(
overrides: Vec<(String, toml::Value)>,
args: UpgradeMarketplaceArgs,
) -> Result<()> {
async fn run_upgrade(config: Config, args: UpgradeMarketplaceArgs) -> Result<()> {
let UpgradeMarketplaceArgs {
marketplace_name,
json,
} = args;
let config = Config::load_with_cli_overrides(overrides)
.await
.context("failed to load configuration")?;
let manager = plugins_manager_for_config(&config, load_cli_auth_manager(&config).await?);
let plugins_input = config.plugins_config_input();
let outcome = manager
@@ -389,14 +377,14 @@ async fn run_upgrade(
}
}
async fn run_remove(args: RemoveMarketplaceArgs) -> Result<()> {
async fn run_remove(config: Config, args: RemoveMarketplaceArgs) -> Result<()> {
let RemoveMarketplaceArgs {
marketplace_name,
json,
} = args;
let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
let outcome = remove_marketplace(
codex_home.to_path_buf(),
config.codex_home.to_path_buf(),
config.config_layer_stack,
MarketplaceRemoveRequest { marketplace_name },
)
.await?;