Skip to content

Commit 00ff290

Browse files
committed
fix(cli): allow --info flag to work without git repository
Fixes bounty issue #1382 Added --repo flag to allow specifying the GitHub repository directly (in owner/repo format) when using the --info flag. This enables users to view PR details without needing to be inside a git repository, which is useful for quickly checking PR information.
1 parent 771ba8f commit 00ff290

1 file changed

Lines changed: 86 additions & 14 deletions

File tree

cortex-cli/src/pr_cmd.rs

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ pub struct PrCli {
7474
/// GitHub token for API access (for private repos).
7575
#[arg(long)]
7676
pub token: Option<String>,
77+
78+
/// GitHub repository in "owner/repo" format. Required when using --info outside a git repository.
79+
#[arg(short, long)]
80+
pub repo: Option<String>,
7781
}
7882

7983
impl PrCli {
@@ -83,28 +87,54 @@ impl PrCli {
8387
}
8488
}
8589

90+
/// Parse a repository string in "owner/repo" format.
91+
fn parse_repo_arg(repo_arg: &str) -> Result<(String, String)> {
92+
let parts: Vec<&str> = repo_arg.split('/').collect();
93+
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
94+
bail!(
95+
"Invalid repository format: '{}'. Expected 'owner/repo' format.",
96+
repo_arg
97+
);
98+
}
99+
Ok((parts[0].to_string(), parts[1].to_string()))
100+
}
101+
86102
/// Checkout a pull request branch.
87103
async fn run_pr_checkout(args: PrCli) -> Result<()> {
88104
use cortex_engine::github::GitHubClient;
89105

90-
let repo_path = args.path.unwrap_or_else(|| PathBuf::from("."));
91106
let pr_number = args.number;
92107

93-
// Change to repo directory
94-
std::env::set_current_dir(&repo_path)
95-
.with_context(|| format!("Failed to change to directory: {}", repo_path.display()))?;
96-
97-
// Check if we're in a git repo
98-
if !repo_path.join(".git").exists() {
99-
bail!("Not a git repository. Run this command from a git repository root.");
100-
}
108+
// Determine the repository - either from --repo flag or from git remote
109+
let repository = if let Some(ref repo_arg) = args.repo {
110+
// Use the provided --repo flag
111+
let (owner, repo) = parse_repo_arg(repo_arg)?;
112+
format!("{}/{}", owner, repo)
113+
} else {
114+
// Need to be in a git repository to determine owner/repo
115+
let repo_path = args.path.clone().unwrap_or_else(|| PathBuf::from("."));
116+
117+
// Change to repo directory
118+
std::env::set_current_dir(&repo_path)
119+
.with_context(|| format!("Failed to change to directory: {}", repo_path.display()))?;
120+
121+
// Check if we're in a git repo
122+
if !repo_path.join(".git").exists() {
123+
if args.info {
124+
bail!(
125+
"Not a git repository. Use --repo owner/repo to specify the repository when using --info outside a git repository."
126+
);
127+
}
128+
bail!("Not a git repository. Run this command from a git repository root.");
129+
}
101130

102-
// Get the remote URL to determine owner/repo
103-
let remote_url = get_git_remote_url()?;
104-
let (owner, repo) = parse_github_url(&remote_url)
105-
.with_context(|| format!("Failed to parse GitHub URL: {}", remote_url))?;
131+
// Get the remote URL to determine owner/repo
132+
let remote_url = get_git_remote_url()?;
133+
let (owner, repo) = parse_github_url(&remote_url)
134+
.with_context(|| format!("Failed to parse GitHub URL: {}", remote_url))?;
106135

107-
let repository = format!("{}/{}", owner, repo);
136+
format!("{}/{}", owner, repo)
137+
};
108138

109139
println!("🔀 Pull Request #{}", pr_number);
110140
println!("{}", "=".repeat(40));
@@ -149,6 +179,17 @@ async fn run_pr_checkout(args: PrCli) -> Result<()> {
149179
return Ok(());
150180
}
151181

182+
// For checkout operations, we need to be in a git repository
183+
// This check is needed when --repo was provided but --info was not
184+
if args.repo.is_some() {
185+
let repo_path = args.path.clone().unwrap_or_else(|| PathBuf::from("."));
186+
std::env::set_current_dir(&repo_path)
187+
.with_context(|| format!("Failed to change to directory: {}", repo_path.display()))?;
188+
if !repo_path.join(".git").exists() {
189+
bail!("Not a git repository. Checkout operations require a git repository.");
190+
}
191+
}
192+
152193
// Check for uncommitted changes
153194
if !args.force {
154195
let status_output = Command::new("git")
@@ -342,4 +383,35 @@ mod tests {
342383
assert_eq!(owner, "Cortex-ai");
343384
assert_eq!(repo, "Cortex");
344385
}
386+
387+
#[test]
388+
fn test_parse_repo_arg_valid() {
389+
let (owner, repo) = parse_repo_arg("CortexLM/cortex-cli").unwrap();
390+
assert_eq!(owner, "CortexLM");
391+
assert_eq!(repo, "cortex-cli");
392+
}
393+
394+
#[test]
395+
fn test_parse_repo_arg_invalid_no_slash() {
396+
let result = parse_repo_arg("invalid");
397+
assert!(result.is_err());
398+
}
399+
400+
#[test]
401+
fn test_parse_repo_arg_invalid_empty_owner() {
402+
let result = parse_repo_arg("/repo");
403+
assert!(result.is_err());
404+
}
405+
406+
#[test]
407+
fn test_parse_repo_arg_invalid_empty_repo() {
408+
let result = parse_repo_arg("owner/");
409+
assert!(result.is_err());
410+
}
411+
412+
#[test]
413+
fn test_parse_repo_arg_invalid_too_many_slashes() {
414+
let result = parse_repo_arg("owner/repo/extra");
415+
assert!(result.is_err());
416+
}
345417
}

0 commit comments

Comments
 (0)