Add case-insensitive duplicate detection for repos
All checks were successful
Cargo Build & Test / Rust project - latest (1.90) (push) Successful in 5m25s

```
│  Agent powering down. Goodbye!
│
│  Interaction Summary
│  Session ID:                 4fe7dbe6-ab78-49d0-9073-f4ed5ee0afb3
│  Tool Calls:                 21 ( ✓ 20 x 1 )
│  Success Rate:               95.2%
│  User Agreement:             95.2% (21 reviewed)
│  Code Changes:               +73 -2
│
│  Performance
│  Wall Time:                  17m 8s
│  Agent Active:               4m 23s
│    » API Time:               2m 35s (59.2%)
│    » Tool Time:              1m 47s (40.8%)
│
│
│  Model Usage                 Reqs   Input Tokens   Cache Reads  Output Tokens
│  ────────────────────────────────────────────────────────────────────────────
│  gemini-2.5-flash-lite          4          6,360             0            261
│  gemini-3-pro-preview          22         80,208       264,075          4,529
│
│  Savings Highlight: 264,075 (75.3%) of input tokens were served from the cache, reducing costs.
```
This commit is contained in:
2026-01-10 14:30:32 -05:00
parent 4c2086e2b4
commit 568e5ece49
2 changed files with 51 additions and 0 deletions

View File

@@ -92,6 +92,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 2. Build 'Desired' State (Map<RepoName, CloneUrl>) // 2. Build 'Desired' State (Map<RepoName, CloneUrl>)
info!("Resolving desired state from configuration..."); info!("Resolving desired state from configuration...");
let mut desired_repos: HashMap<String, String> = HashMap::new(); let mut desired_repos: HashMap<String, String> = HashMap::new();
let mut seen_names: HashSet<String> = HashSet::new();
let mut has_error = false;
// 2a. Static Repos // 2a. Static Repos
if let Some(repos) = &config.repos { if let Some(repos) = &config.repos {
@@ -101,6 +103,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.as_deref() .as_deref()
.or_else(|| extract_repo_name(&r.url)) .or_else(|| extract_repo_name(&r.url))
.ok_or_else(|| format!("Invalid URL: {}", r.url))?; .ok_or_else(|| format!("Invalid URL: {}", r.url))?;
let name_lower = name.to_lowercase();
if seen_names.contains(&name_lower) {
warn!(
"Duplicate repository name detected (case-insensitive): '{}'. URL: {}",
name, r.url
);
has_error = true;
continue;
}
seen_names.insert(name_lower);
desired_repos.insert(name.to_string(), r.url.clone()); desired_repos.insert(name.to_string(), r.url.clone());
} }
} }
@@ -113,12 +127,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
fetch_external_org_repos(&http_client, &org.url, org.api_key.as_deref()).await?; fetch_external_org_repos(&http_client, &org.url, org.api_key.as_deref()).await?;
for url in urls { for url in urls {
if let Some(name) = extract_repo_name(&url) { if let Some(name) = extract_repo_name(&url) {
let name_lower = name.to_lowercase();
if seen_names.contains(&name_lower) {
warn!(
"Duplicate repository name detected (case-insensitive) from organization import: '{}'. URL: {}",
name, url
);
has_error = true;
continue;
}
seen_names.insert(name_lower);
desired_repos.insert(name.to_string(), url); desired_repos.insert(name.to_string(), url);
} }
} }
} }
} }
if has_error {
return Err("Duplicate repository names detected. Please fix the configuration.".into());
}
// 3. Build 'Current' State (Set<RepoName>) // 3. Build 'Current' State (Set<RepoName>)
info!("Fetching existing repositories from Gitea ({})", owner_name); info!("Fetching existing repositories from Gitea ({})", owner_name);
let existing_repos = let existing_repos =

View File

@@ -0,0 +1,23 @@
# Session 2026-01-10: Duplicate Repository Detection
* **Date**: 2026-01-10
* **Model**: Gemini 2.5 Flash / Gemini 3 Pro Preview
* **Goal**: Implement case-insensitive duplication detection for repository names in the configuration.
* **Outcome**: Added logic to detect duplicate repository names (case-insensitive) from both static configuration and organization imports. The tool now logs warnings for all detected duplicates and then exits with a fatal error if any duplicates were found.
## Details
1. **Duplicate Detection**:
* Modified `src/main.rs` to maintain a `HashSet` of lowercased repository names.
* Checks both the `repos` list and `organizations` imports.
* If a duplicate is found, a `WARN` log is emitted with details (name and URL).
* A `has_error` flag is set to true.
2. **Error Handling**:
* After processing all sources, if `has_error` is true, the program returns a fatal error: "Duplicate repository names detected. Please fix the configuration."
* This ensures the user sees all conflicts before the program exits.
## Testing
* Created a `duplicate_repro.toml` with conflicting names (e.g., `ProjectA` vs `projecta`).
* Verified that `cargo run -- --config duplicate_repro.toml --dry-run` correctly outputted warnings for each duplicate and then exited with a non-zero status code and the expected error message.