From 568e5ece49b8e46d5d7a8bf5af937b18c1a7fc2e Mon Sep 17 00:00:00 2001 From: hak8or Date: Sat, 10 Jan 2026 14:30:32 -0500 Subject: [PATCH] Add case-insensitive duplicate detection for repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` │ 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. ``` --- src/main.rs | 28 +++++++++++++++++++ .../session_2026_01_10_duplicate_detection.md | 23 +++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 vibe_coding_log/session_2026_01_10_duplicate_detection.md diff --git a/src/main.rs b/src/main.rs index 0112a4e..7f49f53 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,6 +92,8 @@ async fn main() -> Result<(), Box> { // 2. Build 'Desired' State (Map) info!("Resolving desired state from configuration..."); let mut desired_repos: HashMap = HashMap::new(); + let mut seen_names: HashSet = HashSet::new(); + let mut has_error = false; // 2a. Static Repos if let Some(repos) = &config.repos { @@ -101,6 +103,18 @@ async fn main() -> Result<(), Box> { .as_deref() .or_else(|| extract_repo_name(&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()); } } @@ -113,12 +127,26 @@ async fn main() -> Result<(), Box> { fetch_external_org_repos(&http_client, &org.url, org.api_key.as_deref()).await?; for url in urls { 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); } } } } + if has_error { + return Err("Duplicate repository names detected. Please fix the configuration.".into()); + } + // 3. Build 'Current' State (Set) info!("Fetching existing repositories from Gitea ({})", owner_name); let existing_repos = diff --git a/vibe_coding_log/session_2026_01_10_duplicate_detection.md b/vibe_coding_log/session_2026_01_10_duplicate_detection.md new file mode 100644 index 0000000..c47d43a --- /dev/null +++ b/vibe_coding_log/session_2026_01_10_duplicate_detection.md @@ -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.