brainiac-github 0.2.9 → 0.3.1

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: ac4e54b24a8bb52bf92a37bfe86acb8d6e16f76c646811dfdb586be8921e4ad3
4
- data.tar.gz: 0c31ee9574f36510100bb08094b884aa3eb81917b89c149ae8fb814fbc6aa7de
3
+ metadata.gz: fa974a43197bb85272bc7a2b53c44b2dc763203d6f5ef7c1286059b50aa2cd97
4
+ data.tar.gz: 15a725f96507c8cf78dc37e73e1e91505b03ba1c5ee21b7f4c5a8c583f353503
5
5
  SHA512:
6
- metadata.gz: 26615c5fd5c52574cd4a9739fa8d2179eb834ed91d805f7f02e7881cee59f3cc2952fce2751b7441ef97fe013e9db1809a026bb987ef7e5108c47b9e738d0185
7
- data.tar.gz: c43852a07188be9a2ae9f8158eb15b91a3c4083dfdc0abab4a0514b1a3b7a755520f4bbde5e8be59919c1a71fc0aa1883c8334c673a7783334f8cd14766eeb19
6
+ metadata.gz: c9f0b4c4c6c453a6c146a9ca5e3fa8aea3657a2aa79345a44e0803121cda492f1ff598256d05de2d4e649058fdfe128249499533c8dc9410c5880194795b1d56
7
+ data.tar.gz: 2a1c93fb97fe48873c4dbffb654d4b4c305929404c70336704c509a06ff0408a33bbb674dcd66bb98356caf6e1ec223b81022445c5b3901da5775342c95df777
@@ -32,6 +32,12 @@ module Brainiac
32
32
 
33
33
  unless base == default_branch
34
34
  LOG.info "PR merged into #{base}, not #{default_branch} — ignoring"
35
+
36
+ # Trigger #2: a child PR merging *into* an epic branch redeploys the
37
+ # epic's ephemeral env (if one is tracked for that branch).
38
+ epic_redeployed = maybe_redeploy_epic_ephemeral_env(base_branch: base)
39
+ return [200, { status: "processed", action: "epic_redeploy", base: base }.to_json] if epic_redeployed
40
+
35
41
  return [200, { status: "ignored", reason: "not merged into #{default_branch}" }.to_json]
36
42
  end
37
43
 
@@ -95,9 +101,18 @@ module Brainiac
95
101
  def handle_pr_synchronized(payload)
96
102
  pr = payload["pull_request"]
97
103
  branch = pr.dig("head", "ref")
104
+ base_branch = pr.dig("base", "ref")
105
+
106
+ # Trigger #1: a push to an epic PR's head branch redeploys the epic env.
107
+ # Epic envs have no card, so resolve this before the card lookup bails.
108
+ epic_redeployed = maybe_redeploy_epic_ephemeral_env(branch: branch)
98
109
 
99
110
  result = find_work_item_by_branch(branch)
100
- return [200, { status: "ignored", reason: "no matching card" }.to_json] unless result
111
+ unless result
112
+ return [200, { status: "processed", action: "epic_sync", branch: branch, epic_redeployed: true }.to_json] if epic_redeployed
113
+
114
+ return [200, { status: "ignored", reason: "no matching card" }.to_json]
115
+ end
101
116
 
102
117
  _internal_id, card_info = result
103
118
  card_number = extract_card_number(card_info)
@@ -105,11 +120,23 @@ module Brainiac
105
120
 
106
121
  return [200, { status: "ignored", reason: "no worktree" }.to_json] unless worktree && File.directory?(worktree)
107
122
 
123
+ # Pull latest changes into worktree
124
+ system("git", "pull", "--ff-only", chdir: worktree)
125
+
126
+ # Try to redeploy to ephemeral Belt environment if one exists
127
+ ephemeral_redeployed = maybe_redeploy_ephemeral_belt_env(
128
+ card_info: card_info, card_number: card_number, worktree: worktree,
129
+ base_branch: base_branch
130
+ )
131
+
132
+ # Also emit the hook for other plugins (e.g., brainiac-fizzy persistent env redeploy)
108
133
  results = Brainiac.emit(:pr_synchronized, card_number: card_number, card_info: card_info,
109
134
  worktree: worktree, pull_request: pr, branch: branch)
110
135
 
111
- if results.any?
112
- [200, { status: "processed", action: "pr_sync", card: card_number }.to_json]
136
+ if ephemeral_redeployed || epic_redeployed || results.any?
137
+ [200,
138
+ { status: "processed", action: "pr_sync", card: card_number, ephemeral_redeployed: ephemeral_redeployed,
139
+ epic_redeployed: epic_redeployed }.to_json]
113
140
  else
114
141
  [200, { status: "ignored", reason: "no deployment plugin" }.to_json]
115
142
  end
@@ -648,6 +675,12 @@ module Brainiac
648
675
 
649
676
  def process_merged_pr(card_info, card_number, branch, pull_request, pr_url, pr_title, project_key, project_config, repo_path)
650
677
  mark_work_item_merged(card_number)
678
+
679
+ # Destroy ephemeral Belt environment BEFORE worktree cleanup (infrastructure lives in worktree)
680
+ maybe_destroy_ephemeral_belt_env(
681
+ card_info: card_info, card_number: card_number, project_key: project_key
682
+ )
683
+
651
684
  cleanup_work_item_worktrees(card_number, repo_path: repo_path,
652
685
  primary_worktree: card_info["worktree"], primary_branch: branch)
653
686
 
@@ -864,6 +897,118 @@ module Brainiac
864
897
 
865
898
  [200, { status: "processed", action: "prod_deploy", closed_cards: closed_cards.map { |c| c[:number] }, project: project_key }.to_json]
866
899
  end
900
+
901
+ # Destroy ephemeral Belt environment on PR merge.
902
+ # Must be called BEFORE worktree cleanup since infrastructure lives in the worktree.
903
+ def maybe_destroy_ephemeral_belt_env(card_info:, card_number:, project_key:)
904
+ unless defined?(BeltConfig) && defined?(BeltEnvironment)
905
+ LOG.debug "[EphemeralEnv] Belt utilities not available — skipping env destruction"
906
+ return
907
+ end
908
+
909
+ env_name = BeltConfig.ephemeral_env_for_card(card_number)
910
+ worktree = card_info&.dig("worktree")
911
+
912
+ unless ephemeral_env_present?(worktree: worktree, env_name: env_name)
913
+ LOG.debug "[EphemeralEnv] No ephemeral env '#{env_name}' found for card ##{card_number}"
914
+ return
915
+ end
916
+
917
+ unless worktree && File.directory?(worktree)
918
+ LOG.warn "[EphemeralEnv] Cannot destroy '#{env_name}' — worktree not found"
919
+ return
920
+ end
921
+
922
+ LOG.info "[EphemeralEnv] Destroying ephemeral environment '#{env_name}' for card ##{card_number}"
923
+ BeltEnvironment.destroy_environment(worktree: worktree, env_name: env_name)
924
+ rescue StandardError => e
925
+ LOG.error "[EphemeralEnv] Error destroying ephemeral env: #{e.message}"
926
+ end
927
+
928
+ # Redeploy to ephemeral Belt environment on PR sync (new commits pushed).
929
+ # Only redeploys if an ephemeral env already exists for this card.
930
+ def maybe_redeploy_ephemeral_belt_env(card_info:, card_number:, worktree:, base_branch: nil)
931
+ return false unless defined?(BeltConfig) && defined?(BeltEnvironment)
932
+ return false unless belt_app_worktree?(worktree)
933
+
934
+ env_name = BeltConfig.ephemeral_env_for_card(card_number)
935
+
936
+ unless ephemeral_env_present?(worktree: worktree, env_name: env_name)
937
+ LOG.debug "[EphemeralEnv] No ephemeral env '#{env_name}' for card ##{card_number} — skipping redeploy"
938
+ return false
939
+ end
940
+
941
+ LOG.info "[EphemeralEnv] Redeploying to ephemeral environment '#{env_name}' (PR sync)"
942
+
943
+ frontend_only = BeltEnvironment.frontend_only_changes?(worktree: worktree, base_branch: base_branch)
944
+ BeltEnvironment.deploy(worktree: worktree, env_name: env_name, frontend_only: frontend_only)
945
+ true
946
+ rescue StandardError => e
947
+ LOG.error "[EphemeralEnv] Error redeploying ephemeral env: #{e.message}"
948
+ false
949
+ end
950
+
951
+ # Redeploy an epic's ephemeral Belt environment.
952
+ #
953
+ # Epic envs aren't keyed by card number — they're tracked in
954
+ # ephemeral_envs.json with `epic_branch` / `epic_pr` fields. This
955
+ # resolves the env by matching the head branch (PR sync / push) or the
956
+ # PR url (child merge into the epic branch), then redeploys its worktree.
957
+ #
958
+ # @param branch [String, nil] Head branch of the synced PR
959
+ # @param base_branch [String, nil] Base branch (for merge-triggered redeploys)
960
+ # @return [Boolean] true if a redeploy was attempted
961
+ def maybe_redeploy_epic_ephemeral_env(branch: nil, base_branch: nil)
962
+ return false unless defined?(BeltConfig) && defined?(BeltEnvironment)
963
+
964
+ # Trigger #1: a push/synchronize on the epic PR branch itself.
965
+ # Trigger #2: a child PR merging *into* the epic branch (base match).
966
+ match = BeltConfig.epic_env_for_branch(branch) ||
967
+ BeltConfig.epic_env_for_branch(base_branch)
968
+ return false unless match
969
+
970
+ env_name, entry = match
971
+ worktree = entry["worktree"]
972
+
973
+ unless worktree && File.directory?(worktree)
974
+ LOG.warn "[EphemeralEnv] Epic env '#{env_name}' worktree missing: #{worktree.inspect} — skipping redeploy"
975
+ return false
976
+ end
977
+ return false unless belt_app_worktree?(worktree)
978
+
979
+ unless ephemeral_env_present?(worktree: worktree, env_name: env_name)
980
+ LOG.debug "[EphemeralEnv] Epic env '#{env_name}' not configured in worktree — skipping redeploy"
981
+ return false
982
+ end
983
+
984
+ # Pull latest into the epic worktree so the deploy reflects the new commits.
985
+ system("git", "pull", "--ff-only", chdir: worktree)
986
+
987
+ LOG.info "[EphemeralEnv] Redeploying epic environment '#{env_name}' (branch: #{branch || base_branch})"
988
+
989
+ # No reliable per-push base for an epic worktree, so let the diff base
990
+ # fall back to origin/HEAD → origin/main → origin/master.
991
+ frontend_only = BeltEnvironment.frontend_only_changes?(worktree: worktree)
992
+ BeltEnvironment.deploy(worktree: worktree, env_name: env_name, frontend_only: frontend_only)
993
+ true
994
+ rescue StandardError => e
995
+ LOG.error "[EphemeralEnv] Error redeploying epic env: #{e.message}"
996
+ false
997
+ end
998
+
999
+ def belt_app_worktree?(worktree)
1000
+ BeltEnvironment.respond_to?(:belt_app?) && BeltEnvironment.belt_app?(worktree)
1001
+ end
1002
+
1003
+ # Worktree infrastructure/<env>/ is the source of truth; tracking JSON is a fallback.
1004
+ def ephemeral_env_present?(worktree:, env_name:)
1005
+ if BeltEnvironment.respond_to?(:environment_configured?) &&
1006
+ BeltEnvironment.environment_configured?(worktree: worktree, env_name: env_name)
1007
+ return true
1008
+ end
1009
+
1010
+ BeltConfig.ephemeral_env?(env_name)
1011
+ end
867
1012
  end
868
1013
  end
869
1014
  end
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Github
6
- VERSION = "0.2.9"
6
+ VERSION = "0.3.1"
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.9
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis