brainiac-github 0.2.8 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db8e2a2ac41b7a6fe786239a6a909aac61975a70273068c243b301eeb2407f93
4
- data.tar.gz: 8427ed0cbf86df7198149a11cbf64309471dea9ce9293ad9a49db57f2d14a03d
3
+ metadata.gz: df22ea85b48c5fd4960ad4e8bb28c3e59f551839c2cbc900dc065edc74039ed3
4
+ data.tar.gz: d8708dd8352a97e01bac35eb65ddb13ab7aea6249876987d4e73fd89328f97ee
5
5
  SHA512:
6
- metadata.gz: e385a16a159af98f746a0c7ada60c04802de2549d8ace8a37ee8830ad8886d9bf5fa5ba89a34d38ba69b21c4b6aa82b0a1d24114a950e19bd10b5f7a45f9877d
7
- data.tar.gz: 43cc967f3aafd227715a82e2a366e289452e0136e29415a52d244b0e796b9355f72983b1b32a056c86707a820dad269f9c2cfa9324d96945580c6574f6554685
6
+ metadata.gz: 241be88652dfaf39203b734d212d6b91e641017a3344cd4c8cabdad2332e1262f8e848e12680ca0612d43c9e44db6c124c9d1e6e76aecc5c625a4f30b8cff499
7
+ data.tar.gz: 5096d8be04c2c469770b93e5c998502cd545cb78553f64ec40e621d84f8d03c0d5e3ec9263c77237db1bc92cdb7396cb2ddff4b626be6230ca279b3f91676a14
@@ -61,15 +61,19 @@ module Brainiac
61
61
  post("/repos/#{repo}/issues/comments/#{comment_id}/reactions", { content: reaction }, agent_key: agent_key)
62
62
  end
63
63
 
64
- # POST a reaction on a PR review.
64
+ # POST a reaction on a PR (or issue) itself.
65
+ #
66
+ # GitHub's reactions API has NO endpoint for PR reviews — only for
67
+ # issue comments, PR review comments, issues, and PRs. Since a PR is
68
+ # an issue under the hood, PR reactions go through the issues path.
65
69
  #
66
70
  # @param repo [String] "owner/repo"
67
- # @param review_id [Integer]
68
- # @param reaction [String]
71
+ # @param pr_number [Integer]
72
+ # @param reaction [String] e.g. "eyes", "+1", "rocket"
69
73
  # @param agent_key [String, nil] agent key for per-agent app identity
70
74
  # @return [Hash] parsed response
71
- def create_review_reaction(repo, review_id, reaction, agent_key: nil)
72
- post("/repos/#{repo}/pulls/reviews/#{review_id}/reactions", { content: reaction }, agent_key: agent_key)
75
+ def create_pr_reaction(repo, pr_number, reaction, agent_key: nil)
76
+ post("/repos/#{repo}/issues/#{pr_number}/reactions", { content: reaction }, agent_key: agent_key)
73
77
  end
74
78
 
75
79
  # GET request to GitHub API.
@@ -105,11 +105,21 @@ module Brainiac
105
105
 
106
106
  return [200, { status: "ignored", reason: "no worktree" }.to_json] unless worktree && File.directory?(worktree)
107
107
 
108
+ # Pull latest changes into worktree
109
+ system("git", "pull", "--ff-only", chdir: worktree)
110
+
111
+ # Try to redeploy to ephemeral Belt environment if one exists
112
+ ephemeral_redeployed = maybe_redeploy_ephemeral_belt_env(
113
+ card_info: card_info, card_number: card_number, worktree: worktree,
114
+ base_branch: pr.dig("base", "ref")
115
+ )
116
+
117
+ # Also emit the hook for other plugins (e.g., brainiac-fizzy persistent env redeploy)
108
118
  results = Brainiac.emit(:pr_synchronized, card_number: card_number, card_info: card_info,
109
119
  worktree: worktree, pull_request: pr, branch: branch)
110
120
 
111
- if results.any?
112
- [200, { status: "processed", action: "pr_sync", card: card_number }.to_json]
121
+ if ephemeral_redeployed || results.any?
122
+ [200, { status: "processed", action: "pr_sync", card: card_number, ephemeral_redeployed: ephemeral_redeployed }.to_json]
113
123
  else
114
124
  [200, { status: "ignored", reason: "no deployment plugin" }.to_json]
115
125
  end
@@ -648,6 +658,12 @@ module Brainiac
648
658
 
649
659
  def process_merged_pr(card_info, card_number, branch, pull_request, pr_url, pr_title, project_key, project_config, repo_path)
650
660
  mark_work_item_merged(card_number)
661
+
662
+ # Destroy ephemeral Belt environment BEFORE worktree cleanup (infrastructure lives in worktree)
663
+ maybe_destroy_ephemeral_belt_env(
664
+ card_info: card_info, card_number: card_number, project_key: project_key
665
+ )
666
+
651
667
  cleanup_work_item_worktrees(card_number, repo_path: repo_path,
652
668
  primary_worktree: card_info["worktree"], primary_branch: branch)
653
669
 
@@ -730,10 +746,12 @@ module Brainiac
730
746
  agent_name = resolve_work_item_agent(card_info, project_config)
731
747
 
732
748
  Thread.new do
749
+ # GitHub has no reactions endpoint for PR reviews, so react on the
750
+ # PR itself (PRs are issues under the hood) to signal "on it".
733
751
  if AppClient.configured?(agent_name)
734
- AppClient.create_review_reaction(repo_name, review_id, "eyes", agent_key: agent_name)
752
+ AppClient.create_pr_reaction(repo_name, pr_number, "eyes", agent_key: agent_name)
735
753
  else
736
- run_cmd("gh", "api", "-X", "POST", "/repos/#{repo_name}/pulls/reviews/#{review_id}/reactions",
754
+ run_cmd("gh", "api", "-X", "POST", "/repos/#{repo_name}/issues/#{pr_number}/reactions",
737
755
  "-f", "content=eyes", "-H", "Accept: application/vnd.github+json", chdir: repo_path)
738
756
  end
739
757
 
@@ -862,6 +880,71 @@ module Brainiac
862
880
 
863
881
  [200, { status: "processed", action: "prod_deploy", closed_cards: closed_cards.map { |c| c[:number] }, project: project_key }.to_json]
864
882
  end
883
+
884
+ # Destroy ephemeral Belt environment on PR merge.
885
+ # Must be called BEFORE worktree cleanup since infrastructure lives in the worktree.
886
+ def maybe_destroy_ephemeral_belt_env(card_info:, card_number:, project_key:)
887
+ unless defined?(BeltConfig) && defined?(BeltEnvironment)
888
+ LOG.debug "[EphemeralEnv] Belt utilities not available — skipping env destruction"
889
+ return
890
+ end
891
+
892
+ env_name = BeltConfig.ephemeral_env_for_card(card_number)
893
+ worktree = card_info&.dig("worktree")
894
+
895
+ unless ephemeral_env_present?(worktree: worktree, env_name: env_name)
896
+ LOG.debug "[EphemeralEnv] No ephemeral env '#{env_name}' found for card ##{card_number}"
897
+ return
898
+ end
899
+
900
+ unless worktree && File.directory?(worktree)
901
+ LOG.warn "[EphemeralEnv] Cannot destroy '#{env_name}' — worktree not found"
902
+ return
903
+ end
904
+
905
+ LOG.info "[EphemeralEnv] Destroying ephemeral environment '#{env_name}' for card ##{card_number}"
906
+ BeltEnvironment.destroy_environment(worktree: worktree, env_name: env_name)
907
+ rescue StandardError => e
908
+ LOG.error "[EphemeralEnv] Error destroying ephemeral env: #{e.message}"
909
+ end
910
+
911
+ # Redeploy to ephemeral Belt environment on PR sync (new commits pushed).
912
+ # Only redeploys if an ephemeral env already exists for this card.
913
+ def maybe_redeploy_ephemeral_belt_env(card_info:, card_number:, worktree:, base_branch: nil)
914
+ return false unless defined?(BeltConfig) && defined?(BeltEnvironment)
915
+ return false unless belt_app_worktree?(worktree)
916
+
917
+ env_name = BeltConfig.ephemeral_env_for_card(card_number)
918
+
919
+ unless ephemeral_env_present?(worktree: worktree, env_name: env_name)
920
+ LOG.debug "[EphemeralEnv] No ephemeral env '#{env_name}' for card ##{card_number} — skipping redeploy"
921
+ return false
922
+ end
923
+
924
+ LOG.info "[EphemeralEnv] Redeploying to ephemeral environment '#{env_name}' (PR sync)"
925
+
926
+ frontend_only = BeltEnvironment.frontend_only_changes?(worktree: worktree, base_branch: base_branch)
927
+ BeltEnvironment.deploy(worktree: worktree, env_name: env_name, frontend_only: frontend_only)
928
+ true
929
+ rescue StandardError => e
930
+ LOG.error "[EphemeralEnv] Error redeploying ephemeral env: #{e.message}"
931
+ false
932
+ end
933
+
934
+ # belt_app? lives on BeltEnvironment (via BeltHelpers), not on this Handler module.
935
+ def belt_app_worktree?(worktree)
936
+ BeltEnvironment.respond_to?(:belt_app?) && BeltEnvironment.belt_app?(worktree)
937
+ end
938
+
939
+ # Worktree infrastructure/<env>/ is the source of truth; tracking JSON is a fallback.
940
+ def ephemeral_env_present?(worktree:, env_name:)
941
+ if BeltEnvironment.respond_to?(:environment_configured?) &&
942
+ BeltEnvironment.environment_configured?(worktree: worktree, env_name: env_name)
943
+ return true
944
+ end
945
+
946
+ BeltConfig.ephemeral_env?(env_name)
947
+ end
865
948
  end
866
949
  end
867
950
  end
@@ -14,6 +14,19 @@ module Brainiac
14
14
  - ``` ```language ``` for code blocks
15
15
  - `- item` for lists
16
16
 
17
+ ### @Mentions (STRICT — read carefully)
18
+ On GitHub, an `@name` only works if it maps to a real GitHub account.
19
+ The other agents (Galen, GLaDOS, Threepio, etc.) are NOT GitHub users —
20
+ `@threepio` just pings some unrelated stranger who happens to own that
21
+ username. That's noisy and rude.
22
+
23
+ Rules for GitHub comments:
24
+ - The ONLY people you may @mention are **@ardavis** (Andy) and **@fladamd** (Adam).
25
+ - NEVER @mention any other agent or person. Refer to other agents by
26
+ plain name instead — write `Threepio` or `GLaDOS`, not `@threepio`.
27
+ - If you're unsure whether a name is a real GitHub account, don't @mention it.
28
+ Drop the `@` and use the plain name.
29
+
17
30
  ### Scope
18
31
  You are responding to activity on a GitHub PR. Focus on the code changes and review feedback.
19
32
  When posting comments, post on the PR unless specifically asked to update the card.
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Github
6
- VERSION = "0.2.8"
6
+ VERSION = "0.3.0"
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis