diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-07 23:31:38 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-07 23:31:38 +0000 |
| commit | c67ebe6f33bfa191f17eb0df24d3ee18092c74e1 (patch) | |
| tree | b86911bbb406f4aa0253b1cf1e0a82aed16c972b /src/nostr/policy/pr_event.rs | |
| parent | 4dc0ed66a0bd3b4b00804bb13adf93b207bb5fc4 (diff) | |
refactor: unify event processing logic
Eliminates code duplication by extracting core event processing into
reusable functions. All state and PR event processing now uses the same
unified logic from src/git/process.rs.
Changes:
- Add src/git/process.rs with unified processing functions
- process_state_with_git_data() for state events
- process_pr_with_git_data() for PR events
- Pure functions with comprehensive result types
- Refactor policy handlers to use unified processing
- src/nostr/policy/state.rs: Remove ~70 lines of duplicated logic
- src/nostr/policy/pr_event.rs: Remove ~40 lines of duplicated logic
- Refactor purgatory processing to use unified functions
- src/git/sync.rs: Remove ~125 lines of duplicated logic
- Make extract_owner_from_repo_path() public for reuse
Benefits:
- DRY: Single source of truth for event processing
- Testable: Pure functions with clear contracts
- Maintainable: Changes happen in one place
- Consistent: All code paths use same logic
All 217 unit tests + 40 integration tests pass (257/257).
Diffstat (limited to 'src/nostr/policy/pr_event.rs')
| -rw-r--r-- | src/nostr/policy/pr_event.rs | 132 |
1 files changed, 42 insertions, 90 deletions
diff --git a/src/nostr/policy/pr_event.rs b/src/nostr/policy/pr_event.rs index ff3bade..9942a6a 100644 --- a/src/nostr/policy/pr_event.rs +++ b/src/nostr/policy/pr_event.rs | |||
| @@ -27,7 +27,7 @@ impl PrEventPolicy { | |||
| 27 | /// 2. Commit existence in referenced repositories | 27 | /// 2. Commit existence in referenced repositories |
| 28 | /// 3. Deletion of incorrect refs/nostr/<event-id> refs | 28 | /// 3. Deletion of incorrect refs/nostr/<event-id> refs |
| 29 | /// 4. Deletion of incorrect placeholders | 29 | /// 4. Deletion of incorrect placeholders |
| 30 | /// 5. Copying git data to all referenced repositories when found | 30 | /// 5. Processing PR event with unified function |
| 31 | /// | 31 | /// |
| 32 | /// # Returns | 32 | /// # Returns |
| 33 | /// - `Ok(true)` if git data ready (commit exists and is synced to all repos) | 33 | /// - `Ok(true)` if git data ready (commit exists and is synced to all repos) |
| @@ -64,7 +64,6 @@ impl PrEventPolicy { | |||
| 64 | ); | 64 | ); |
| 65 | // Remove placeholder - event processing will continue normally | 65 | // Remove placeholder - event processing will continue normally |
| 66 | self.ctx.purgatory.remove_pr(&event_id); | 66 | self.ctx.purgatory.remove_pr(&event_id); |
| 67 | // Continue to validate and sync refs across all repos | ||
| 68 | } else { | 67 | } else { |
| 69 | // Placeholder has different commit - incoming event supersedes | 68 | // Placeholder has different commit - incoming event supersedes |
| 70 | tracing::info!( | 69 | tracing::info!( |
| @@ -75,8 +74,7 @@ impl PrEventPolicy { | |||
| 75 | ); | 74 | ); |
| 76 | // Remove incorrect placeholder | 75 | // Remove incorrect placeholder |
| 77 | self.ctx.purgatory.remove_pr(&event_id); | 76 | self.ctx.purgatory.remove_pr(&event_id); |
| 78 | // Delete incorrect git data (refs/nostr/<event-id>) from all repos | 77 | // Delete incorrect git data (refs/nostr/<event-id>) will be handled below |
| 79 | // This will be handled below when we validate refs | ||
| 80 | } | 78 | } |
| 81 | } | 79 | } |
| 82 | 80 | ||
| @@ -87,9 +85,8 @@ impl PrEventPolicy { | |||
| 87 | return Ok(false); | 85 | return Ok(false); |
| 88 | } | 86 | } |
| 89 | 87 | ||
| 90 | // delete incorrect refs/nostr/<event-id> | 88 | // Delete incorrect refs/nostr/<event-id> |
| 91 | for repo_path in &repo_paths { | 89 | for repo_path in &repo_paths { |
| 92 | // First, validate/delete any incorrect refs/nostr/<event-id> | ||
| 93 | match git::validate_nostr_ref(repo_path, &event_id, &commit) { | 90 | match git::validate_nostr_ref(repo_path, &event_id, &commit) { |
| 94 | Ok(true) => { | 91 | Ok(true) => { |
| 95 | tracing::info!( | 92 | tracing::info!( |
| @@ -110,10 +107,9 @@ impl PrEventPolicy { | |||
| 110 | } | 107 | } |
| 111 | } | 108 | } |
| 112 | 109 | ||
| 113 | // find location of correct git data (if exists) | 110 | // Find location of correct git data (if exists) |
| 114 | let mut source_repo: Option<std::path::PathBuf> = None; | 111 | let mut source_repo: Option<std::path::PathBuf> = None; |
| 115 | for repo_path in &repo_paths { | 112 | for repo_path in &repo_paths { |
| 116 | // Check if commit exists in this repository | ||
| 117 | if git::commit_exists(repo_path, &commit) { | 113 | if git::commit_exists(repo_path, &commit) { |
| 118 | source_repo = Some(repo_path.clone()); | 114 | source_repo = Some(repo_path.clone()); |
| 119 | tracing::debug!( | 115 | tracing::debug!( |
| @@ -125,59 +121,50 @@ impl PrEventPolicy { | |||
| 125 | } | 121 | } |
| 126 | } | 122 | } |
| 127 | 123 | ||
| 128 | // Copy commit to all other referenced repositories | ||
| 129 | if let Some(source_repo) = source_repo { | 124 | if let Some(source_repo) = source_repo { |
| 130 | for repo_path in &repo_paths { | 125 | // Extract identifier |
| 131 | if repo_path == &source_repo { | 126 | let identifier = crate::git::sync::extract_identifier_from_pr_event(event) |
| 132 | // Skip source repo | 127 | .ok_or_else(|| anyhow::anyhow!("No identifier in PR event"))?; |
| 133 | continue; | 128 | |
| 134 | } | 129 | // Fetch repository data |
| 130 | let db_repo_data = fetch_repository_data(&self.ctx.database, &identifier).await?; | ||
| 131 | |||
| 132 | // Extract owner pubkey from source repo path | ||
| 133 | let owner_pubkey = crate::git::sync::extract_owner_from_repo_path( | ||
| 134 | &source_repo, | ||
| 135 | &self.ctx.git_data_path, | ||
| 136 | ) | ||
| 137 | .unwrap_or_default(); | ||
| 138 | |||
| 139 | // Use unified processing function | ||
| 140 | let result = crate::git::process::process_pr_with_git_data( | ||
| 141 | event, | ||
| 142 | &commit, | ||
| 143 | &source_repo, | ||
| 144 | &db_repo_data, | ||
| 145 | &self.ctx.git_data_path, | ||
| 146 | &owner_pubkey, | ||
| 147 | ); | ||
| 135 | 148 | ||
| 136 | // Check if repository exists | 149 | tracing::info!( |
| 137 | if !repo_path.exists() { | 150 | identifier = %identifier, |
| 138 | tracing::debug!( | 151 | event_id = %event_id, |
| 139 | "Repository {} does not exist, skipping sync", | 152 | repos_synced = result.repos_synced, |
| 140 | repo_path.display() | 153 | refs_created = result.refs_created, |
| 141 | ); | 154 | "Processed PR event with git data already available" |
| 142 | continue; | 155 | ); |
| 143 | } | ||
| 144 | 156 | ||
| 145 | // Check if commit already exists | 157 | if !result.errors.is_empty() { |
| 146 | if git::commit_exists(repo_path, &commit) { | 158 | for error in &result.errors { |
| 147 | tracing::debug!( | 159 | tracing::warn!( |
| 148 | "Commit {} already exists in {}, skipping sync", | 160 | identifier = %identifier, |
| 149 | commit, | 161 | event_id = %event_id, |
| 150 | repo_path.display() | 162 | error = %error, |
| 163 | "Error processing PR event" | ||
| 151 | ); | 164 | ); |
| 152 | continue; | ||
| 153 | } | ||
| 154 | |||
| 155 | // Fetch commit from source repo to target repo | ||
| 156 | tracing::info!( | ||
| 157 | "Syncing commit {} from {} to {}", | ||
| 158 | commit, | ||
| 159 | source_repo.display(), | ||
| 160 | repo_path.display() | ||
| 161 | ); | ||
| 162 | |||
| 163 | match self.copy_commit(&source_repo, repo_path, &commit).await { | ||
| 164 | Ok(()) => { | ||
| 165 | tracing::info!( | ||
| 166 | "Successfully synced commit {} to {}", | ||
| 167 | commit, | ||
| 168 | repo_path.display() | ||
| 169 | ); | ||
| 170 | } | ||
| 171 | Err(e) => { | ||
| 172 | tracing::warn!( | ||
| 173 | "Failed to sync commit {} to {}: {}", | ||
| 174 | commit, | ||
| 175 | repo_path.display(), | ||
| 176 | e | ||
| 177 | ); | ||
| 178 | } | ||
| 179 | } | 165 | } |
| 180 | } | 166 | } |
| 167 | |||
| 181 | Ok(true) | 168 | Ok(true) |
| 182 | } else { | 169 | } else { |
| 183 | tracing::debug!( | 170 | tracing::debug!( |
| @@ -250,40 +237,5 @@ impl PrEventPolicy { | |||
| 250 | 237 | ||
| 251 | Ok(repo_paths) | 238 | Ok(repo_paths) |
| 252 | } | 239 | } |
| 253 | /// Copy a commit from source repository to target repository | ||
| 254 | /// | ||
| 255 | /// Uses `git fetch` to copy a specific commit between local repositories. | ||
| 256 | /// | ||
| 257 | /// # Arguments | ||
| 258 | /// * `source_repo` - Path to repository containing the commit | ||
| 259 | /// * `target_repo` - Path to repository to receive the commit | ||
| 260 | /// * `commit` - Commit hash to copy | ||
| 261 | /// | ||
| 262 | /// # Returns | ||
| 263 | /// Ok(()) on success, Err with error message on failure | ||
| 264 | async fn copy_commit( | ||
| 265 | &self, | ||
| 266 | source_repo: &std::path::Path, | ||
| 267 | target_repo: &std::path::Path, | ||
| 268 | commit: &str, | ||
| 269 | ) -> Result<(), String> { | ||
| 270 | use std::process::Command; | ||
| 271 | |||
| 272 | let output = Command::new("git") | ||
| 273 | .args([ | ||
| 274 | "fetch", | ||
| 275 | source_repo.to_str().ok_or("Invalid source path")?, | ||
| 276 | commit, | ||
| 277 | ]) | ||
| 278 | .current_dir(target_repo) | ||
| 279 | .output() | ||
| 280 | .map_err(|e| format!("Failed to execute git fetch: {}", e))?; | ||
| 281 | 240 | ||
| 282 | if !output.status.success() { | ||
| 283 | let stderr = String::from_utf8_lossy(&output.stderr); | ||
| 284 | return Err(format!("git fetch failed: {}", stderr)); | ||
| 285 | } | ||
| 286 | |||
| 287 | Ok(()) | ||
| 288 | } | ||
| 289 | } | 241 | } |