brainiac-github 0.2.2 → 0.2.4
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 +96 -2
- 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: 4cb6c5132e15d851261b150a636b895a0d8d8f6487b51a5049290a1ce2f12274
|
|
4
|
+
data.tar.gz: c6bf867e0bb5dbca4008e0971dc0568fb95755c2b296b7865e889e2d09645120
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed2481547416f7bd7d0194aff3ec96da00811c6fe2a4ee8478b4f90c63820fde0318e7ea2427ba07646bd5755abcba3ac2a3c32af78170abb6eebb461f54a4dc
|
|
7
|
+
data.tar.gz: 3df84f31506b7fbeeaf7636f84b8d8602a9e6fee9d6d8a6a4b18b4f5b76a9de666df79e5c2dccb9c81d1f90727bc8ffd60cc8b784776ea8f55ef0385e3234791
|
|
@@ -14,6 +14,22 @@ module Brainiac
|
|
|
14
14
|
repo_full_name = payload.dig("repository", "full_name")
|
|
15
15
|
|
|
16
16
|
default_branch = payload.dig("repository", "default_branch") || "main"
|
|
17
|
+
|
|
18
|
+
# Extract card number from branch name (e.g., "fizzy-1182-add-goodbye-method" → 1182)
|
|
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
|
|
21
|
+
|
|
22
|
+
# Emit a hook for ALL merges (including epic branches) so consumers can decide
|
|
23
|
+
if card_number_from_branch
|
|
24
|
+
Brainiac.emit(:pr_merged_to_branch,
|
|
25
|
+
card_number: card_number_from_branch,
|
|
26
|
+
branch: branch,
|
|
27
|
+
base_branch: base,
|
|
28
|
+
pr_url: pr_url,
|
|
29
|
+
pr_title: pr_title,
|
|
30
|
+
repo_full_name: repo_full_name)
|
|
31
|
+
end
|
|
32
|
+
|
|
17
33
|
unless base == default_branch
|
|
18
34
|
LOG.info "PR merged into #{base}, not #{default_branch} — ignoring"
|
|
19
35
|
return [200, { status: "ignored", reason: "not merged into #{default_branch}" }.to_json]
|
|
@@ -111,8 +127,12 @@ module Brainiac
|
|
|
111
127
|
review_state = review["state"]
|
|
112
128
|
reviewer = review.dig("user", "login")
|
|
113
129
|
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
# If the PR was opened by a remote agent bot (not local to this machine),
|
|
131
|
+
# skip dispatch — the other machine that owns the agent will handle it.
|
|
132
|
+
unless pr_owned_locally?(pr)
|
|
133
|
+
pr_author = pr.dig("user", "login")
|
|
134
|
+
LOG.info "[GitHub] PR ##{pr_number} owned by remote agent (#{pr_author}) — skipping dispatch"
|
|
135
|
+
return [200, { status: "ignored", reason: "PR owned by remote agent" }.to_json]
|
|
116
136
|
end
|
|
117
137
|
|
|
118
138
|
project_result = identify_project_by_repo(repo_name)
|
|
@@ -134,6 +154,24 @@ module Brainiac
|
|
|
134
154
|
card_info = {}
|
|
135
155
|
card_number = nil
|
|
136
156
|
end
|
|
157
|
+
|
|
158
|
+
# Always emit the hook so gate tracking works for approvals too
|
|
159
|
+
Brainiac.emit(:pr_review_received,
|
|
160
|
+
card_number: card_number,
|
|
161
|
+
reviewer: reviewer,
|
|
162
|
+
review_state: review_state,
|
|
163
|
+
pr_number: pr_number,
|
|
164
|
+
repo_name: repo_name,
|
|
165
|
+
project_config: project_config,
|
|
166
|
+
repo_path: repo_path)
|
|
167
|
+
|
|
168
|
+
# Only dispatch the implementation agent for changes_requested or commented reviews
|
|
169
|
+
# Approvals don't need the agent to do anything (the gate hook handles the flow)
|
|
170
|
+
unless %w[changes_requested commented].include?(review_state)
|
|
171
|
+
LOG.info "PR review (#{review_state}) by #{reviewer} on PR ##{pr_number} — hook emitted, no agent dispatch"
|
|
172
|
+
return [200, { status: "processed", card: card_number, pr: pr_number, reviewer: reviewer, action: "hook_only" }.to_json]
|
|
173
|
+
end
|
|
174
|
+
|
|
137
175
|
card_key = "pr-review-#{repo_name.tr("/", "-")}-#{pr_number}"
|
|
138
176
|
|
|
139
177
|
return [200, { status: "ignored", reason: "session already active" }.to_json] if session_active?(card_key)
|
|
@@ -163,6 +201,16 @@ module Brainiac
|
|
|
163
201
|
return [200, { status: "ignored", reason: "not a PR comment" }.to_json]
|
|
164
202
|
end
|
|
165
203
|
|
|
204
|
+
# If the PR was opened by a remote agent bot (not local to this machine),
|
|
205
|
+
# skip dispatch — the other machine that owns the agent will handle it.
|
|
206
|
+
pr_author = issue.dig("user", "login")
|
|
207
|
+
pr_author_type = issue.dig("user", "type")
|
|
208
|
+
unless pr_author_owned_locally?(pr_author, pr_author_type)
|
|
209
|
+
pr_number = issue["number"]
|
|
210
|
+
LOG.info "[GitHub] PR ##{pr_number} owned by remote agent (#{pr_author}) — skipping dispatch"
|
|
211
|
+
return [200, { status: "ignored", reason: "PR owned by remote agent" }.to_json]
|
|
212
|
+
end
|
|
213
|
+
|
|
166
214
|
project_result = identify_project_by_repo(repo_name)
|
|
167
215
|
unless project_result
|
|
168
216
|
LOG.info "No project found for GitHub repo #{repo_name}"
|
|
@@ -288,6 +336,52 @@ module Brainiac
|
|
|
288
336
|
nil
|
|
289
337
|
end
|
|
290
338
|
|
|
339
|
+
# Determine if this machine should handle a PR based on who opened it.
|
|
340
|
+
# If the PR was opened by a bot that maps to a known agent, only the
|
|
341
|
+
# machine where that agent is local should dispatch. Human-opened PRs
|
|
342
|
+
# are always considered local (handled by normal routing logic).
|
|
343
|
+
#
|
|
344
|
+
# This prevents both machines from dispatching when they share the same
|
|
345
|
+
# GitHub webhook — e.g. Adam's Kaylee opens a PR, only Adam's machine handles it.
|
|
346
|
+
def pr_owned_locally?(pull_request)
|
|
347
|
+
pr_author = pull_request.dig("user", "login")
|
|
348
|
+
pr_author_type = pull_request.dig("user", "type")
|
|
349
|
+
pr_author_owned_locally?(pr_author, pr_author_type)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Check PR ownership given the author login and account type.
|
|
353
|
+
# Used by both pull_request_review (has full PR object) and issue_comment
|
|
354
|
+
# (has issue.user which is the PR author on PR-linked issues).
|
|
355
|
+
def pr_author_owned_locally?(pr_author, pr_author_type)
|
|
356
|
+
# Human-opened PRs are always handled (normal routing applies)
|
|
357
|
+
return true unless pr_author_type == "Bot"
|
|
358
|
+
|
|
359
|
+
# Extract agent name from bot login (e.g. "kaylee-brainiac[bot]" → "Kaylee")
|
|
360
|
+
agent_from_bot = agent_name_from_bot_login(pr_author)
|
|
361
|
+
return true unless agent_from_bot
|
|
362
|
+
|
|
363
|
+
# If the bot's agent is local to this machine, we own it
|
|
364
|
+
local_agent_names.include?(agent_from_bot)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Extract a Brainiac agent display name from a GitHub bot login.
|
|
368
|
+
# Bot logins follow the pattern "<name>-brainiac[bot]" or "<name>-brainiac".
|
|
369
|
+
# Returns the matched agent display name, or nil if not a known agent bot.
|
|
370
|
+
def agent_name_from_bot_login(bot_login)
|
|
371
|
+
return nil unless bot_login
|
|
372
|
+
|
|
373
|
+
# Normalize: remove [bot] suffix, lowercase
|
|
374
|
+
normalized = bot_login.to_s.downcase.delete_suffix("[bot]").strip
|
|
375
|
+
|
|
376
|
+
# Match against known agents — bot logins are "<agent>-brainiac"
|
|
377
|
+
all_agent_names.each do |name|
|
|
378
|
+
return name if normalized == "#{name.downcase}-brainiac"
|
|
379
|
+
return name if normalized == name.downcase
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
nil
|
|
383
|
+
end
|
|
384
|
+
|
|
291
385
|
# Generate a GH_TOKEN env var for the agent process so `gh` CLI
|
|
292
386
|
# authenticates as the app bot instead of the user's personal account.
|
|
293
387
|
# Falls back to the shared "brainiac" app if the agent's own app isn't configured.
|