diff --git a/codex-rs/cli/src/marketplace_cmd.rs b/codex-rs/cli/src/marketplace_cmd.rs index d163abfe29..48dc2e4c8e 100644 --- a/codex-rs/cli/src/marketplace_cmd.rs +++ b/codex-rs/cli/src/marketplace_cmd.rs @@ -91,8 +91,13 @@ async fn run_add(args: AddMarketplaceArgs) -> Result<()> { sparse_paths, } = args; + let has_explicit_ref = ref_name.is_some(); let source = parse_marketplace_source(&source, ref_name)?; - if !sparse_paths.is_empty() && !matches!(source, MarketplaceSource::Git { .. }) { + let source_is_git = matches!(source, MarketplaceSource::Git { .. }); + if has_explicit_ref && !source_is_git { + bail!("--ref can only be used with git marketplace sources"); + } + if !sparse_paths.is_empty() && !source_is_git { bail!("--sparse can only be used with git marketplace sources"); } @@ -281,12 +286,6 @@ fn parse_marketplace_source( return Ok(MarketplaceSource::Git { url, ref_name }); } - if base_source.starts_with("http://") || base_source.starts_with("https://") { - bail!( - "URL marketplace manifests are not supported yet; pass a git repository URL or a local marketplace directory" - ); - } - bail!("invalid marketplace source format: {source}"); } @@ -340,8 +339,7 @@ fn is_ssh_git_url(source: &str) -> bool { } fn is_http_git_url(source: &str) -> bool { - (source.starts_with("http://") || source.starts_with("https://")) - && (source.ends_with(".git") || source.starts_with("https://github.com/")) + source.starts_with("http://") || source.starts_with("https://") } fn looks_like_github_shorthand(source: &str) -> bool { @@ -522,6 +520,21 @@ mod tests { ); } + #[test] + fn non_github_https_source_parses_as_git_url() { + assert_eq!( + parse_marketplace_source( + "https://gitlab.com/owner/repo", + /* explicit_ref */ None + ) + .unwrap(), + MarketplaceSource::Git { + url: "https://gitlab.com/owner/repo".to_string(), + ref_name: None, + } + ); + } + #[test] fn utc_timestamp_formats_unix_epoch_as_rfc3339_utc() { assert_eq!(format_utc_timestamp(0), "1970-01-01T00:00:00Z"); diff --git a/codex-rs/cli/src/marketplace_cmd/ops.rs b/codex-rs/cli/src/marketplace_cmd/ops.rs index 1027e96fd3..b8e3091964 100644 --- a/codex-rs/cli/src/marketplace_cmd/ops.rs +++ b/codex-rs/cli/src/marketplace_cmd/ops.rs @@ -14,7 +14,7 @@ pub(super) fn clone_git_source( ) -> Result<()> { let destination = destination.to_string_lossy().to_string(); if sparse_paths.is_empty() { - run_git(&["clone", url, destination.as_str()], None)?; + run_git(&["clone", url, destination.as_str()], /*cwd*/ None)?; if let Some(ref_name) = ref_name { run_git(&["checkout", ref_name], Some(Path::new(&destination)))?; } @@ -29,7 +29,7 @@ pub(super) fn clone_git_source( url, destination.as_str(), ], - None, + /*cwd*/ None, )?; let mut sparse_args = vec!["sparse-checkout", "set"]; sparse_args.extend(sparse_paths.iter().map(String::as_str)); diff --git a/codex-rs/cli/tests/marketplace_add.rs b/codex-rs/cli/tests/marketplace_add.rs index d09f6d836a..46dbe663e9 100644 --- a/codex-rs/cli/tests/marketplace_add.rs +++ b/codex-rs/cli/tests/marketplace_add.rs @@ -255,3 +255,32 @@ async fn marketplace_add_sparse_flag_parses_before_and_after_source() -> Result< Ok(()) } + +#[tokio::test] +async fn marketplace_add_rejects_ref_for_local_directory() -> Result<()> { + let codex_home = TempDir::new()?; + let source = TempDir::new()?; + write_marketplace_source(source.path(), "local ref")?; + + codex_command(codex_home.path())? + .args([ + "marketplace", + "add", + "--ref", + "main", + source.path().to_str().unwrap(), + ]) + .assert() + .failure() + .stderr(contains( + "--ref can only be used with git marketplace sources", + )); + + assert!( + !marketplace_install_root(codex_home.path()) + .join("debug") + .exists() + ); + + Ok(()) +}