brainiac-github 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/brainiac/plugins/github/handler.rb +106 -7
- data/lib/brainiac/plugins/github/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4646b834b308b619a34c5b94da48aeb88b80caa3fb5efd107ac40a5dc56da2a0
|
|
4
|
+
data.tar.gz: af0c3f9c0b47e366928760c64168a74eb3651f87c193156cbced75336626bac0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 945f7fad37cd33a37d755b6517faa8638c4a351d74f11edb60bf8299d04933837f38b34e1fa39afaea34af2b2d57c89f5b88d641fb6e6001436622928143d445
|
|
7
|
+
data.tar.gz: 5c2e27ab69d9af292ddbd3c7ac299629f1b97c005b85275bdaaa31d24b010903a75b4973d6d91cf99ab5ea580c53c67c77e341724f46c633a2628d2a78ad6c67
|
|
@@ -16,7 +16,8 @@ module Brainiac
|
|
|
16
16
|
default_branch = payload.dig("repository", "default_branch") || "main"
|
|
17
17
|
|
|
18
18
|
# Extract card number from branch name (e.g., "fizzy-1182-add-goodbye-method" → 1182)
|
|
19
|
-
card_number_from_branch = branch&.match(/^fizzy-(\d+)-/)
|
|
19
|
+
card_number_from_branch = branch&.match(/^fizzy-(\d+)-/)
|
|
20
|
+
card_number_from_branch = card_number_from_branch[1].to_i if card_number_from_branch
|
|
20
21
|
|
|
21
22
|
# Emit a hook for ALL merges (including epic branches) so consumers can decide
|
|
22
23
|
if card_number_from_branch
|
|
@@ -125,6 +126,24 @@ module Brainiac
|
|
|
125
126
|
repo_name = payload.dig("repository", "full_name")
|
|
126
127
|
review_state = review["state"]
|
|
127
128
|
reviewer = review.dig("user", "login")
|
|
129
|
+
reviewer_type = review.dig("user", "type")
|
|
130
|
+
|
|
131
|
+
# If the review was submitted by a remote agent bot, ignore — the other machine handles it.
|
|
132
|
+
if reviewer_type == "Bot"
|
|
133
|
+
reviewing_agent = agent_name_from_bot_login(reviewer)
|
|
134
|
+
if reviewing_agent && !local_agent_names.include?(reviewing_agent)
|
|
135
|
+
LOG.info "[GitHub] Ignoring review from remote agent bot #{reviewer} (agent: #{reviewing_agent}) on PR ##{pr_number}"
|
|
136
|
+
return [200, { status: "ignored", reason: "remote agent bot review" }.to_json]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# If the PR was opened by a remote agent bot (not local to this machine),
|
|
141
|
+
# skip dispatch — the other machine that owns the agent will handle it.
|
|
142
|
+
unless pr_owned_locally?(pr)
|
|
143
|
+
pr_author = pr.dig("user", "login")
|
|
144
|
+
LOG.info "[GitHub] PR ##{pr_number} owned by remote agent (#{pr_author}) — skipping dispatch"
|
|
145
|
+
return [200, { status: "ignored", reason: "PR owned by remote agent" }.to_json]
|
|
146
|
+
end
|
|
128
147
|
|
|
129
148
|
project_result = identify_project_by_repo(repo_name)
|
|
130
149
|
return [200, { status: "ignored", reason: "no matching project" }.to_json] unless project_result
|
|
@@ -192,6 +211,16 @@ module Brainiac
|
|
|
192
211
|
return [200, { status: "ignored", reason: "not a PR comment" }.to_json]
|
|
193
212
|
end
|
|
194
213
|
|
|
214
|
+
# If the PR was opened by a remote agent bot (not local to this machine),
|
|
215
|
+
# skip dispatch — the other machine that owns the agent will handle it.
|
|
216
|
+
pr_author = issue.dig("user", "login")
|
|
217
|
+
pr_author_type = issue.dig("user", "type")
|
|
218
|
+
unless pr_author_owned_locally?(pr_author, pr_author_type)
|
|
219
|
+
pr_number = issue["number"]
|
|
220
|
+
LOG.info "[GitHub] PR ##{pr_number} owned by remote agent (#{pr_author}) — skipping dispatch"
|
|
221
|
+
return [200, { status: "ignored", reason: "PR owned by remote agent" }.to_json]
|
|
222
|
+
end
|
|
223
|
+
|
|
195
224
|
project_result = identify_project_by_repo(repo_name)
|
|
196
225
|
unless project_result
|
|
197
226
|
LOG.info "No project found for GitHub repo #{repo_name}"
|
|
@@ -217,12 +246,12 @@ module Brainiac
|
|
|
217
246
|
agent_name = mentioned
|
|
218
247
|
end
|
|
219
248
|
|
|
220
|
-
# Ignore comments from
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
249
|
+
# Ignore comments from agent bots that shouldn't trigger dispatch on this machine:
|
|
250
|
+
# 1. Self-triggered: bot matches the dispatching agent (prevents loops)
|
|
251
|
+
# 2. Remote agent: bot matches a known agent that isn't local (other machine handles it)
|
|
252
|
+
# Cross-agent LOCAL bot comments (e.g. GLaDOS commenting on Galen's PR) are still processed.
|
|
253
|
+
bot_ignore_reason = bot_comment_ignore_reason(comment_user, comment_user_type, agent_name, pr_number)
|
|
254
|
+
return bot_ignore_reason if bot_ignore_reason
|
|
226
255
|
|
|
227
256
|
branch = fetch_pr_branch(repo_name, pr_number, agent_name, project_config)
|
|
228
257
|
result = find_work_item_by_branch(branch)
|
|
@@ -317,6 +346,52 @@ module Brainiac
|
|
|
317
346
|
nil
|
|
318
347
|
end
|
|
319
348
|
|
|
349
|
+
# Determine if this machine should handle a PR based on who opened it.
|
|
350
|
+
# If the PR was opened by a bot that maps to a known agent, only the
|
|
351
|
+
# machine where that agent is local should dispatch. Human-opened PRs
|
|
352
|
+
# are always considered local (handled by normal routing logic).
|
|
353
|
+
#
|
|
354
|
+
# This prevents both machines from dispatching when they share the same
|
|
355
|
+
# GitHub webhook — e.g. Adam's Kaylee opens a PR, only Adam's machine handles it.
|
|
356
|
+
def pr_owned_locally?(pull_request)
|
|
357
|
+
pr_author = pull_request.dig("user", "login")
|
|
358
|
+
pr_author_type = pull_request.dig("user", "type")
|
|
359
|
+
pr_author_owned_locally?(pr_author, pr_author_type)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Check PR ownership given the author login and account type.
|
|
363
|
+
# Used by both pull_request_review (has full PR object) and issue_comment
|
|
364
|
+
# (has issue.user which is the PR author on PR-linked issues).
|
|
365
|
+
def pr_author_owned_locally?(pr_author, pr_author_type)
|
|
366
|
+
# Human-opened PRs are always handled (normal routing applies)
|
|
367
|
+
return true unless pr_author_type == "Bot"
|
|
368
|
+
|
|
369
|
+
# Extract agent name from bot login (e.g. "kaylee-brainiac[bot]" → "Kaylee")
|
|
370
|
+
agent_from_bot = agent_name_from_bot_login(pr_author)
|
|
371
|
+
return true unless agent_from_bot
|
|
372
|
+
|
|
373
|
+
# If the bot's agent is local to this machine, we own it
|
|
374
|
+
local_agent_names.include?(agent_from_bot)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Extract a Brainiac agent display name from a GitHub bot login.
|
|
378
|
+
# Bot logins follow the pattern "<name>-brainiac[bot]" or "<name>-brainiac".
|
|
379
|
+
# Returns the matched agent display name, or nil if not a known agent bot.
|
|
380
|
+
def agent_name_from_bot_login(bot_login)
|
|
381
|
+
return nil unless bot_login
|
|
382
|
+
|
|
383
|
+
# Normalize: remove [bot] suffix, lowercase
|
|
384
|
+
normalized = bot_login.to_s.downcase.delete_suffix("[bot]").strip
|
|
385
|
+
|
|
386
|
+
# Match against known agents — bot logins are "<agent>-brainiac"
|
|
387
|
+
all_agent_names.each do |name|
|
|
388
|
+
return name if normalized == "#{name.downcase}-brainiac"
|
|
389
|
+
return name if normalized == name.downcase
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
nil
|
|
393
|
+
end
|
|
394
|
+
|
|
320
395
|
# Generate a GH_TOKEN env var for the agent process so `gh` CLI
|
|
321
396
|
# authenticates as the app bot instead of the user's personal account.
|
|
322
397
|
# Falls back to the shared "brainiac" app if the agent's own app isn't configured.
|
|
@@ -429,6 +504,30 @@ module Brainiac
|
|
|
429
504
|
nil
|
|
430
505
|
end
|
|
431
506
|
|
|
507
|
+
# Determine if a bot comment should be ignored by this machine.
|
|
508
|
+
# Returns a Rack response array if the comment should be ignored, or nil if it should be processed.
|
|
509
|
+
#
|
|
510
|
+
# Ignores:
|
|
511
|
+
# 1. Self-triggered: bot matches the dispatching agent (prevents loops)
|
|
512
|
+
# 2. Remote agent: bot matches a known agent that isn't local (other machine handles it)
|
|
513
|
+
# Allows: cross-agent LOCAL bot comments (e.g. GLaDOS commenting on Galen's PR)
|
|
514
|
+
def bot_comment_ignore_reason(comment_user, comment_user_type, agent_name, pr_number)
|
|
515
|
+
return nil unless comment_user_type == "Bot"
|
|
516
|
+
|
|
517
|
+
if own_bot_comment?(comment_user, agent_name)
|
|
518
|
+
LOG.info "Ignoring self-triggered bot comment from #{comment_user} (agent: #{agent_name})"
|
|
519
|
+
return [200, { status: "ignored", reason: "self-triggered bot comment" }.to_json]
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
commenting_agent = agent_name_from_bot_login(comment_user)
|
|
523
|
+
if commenting_agent && !local_agent_names.include?(commenting_agent)
|
|
524
|
+
LOG.info "[GitHub] Ignoring comment from remote agent bot #{comment_user} (agent: #{commenting_agent}) on PR ##{pr_number}"
|
|
525
|
+
return [200, { status: "ignored", reason: "remote agent bot comment" }.to_json]
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
nil
|
|
529
|
+
end
|
|
530
|
+
|
|
432
531
|
# Check if a bot comment is from the same agent that would handle this PR.
|
|
433
532
|
# e.g. "galen-brainiac[bot]" is a self-trigger for agent "Galen",
|
|
434
533
|
# but "glados-brainiac[bot]" is a cross-agent request and should be processed.
|