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 +4 -4
- data/lib/brainiac/plugins/github/handler.rb +148 -3
- 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: fa974a43197bb85272bc7a2b53c44b2dc763203d6f5ef7c1286059b50aa2cd97
|
|
4
|
+
data.tar.gz: 15a725f96507c8cf78dc37e73e1e91505b03ba1c5ee21b7f4c5a8c583f353503
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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,
|
|
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
|