|
7 | 7 | "crypto/sha256" |
8 | 8 | "encoding/hex" |
9 | 9 | "encoding/json" |
| 10 | + "fmt" |
10 | 11 | "io" |
11 | 12 | "log" |
12 | 13 | "net/http" |
@@ -135,33 +136,70 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
135 | 136 | return |
136 | 137 | } |
137 | 138 |
|
138 | | - // Extract PR URL |
139 | | - prURL := ExtractPRURL(eventType, payload) |
140 | | - if prURL == "" { |
141 | | - // Log full payload to understand the structure |
| 139 | + // For check events, always log the full payload to help with debugging |
| 140 | + if eventType == "check_run" || eventType == "check_suite" { |
142 | 141 | payloadJSON, err := json.Marshal(payload) |
143 | 142 | if err != nil { |
144 | | - logger.Warn("failed to marshal payload for logging", logger.Fields{ |
| 143 | + logger.Warn("failed to marshal check event payload", logger.Fields{ |
145 | 144 | "event_type": eventType, |
146 | 145 | "delivery_id": deliveryID, |
147 | 146 | "error": err.Error(), |
148 | 147 | }) |
149 | 148 | } else { |
150 | | - logger.Info("no PR URL found in event - full payload", logger.Fields{ |
| 149 | + logger.Info("received check event - full payload for debugging", logger.Fields{ |
151 | 150 | "event_type": eventType, |
152 | 151 | "delivery_id": deliveryID, |
153 | 152 | "payload": string(payloadJSON), |
154 | 153 | }) |
155 | 154 | } |
| 155 | + } |
| 156 | + |
| 157 | + // Extract PR URL |
| 158 | + prURL := ExtractPRURL(eventType, payload) |
| 159 | + if prURL == "" { |
| 160 | + // For check events, try to extract commit SHA and look up associated PRs via API |
| 161 | + if eventType == "check_run" || eventType == "check_suite" { |
| 162 | + commitSHA := extractCommitSHA(eventType, payload) |
| 163 | + if commitSHA != "" { |
| 164 | + logger.Info("no PR URL in check event payload, will need API lookup", logger.Fields{ |
| 165 | + "event_type": eventType, |
| 166 | + "delivery_id": deliveryID, |
| 167 | + "commit_sha": commitSHA, |
| 168 | + "note": "commit SHA can be used to query GitHub API: GET /repos/OWNER/REPO/commits/SHA/pulls", |
| 169 | + }) |
| 170 | + } else { |
| 171 | + logger.Warn("check event has no PR URL and no commit SHA", logger.Fields{ |
| 172 | + "event_type": eventType, |
| 173 | + "delivery_id": deliveryID, |
| 174 | + }) |
| 175 | + } |
| 176 | + } else { |
| 177 | + // Log full payload to understand the structure (for non-check events) |
| 178 | + payloadJSON, err := json.Marshal(payload) |
| 179 | + if err != nil { |
| 180 | + logger.Warn("failed to marshal payload for logging", logger.Fields{ |
| 181 | + "event_type": eventType, |
| 182 | + "delivery_id": deliveryID, |
| 183 | + "error": err.Error(), |
| 184 | + }) |
| 185 | + } else { |
| 186 | + logger.Info("no PR URL found in event - full payload", logger.Fields{ |
| 187 | + "event_type": eventType, |
| 188 | + "delivery_id": deliveryID, |
| 189 | + "payload": string(payloadJSON), |
| 190 | + }) |
| 191 | + } |
| 192 | + } |
156 | 193 | w.WriteHeader(http.StatusOK) |
157 | 194 | return |
158 | 195 | } |
159 | 196 |
|
160 | 197 | // Create and broadcast event |
161 | 198 | event := srv.Event{ |
162 | | - URL: prURL, |
163 | | - Timestamp: time.Now(), |
164 | | - Type: eventType, |
| 199 | + URL: prURL, |
| 200 | + Timestamp: time.Now(), |
| 201 | + Type: eventType, |
| 202 | + DeliveryID: deliveryID, |
165 | 203 | } |
166 | 204 |
|
167 | 205 | h.hub.Broadcast(event, payload) |
@@ -220,53 +258,130 @@ func ExtractPRURL(eventType string, payload map[string]any) string { |
220 | 258 | case "check_run", "check_suite": |
221 | 259 | // Extract PR URLs from check events if available |
222 | 260 | if checkRun, ok := payload["check_run"].(map[string]any); ok { |
223 | | - if url := extractPRFromCheckEvent(checkRun, payload); url != "" { |
| 261 | + if url := extractPRFromCheckEvent(checkRun, payload, eventType); url != "" { |
224 | 262 | return url |
225 | 263 | } |
226 | 264 | } |
227 | 265 | if checkSuite, ok := payload["check_suite"].(map[string]any); ok { |
228 | | - if url := extractPRFromCheckEvent(checkSuite, payload); url != "" { |
| 266 | + if url := extractPRFromCheckEvent(checkSuite, payload, eventType); url != "" { |
229 | 267 | return url |
230 | 268 | } |
231 | 269 | } |
| 270 | + // Log when we can't extract PR URL from check event |
| 271 | + logger.Warn("no PR URL found in check event", logger.Fields{ |
| 272 | + "event_type": eventType, |
| 273 | + "has_check_run": payload["check_run"] != nil, |
| 274 | + "has_check_suite": payload["check_suite"] != nil, |
| 275 | + "payload_keys": getPayloadKeys(payload), |
| 276 | + }) |
232 | 277 | default: |
233 | 278 | // For other event types, no PR URL can be extracted |
234 | 279 | } |
235 | 280 | return "" |
236 | 281 | } |
237 | 282 |
|
238 | 283 | // extractPRFromCheckEvent extracts PR URL from check_run or check_suite events. |
239 | | -func extractPRFromCheckEvent(checkEvent map[string]any, payload map[string]any) string { |
| 284 | +func extractPRFromCheckEvent(checkEvent map[string]any, payload map[string]any, eventType string) string { |
240 | 285 | prs, ok := checkEvent["pull_requests"].([]any) |
241 | 286 | if !ok || len(prs) == 0 { |
| 287 | + logger.Info("check event has no pull_requests array", logger.Fields{ |
| 288 | + "event_type": eventType, |
| 289 | + "has_pr_array": ok, |
| 290 | + "pr_array_length": len(prs), |
| 291 | + "check_event_keys": getMapKeys(checkEvent), |
| 292 | + }) |
242 | 293 | return "" |
243 | 294 | } |
244 | 295 |
|
245 | 296 | pr, ok := prs[0].(map[string]any) |
246 | 297 | if !ok { |
| 298 | + logger.Warn("pull_requests[0] is not a map", logger.Fields{ |
| 299 | + "event_type": eventType, |
| 300 | + "pr_type": fmt.Sprintf("%T", prs[0]), |
| 301 | + }) |
247 | 302 | return "" |
248 | 303 | } |
249 | 304 |
|
250 | 305 | // Try html_url first |
251 | 306 | if htmlURL, ok := pr["html_url"].(string); ok { |
| 307 | + logger.Info("extracted PR URL from check event html_url", logger.Fields{ |
| 308 | + "event_type": eventType, |
| 309 | + "pr_url": htmlURL, |
| 310 | + }) |
252 | 311 | return htmlURL |
253 | 312 | } |
254 | 313 |
|
255 | 314 | // Fallback: construct from number |
256 | 315 | num, ok := pr["number"].(float64) |
257 | 316 | if !ok { |
| 317 | + logger.Warn("PR number not found in check event", logger.Fields{ |
| 318 | + "event_type": eventType, |
| 319 | + "pr_keys": getMapKeys(pr), |
| 320 | + }) |
258 | 321 | return "" |
259 | 322 | } |
260 | 323 |
|
261 | 324 | repo, ok := payload["repository"].(map[string]any) |
262 | 325 | if !ok { |
| 326 | + logger.Warn("repository not found in payload", logger.Fields{ |
| 327 | + "event_type": eventType, |
| 328 | + }) |
263 | 329 | return "" |
264 | 330 | } |
265 | 331 |
|
266 | 332 | repoURL, ok := repo["html_url"].(string) |
267 | 333 | if !ok { |
| 334 | + logger.Warn("repository html_url not found", logger.Fields{ |
| 335 | + "event_type": eventType, |
| 336 | + "repo_keys": getMapKeys(repo), |
| 337 | + }) |
268 | 338 | return "" |
269 | 339 | } |
270 | 340 |
|
271 | | - return repoURL + "/pull/" + strconv.Itoa(int(num)) |
| 341 | + constructedURL := repoURL + "/pull/" + strconv.Itoa(int(num)) |
| 342 | + logger.Info("constructed PR URL from check event", logger.Fields{ |
| 343 | + "event_type": eventType, |
| 344 | + "pr_url": constructedURL, |
| 345 | + "pr_number": int(num), |
| 346 | + }) |
| 347 | + return constructedURL |
| 348 | +} |
| 349 | + |
| 350 | +// getPayloadKeys returns the keys from a payload map for logging. |
| 351 | +func getPayloadKeys(payload map[string]any) []string { |
| 352 | + keys := make([]string, 0, len(payload)) |
| 353 | + for k := range payload { |
| 354 | + keys = append(keys, k) |
| 355 | + } |
| 356 | + return keys |
| 357 | +} |
| 358 | + |
| 359 | +// getMapKeys returns the keys from a map for logging. |
| 360 | +func getMapKeys(m map[string]any) []string { |
| 361 | + keys := make([]string, 0, len(m)) |
| 362 | + for k := range m { |
| 363 | + keys = append(keys, k) |
| 364 | + } |
| 365 | + return keys |
| 366 | +} |
| 367 | + |
| 368 | +// extractCommitSHA extracts the commit SHA from check_run or check_suite events. |
| 369 | +func extractCommitSHA(eventType string, payload map[string]any) string { |
| 370 | + switch eventType { |
| 371 | + case "check_run": |
| 372 | + if checkRun, ok := payload["check_run"].(map[string]any); ok { |
| 373 | + if headSHA, ok := checkRun["head_sha"].(string); ok { |
| 374 | + return headSHA |
| 375 | + } |
| 376 | + } |
| 377 | + case "check_suite": |
| 378 | + if checkSuite, ok := payload["check_suite"].(map[string]any); ok { |
| 379 | + if headSHA, ok := checkSuite["head_sha"].(string); ok { |
| 380 | + return headSHA |
| 381 | + } |
| 382 | + } |
| 383 | + default: |
| 384 | + // Not a check event, no SHA to extract |
| 385 | + } |
| 386 | + return "" |
272 | 387 | } |
0 commit comments