mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add JSON output for plugin subcommands (#26631)
## Summary - Follow-up to #25330 and #26417 - Add `--json` output for `codex plugin add` and `codex plugin remove` - Add `--json` output for `codex plugin marketplace add/list/upgrade/remove` - Keep existing human-readable output unchanged - Keep existing error handling/stderr behavior unchanged; `--json` changes successful stdout output only - Align marketplace add/remove JSON field names with the existing app-server protocol shape - Add CLI coverage for plugin and marketplace JSON outputs ## Validation - `just fmt` - `just fix -p codex-cli` - `just test -p codex-cli`
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_core_plugins::installed_marketplaces::marketplace_install_root;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use predicates::str::contains;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -70,6 +72,41 @@ async fn marketplace_add_local_directory_source() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_add_json_prints_add_outcome() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let source = TempDir::new()?;
|
||||
write_marketplace_source(source.path(), "local ref")?;
|
||||
let source_parent = source.path().parent().unwrap();
|
||||
let source_arg = format!("./{}", source.path().file_name().unwrap().to_string_lossy());
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.current_dir(source_parent)
|
||||
.args([
|
||||
"plugin",
|
||||
"marketplace",
|
||||
"add",
|
||||
source_arg.as_str(),
|
||||
"--json",
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
let expected_installed_root = AbsolutePathBuf::try_from(source.path().canonicalize()?)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"marketplaceName": "debug",
|
||||
"installedRoot": expected_installed_root.as_path().display().to_string(),
|
||||
"alreadyAdded": false,
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_add_rejects_local_manifest_file_source() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -2,7 +2,10 @@ use anyhow::Result;
|
||||
use codex_config::MarketplaceConfigUpdate;
|
||||
use codex_config::record_user_marketplace;
|
||||
use codex_core_plugins::installed_marketplaces::marketplace_install_root;
|
||||
use codex_utils_absolute_path::canonicalize_existing_preserving_symlinks;
|
||||
use predicates::str::contains;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -54,6 +57,32 @@ async fn marketplace_remove_deletes_config_and_installed_root() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_remove_json_prints_remove_outcome() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
record_user_marketplace(codex_home.path(), "debug", &configured_marketplace_update())?;
|
||||
write_installed_marketplace(codex_home.path(), "debug")?;
|
||||
let installed_root = marketplace_install_root(codex_home.path()).join("debug");
|
||||
let normalized_installed_root = canonicalize_existing_preserving_symlinks(&installed_root)?;
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.args(["plugin", "marketplace", "remove", "debug", "--json"])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"marketplaceName": "debug",
|
||||
"installedRoot": normalized_installed_root.display().to_string(),
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_remove_rejects_unknown_marketplace() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use predicates::str::contains;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -22,6 +24,29 @@ async fn marketplace_upgrade_runs_under_plugin() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_upgrade_json_prints_upgrade_outcome() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.args(["plugin", "marketplace", "upgrade", "--json"])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"selectedMarketplaces": [],
|
||||
"upgradedRoots": [],
|
||||
"errors": [],
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_upgrade_no_longer_runs_at_top_level() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -338,6 +338,32 @@ async fn marketplace_list_shows_configured_marketplace_names() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_list_json_prints_configured_marketplaces() -> Result<()> {
|
||||
let (codex_home, source) = setup_local_marketplace()?;
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.args(["plugin", "marketplace", "list", "--json"])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"marketplaces": [
|
||||
{
|
||||
"name": "debug",
|
||||
"root": source.path().display().to_string(),
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn marketplace_list_includes_home_marketplace_when_present() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -802,6 +828,69 @@ async fn plugin_add_and_remove_updates_installed_plugin_config() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_add_json_prints_install_outcome() -> Result<()> {
|
||||
let (codex_home, _source) = setup_local_marketplace()?;
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.args(["plugin", "add", "sample@debug", "--json"])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
let installed_path = codex_home.path().join("plugins/cache/debug/sample/1.2.3");
|
||||
let normalized_installed_path = canonicalize_existing_preserving_symlinks(&installed_path)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"pluginId": "sample@debug",
|
||||
"name": "sample",
|
||||
"marketplaceName": "debug",
|
||||
"version": "1.2.3",
|
||||
"installedPath": normalized_installed_path.display().to_string(),
|
||||
"authPolicy": "ON_INSTALL",
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_remove_json_prints_remove_outcome() -> Result<()> {
|
||||
let (codex_home, _source) = setup_local_marketplace()?;
|
||||
|
||||
codex_command(codex_home.path())?
|
||||
.args(["plugin", "add", "sample@debug"])
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
let assert = codex_command(codex_home.path())?
|
||||
.args([
|
||||
"plugin",
|
||||
"remove",
|
||||
"sample",
|
||||
"--marketplace",
|
||||
"debug",
|
||||
"--json",
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
let stdout = assert.get_output().stdout.as_slice();
|
||||
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
json!({
|
||||
"pluginId": "sample@debug",
|
||||
"name": "sample",
|
||||
"marketplaceName": "debug",
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_add_rejects_unconfigured_repo_local_marketplaces() -> Result<()> {
|
||||
let (codex_home, source) = setup_unconfigured_local_marketplace()?;
|
||||
|
||||
Reference in New Issue
Block a user