brainiac-fizzy 0.0.27 → 0.0.29
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/README.md +21 -0
- data/lib/brainiac/plugins/fizzy/config.rb +20 -0
- data/lib/brainiac/plugins/fizzy/handlers/deployments.rb +109 -0
- data/lib/brainiac/plugins/fizzy/handlers/summarize.rb +6 -1
- data/lib/brainiac/plugins/fizzy/helpers.rb +42 -36
- data/lib/brainiac/plugins/fizzy/hooks.rb +70 -30
- data/lib/brainiac/plugins/fizzy/prompts.rb +24 -7
- data/lib/brainiac/plugins/fizzy/version.rb +1 -1
- data/lib/brainiac/plugins/fizzy.rb +0 -1
- metadata +1 -2
- data/lib/brainiac/plugins/fizzy/env_url.rb +0 -73
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93dfc73c8a3984f834883fbc59057446f220cf640ef14feecdc3882eab0f5e79
|
|
4
|
+
data.tar.gz: 16dccf767e981ab8b7ec4f4c9ad570a6af713fe9deb410f0e3163f8745def891
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfabf4b7239f9fa9181fb327c24660e7c39c0a1558151362330ff67c8c334ba9565239e5da35c1335c67bd5dfdd246cacf8295509e6e61c86b9afb0f2f6be43d
|
|
7
|
+
data.tar.gz: ebafbeceb274b07e70f23f69d8b7123af573d646e58d92eb756b693bf2cf4d87a12ab7fc79ddca3ced785c54d3beca9c8c602a4c655d0158067cc952d441a90f
|
data/README.md
CHANGED
|
@@ -37,6 +37,8 @@ Fizzy configuration lives in `~/.brainiac/fizzy.json` (same as before):
|
|
|
37
37
|
{ "id": "user-id-1", "name": "Andy", "human": true },
|
|
38
38
|
{ "id": "agent-id-1", "name": "Galen", "human": false }
|
|
39
39
|
],
|
|
40
|
+
"uat_agent": false,
|
|
41
|
+
"uat_agent_tag": "uat",
|
|
40
42
|
"boards": {
|
|
41
43
|
"development": {
|
|
42
44
|
"board_id": "your-board-id",
|
|
@@ -51,6 +53,25 @@ Fizzy configuration lives in `~/.brainiac/fizzy.json` (same as before):
|
|
|
51
53
|
}
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
### UAT agent (`uat_agent`)
|
|
57
|
+
|
|
58
|
+
When a PR is merged and its card moves to the UAT column, Brainiac can dispatch
|
|
59
|
+
an LLM agent to write manual testing steps as a card comment. This spends tokens
|
|
60
|
+
on every merge, so it's **disabled by default**. Set `"uat_agent": true` at the
|
|
61
|
+
top level of `fizzy.json` to opt back in globally. The card still moves to UAT
|
|
62
|
+
and gets the PR-merged comment either way — only the agent dispatch is gated.
|
|
63
|
+
|
|
64
|
+
**Per-card opt-in:** Even with `uat_agent` off globally, you can trigger the UAT
|
|
65
|
+
agent for a single card by adding the `uat` tag to it. When the card's PR merges,
|
|
66
|
+
Brainiac fetches the card's live tags and dispatches the UAT agent if the tag is
|
|
67
|
+
present. Customize the tag name with `"uat_agent_tag": "your-tag"`, or set it to
|
|
68
|
+
`false`/`null` to disable per-card overrides entirely. This gives you three
|
|
69
|
+
states:
|
|
70
|
+
|
|
71
|
+
- `uat_agent: true` — dispatch on every merge
|
|
72
|
+
- `uat_agent: false` (default) + `uat` tag on a card — dispatch for that card only
|
|
73
|
+
- `uat_agent: false` + no tag — never dispatch (pure bookkeeping, zero tokens)
|
|
74
|
+
|
|
54
75
|
## What This Plugin Handles
|
|
55
76
|
|
|
56
77
|
| Event | Action |
|
|
@@ -82,6 +82,26 @@ module Brainiac
|
|
|
82
82
|
nil
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
# Whether to dispatch an LLM agent to write UAT testing steps when a
|
|
86
|
+
# PR is merged and the card moves to UAT. Disabled by default to save
|
|
87
|
+
# tokens — set "uat_agent": true in ~/.brainiac/fizzy.json to enable
|
|
88
|
+
# globally. Individual cards can still opt in via the UAT tag even when
|
|
89
|
+
# this is false (see #uat_agent_tag).
|
|
90
|
+
def uat_agent_enabled?
|
|
91
|
+
@config.fetch("uat_agent", false) == true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The card tag that opts a single card into UAT agent dispatch even
|
|
95
|
+
# when uat_agent is globally disabled. Defaults to "uat". Set
|
|
96
|
+
# "uat_agent_tag": "some-tag" in fizzy.json to customize, or false/null
|
|
97
|
+
# to disable per-card overrides entirely.
|
|
98
|
+
def uat_agent_tag
|
|
99
|
+
value = @config.fetch("uat_agent_tag", "uat")
|
|
100
|
+
return nil if value == false || value.nil? || value.to_s.strip.empty?
|
|
101
|
+
|
|
102
|
+
value.to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
85
105
|
def authorized?(payload)
|
|
86
106
|
creator_id = payload.dig("creator", "id")
|
|
87
107
|
@authorized_user_ids.include?(creator_id)
|
|
@@ -274,3 +274,112 @@ def resolve_deployment_url(env_config, card_tags)
|
|
|
274
274
|
end
|
|
275
275
|
env_config["url"]
|
|
276
276
|
end
|
|
277
|
+
|
|
278
|
+
# Resolve the live ephemeral deployment URL for a given card number.
|
|
279
|
+
# Returns { env:, url: } for the environment currently occupied by this card,
|
|
280
|
+
# or nil if the card isn't deployed anywhere (or deployments aren't configured).
|
|
281
|
+
#
|
|
282
|
+
# Resolution order (live state is the source of truth):
|
|
283
|
+
# 1. A tracked dev environment (dev01/dev02...) occupied by this card, from
|
|
284
|
+
# deployment_state.json.
|
|
285
|
+
# 2. The card's ephemeral Belt environment (fizzy-<card>), from
|
|
286
|
+
# ephemeral_envs.json, with the public URL derived from its terraform.tfvars.
|
|
287
|
+
def deployment_url_for_card(card_number)
|
|
288
|
+
return nil unless card_number
|
|
289
|
+
|
|
290
|
+
tracked_deployment_for_card(card_number) || ephemeral_deployment_for_card(card_number)
|
|
291
|
+
rescue StandardError => e
|
|
292
|
+
LOG.warn "[Fizzy:Deploy] Could not resolve deployment URL for card ##{card_number}: #{e.message}" if defined?(LOG)
|
|
293
|
+
nil
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# A tracked dev environment (dev01/dev02...) currently occupied by this card.
|
|
297
|
+
def tracked_deployment_for_card(card_number)
|
|
298
|
+
return nil unless defined?(DEPLOYMENTS_CONFIG)
|
|
299
|
+
|
|
300
|
+
config = DEPLOYMENTS_CONFIG["environments"] || {}
|
|
301
|
+
state = load_deployment_state
|
|
302
|
+
|
|
303
|
+
env_key, info = state.find do |key, v|
|
|
304
|
+
v.is_a?(Hash) && v["card_number"].to_s == card_number.to_s && v["status"] == "occupied" && config.key?(key)
|
|
305
|
+
end
|
|
306
|
+
return nil unless env_key
|
|
307
|
+
|
|
308
|
+
url = resolve_deployment_url(config[env_key], info["card_tags"])
|
|
309
|
+
return nil unless url
|
|
310
|
+
|
|
311
|
+
{ env: env_key, url: url }
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
EPHEMERAL_ENVS_FILE = File.join(BRAINIAC_DIR, "ephemeral_envs.json")
|
|
315
|
+
|
|
316
|
+
# The active ephemeral Belt environment (fizzy-<card>) for this card, if any.
|
|
317
|
+
# The public URL is derived from the env's terraform.tfvars in its worktree,
|
|
318
|
+
# following the Belt frontend_urls convention.
|
|
319
|
+
def ephemeral_deployment_for_card(card_number)
|
|
320
|
+
info = active_ephemeral_env_info(card_number)
|
|
321
|
+
return nil unless info
|
|
322
|
+
|
|
323
|
+
worktree = info["worktree"]
|
|
324
|
+
env_name = "fizzy-#{card_number}"
|
|
325
|
+
return nil unless worktree
|
|
326
|
+
|
|
327
|
+
tfvars = File.join(worktree, "infrastructure", env_name, "terraform.tfvars")
|
|
328
|
+
return nil unless File.exist?(tfvars)
|
|
329
|
+
|
|
330
|
+
url = ephemeral_url_from_tfvars(tfvars)
|
|
331
|
+
return nil unless url
|
|
332
|
+
|
|
333
|
+
{ env: env_name, url: url }
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Look up the active ephemeral env metadata for a card from ephemeral_envs.json.
|
|
337
|
+
def active_ephemeral_env_info(card_number)
|
|
338
|
+
return nil unless File.exist?(EPHEMERAL_ENVS_FILE)
|
|
339
|
+
|
|
340
|
+
state = JSON.parse(File.read(EPHEMERAL_ENVS_FILE))
|
|
341
|
+
info = state["fizzy-#{card_number}"]
|
|
342
|
+
return nil unless info.is_a?(Hash) && info["status"] == "active"
|
|
343
|
+
|
|
344
|
+
info
|
|
345
|
+
rescue JSON::ParserError
|
|
346
|
+
nil
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Derive the public URL from a Belt env's terraform.tfvars, matching the
|
|
350
|
+
# frontend_urls convention in the Belt infra module:
|
|
351
|
+
# parent set -> https://<env>.<parent>.<domain>
|
|
352
|
+
# prod -> https://<domain>
|
|
353
|
+
# otherwise -> https://<env>.<domain>
|
|
354
|
+
def ephemeral_url_from_tfvars(tfvars_path)
|
|
355
|
+
vars = parse_tfvars(File.read(tfvars_path))
|
|
356
|
+
domain = vars["domain"].to_s
|
|
357
|
+
return nil if domain.empty?
|
|
358
|
+
|
|
359
|
+
environment = vars["environment"].to_s
|
|
360
|
+
parent = vars["parent_environment"].to_s
|
|
361
|
+
|
|
362
|
+
host =
|
|
363
|
+
if !parent.empty?
|
|
364
|
+
"#{environment}.#{parent}.#{domain}"
|
|
365
|
+
elsif environment == "prod"
|
|
366
|
+
domain
|
|
367
|
+
else
|
|
368
|
+
"#{environment}.#{domain}"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
"https://#{host}"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Minimal HCL parser for `key = "value"` lines in terraform.tfvars.
|
|
375
|
+
def parse_tfvars(contents)
|
|
376
|
+
vars = {}
|
|
377
|
+
contents.each_line do |line|
|
|
378
|
+
stripped = line.strip
|
|
379
|
+
next if stripped.empty? || stripped.start_with?("#")
|
|
380
|
+
|
|
381
|
+
match = stripped.match(/\A(\w+)\s*=\s*"([^"]*)"/)
|
|
382
|
+
vars[match[1]] = match[2] if match
|
|
383
|
+
end
|
|
384
|
+
vars
|
|
385
|
+
end
|
|
@@ -72,9 +72,14 @@ module Brainiac
|
|
|
72
72
|
env = Helpers.fizzy_env_for(agent_name)
|
|
73
73
|
|
|
74
74
|
pr_url = Helpers.send(:detect_pr_url, branch, project_config)
|
|
75
|
+
deployment = respond_to?(:deployment_url_for_card, true) ? deployment_url_for_card(card_number) : nil
|
|
75
76
|
body = "<p>✅ Work completed on this card.</p>"
|
|
76
|
-
body += "<p><a href=\"#{pr_url}\">View PR</a></p>" if pr_url
|
|
77
77
|
body += "<p><strong>Branch:</strong> <code>#{branch}</code></p>"
|
|
78
|
+
body += "<p><strong>PR:</strong> <a href=\"#{pr_url}\">#{pr_url}</a></p>" if pr_url
|
|
79
|
+
if deployment
|
|
80
|
+
body += "<p><strong>Deployment (#{deployment[:env]}):</strong> " \
|
|
81
|
+
"<a href=\"#{deployment[:url]}\">#{deployment[:url]}</a></p>"
|
|
82
|
+
end
|
|
78
83
|
|
|
79
84
|
run_cmd("fizzy", "comment", "create", "--card", card_number.to_s, "--body", body,
|
|
80
85
|
chdir: repo_path, env: env)
|
|
@@ -171,16 +171,13 @@ module Brainiac
|
|
|
171
171
|
last_agent_comment = agent_comments.last
|
|
172
172
|
return false unless last_agent_comment
|
|
173
173
|
|
|
174
|
-
#
|
|
174
|
+
# Backup: ensure the comment surfaces branch, PR link, and live deployment link.
|
|
175
|
+
# Whatever the agent already included is left alone; we only append what's missing.
|
|
175
176
|
body = last_agent_comment.dig("body", "html") || ""
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
updated_body = body + footer
|
|
181
|
-
run_cmd("fizzy", "comment", "update", last_agent_comment["id"], "--card", card_number.to_s,
|
|
182
|
-
"--body", updated_body, chdir: repo_path, env: env)
|
|
183
|
-
end
|
|
177
|
+
updated_body = ensure_reference_footer(body, card_number, project_config)
|
|
178
|
+
if updated_body != body
|
|
179
|
+
run_cmd("fizzy", "comment", "update", last_agent_comment["id"], "--card", card_number.to_s,
|
|
180
|
+
"--body", updated_body, chdir: repo_path, env: env)
|
|
184
181
|
end
|
|
185
182
|
|
|
186
183
|
true
|
|
@@ -189,6 +186,42 @@ module Brainiac
|
|
|
189
186
|
false
|
|
190
187
|
end
|
|
191
188
|
|
|
189
|
+
# Build the reference footer (Branch / PR / Deployment) and append any pieces
|
|
190
|
+
# that the agent's comment body doesn't already contain. Returns the (possibly
|
|
191
|
+
# unchanged) body. Detection is deliberately lenient — if the branch name, PR
|
|
192
|
+
# URL, or deployment URL already appears anywhere in the body, it's not re-added.
|
|
193
|
+
def ensure_reference_footer(body, card_number, project_config)
|
|
194
|
+
branch = detect_branch_from_comment(body, card_number)
|
|
195
|
+
return body unless branch
|
|
196
|
+
|
|
197
|
+
pr_url = detect_pr_url(branch, project_config)
|
|
198
|
+
deployment = detect_deployment(card_number)
|
|
199
|
+
|
|
200
|
+
footer_lines = []
|
|
201
|
+
footer_lines << "<strong>Branch:</strong> <code>#{branch}</code>" unless body.include?(branch)
|
|
202
|
+
if pr_url && !body.include?(pr_url) && !body.include?("github.com/#{project_config["github_repo"]}/pull")
|
|
203
|
+
footer_lines << "<strong>PR:</strong> <a href=\"#{pr_url}\">#{pr_url}</a>"
|
|
204
|
+
end
|
|
205
|
+
if deployment && !body.include?(deployment[:url])
|
|
206
|
+
footer_lines << "<strong>Deployment (#{deployment[:env]}):</strong> " \
|
|
207
|
+
"<a href=\"#{deployment[:url]}\">#{deployment[:url]}</a>"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
return body if footer_lines.empty?
|
|
211
|
+
|
|
212
|
+
"#{body}<p><em>#{footer_lines.join(" · ")}</em></p>"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Resolve the live deployment for a card as { env:, url: }, or nil.
|
|
216
|
+
# Reads live deployment state (tracked dev envs from deployment_state.json,
|
|
217
|
+
# and the card's ephemeral Belt env from ephemeral_envs.json) — the actual
|
|
218
|
+
# source of truth for which environment a card currently occupies.
|
|
219
|
+
def detect_deployment(card_number)
|
|
220
|
+
return nil unless respond_to?(:deployment_url_for_card, true)
|
|
221
|
+
|
|
222
|
+
deployment_url_for_card(card_number)
|
|
223
|
+
end
|
|
224
|
+
|
|
192
225
|
def ensure_fizzy_yaml!(chdir, project_config)
|
|
193
226
|
fizzy_yaml_dest = File.join(chdir, ".fizzy.yaml")
|
|
194
227
|
return if File.exist?(fizzy_yaml_dest)
|
|
@@ -345,33 +378,6 @@ module Brainiac
|
|
|
345
378
|
rescue StandardError
|
|
346
379
|
nil
|
|
347
380
|
end
|
|
348
|
-
|
|
349
|
-
# True if the comment body already carries a "Branch:" footer/label, in
|
|
350
|
-
# either the footer format (`<em>Branch:`) or the agent-authored format
|
|
351
|
-
# (`<strong>Branch:</strong>`). Prevents appending a duplicate footer.
|
|
352
|
-
def footer_already_present?(body)
|
|
353
|
-
body.include?("<em>Branch:") ||
|
|
354
|
-
body.match?(%r{<strong>\s*Branch:\s*</strong>}i) ||
|
|
355
|
-
body.match?(/(^|>)\s*Branch:\s*</)
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
# Build the "<em>Branch: <code>...</code> | PR | Env</em>" footer.
|
|
359
|
-
# Includes the ephemeral env link when the card has an active env.
|
|
360
|
-
def build_comment_footer(branch, card_number, project_config)
|
|
361
|
-
pr_url = detect_pr_url(branch, project_config)
|
|
362
|
-
env_url = detect_env_url(card_number)
|
|
363
|
-
|
|
364
|
-
footer = "<p><em>Branch: <code>#{branch}</code>"
|
|
365
|
-
footer += " | <a href=\"#{pr_url}\">PR</a>" if pr_url
|
|
366
|
-
footer += " | <a href=\"#{env_url}\">Env</a>" if env_url
|
|
367
|
-
footer += "</em></p>"
|
|
368
|
-
footer
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
# URL of the ephemeral Belt environment for this card, if one is active.
|
|
372
|
-
def detect_env_url(card_number)
|
|
373
|
-
EnvUrl.for_card(card_number)
|
|
374
|
-
end
|
|
375
381
|
end
|
|
376
382
|
end
|
|
377
383
|
end
|
|
@@ -134,36 +134,7 @@ module Brainiac
|
|
|
134
134
|
# PR merged — post comment on Fizzy card, move to UAT, dispatch UAT agent
|
|
135
135
|
def register_pr_merged
|
|
136
136
|
Brainiac.on(:pr_merged) do |ctx|
|
|
137
|
-
|
|
138
|
-
next unless card_number
|
|
139
|
-
|
|
140
|
-
card_info = ctx[:card_info]
|
|
141
|
-
card_agent = card_info["agent"]
|
|
142
|
-
env = Helpers.fizzy_env_for(card_agent)
|
|
143
|
-
repo_path = ctx[:repo_path]
|
|
144
|
-
board_key = card_info.dig("sources", "fizzy", "board_key") || Config.board_key_for_project(ctx[:project_config])
|
|
145
|
-
|
|
146
|
-
# Post PR link comment
|
|
147
|
-
pr_url = ctx[:pr_url]
|
|
148
|
-
pr_title = ctx[:pr_title]
|
|
149
|
-
branch = ctx[:branch]
|
|
150
|
-
comment_body = "<p>PR merged into main: <a href=\"#{pr_url}\">#{pr_title}</a></p>" \
|
|
151
|
-
"<p>Branch: <code>#{branch}</code></p>"
|
|
152
|
-
run_cmd("fizzy", "comment", "create", "--card", card_number.to_s, "--body", comment_body,
|
|
153
|
-
chdir: repo_path, env: env)
|
|
154
|
-
|
|
155
|
-
# Move card to UAT
|
|
156
|
-
Helpers.move_card_to_column(card_number, "uat",
|
|
157
|
-
project_config: ctx[:project_config],
|
|
158
|
-
agent_name: card_agent,
|
|
159
|
-
board_key: board_key)
|
|
160
|
-
record_self_move(card_number)
|
|
161
|
-
|
|
162
|
-
# Clear deployment tracking
|
|
163
|
-
clear_deployment_for_card(card_number) if respond_to?(:clear_deployment_for_card)
|
|
164
|
-
|
|
165
|
-
# Dispatch UAT agent
|
|
166
|
-
dispatch_fizzy_uat_agent(ctx)
|
|
137
|
+
handle_pr_merged(ctx)
|
|
167
138
|
rescue StandardError => e
|
|
168
139
|
LOG.error "[Fizzy] Error in pr_merged hook: #{e.message}" if defined?(LOG)
|
|
169
140
|
end
|
|
@@ -292,6 +263,75 @@ module Brainiac
|
|
|
292
263
|
|
|
293
264
|
# --- Private helpers ---
|
|
294
265
|
|
|
266
|
+
# Body of the :pr_merged hook. Posts the PR link comment, moves the
|
|
267
|
+
# card to UAT, clears deployment tracking, and dispatches the UAT
|
|
268
|
+
# agent when enabled. Extracted from the hook block to keep the
|
|
269
|
+
# registration method within complexity limits.
|
|
270
|
+
def handle_pr_merged(ctx)
|
|
271
|
+
card_number = ctx[:card_number]
|
|
272
|
+
return unless card_number
|
|
273
|
+
|
|
274
|
+
card_info = ctx[:card_info]
|
|
275
|
+
card_agent = card_info["agent"]
|
|
276
|
+
env = Helpers.fizzy_env_for(card_agent)
|
|
277
|
+
repo_path = ctx[:repo_path]
|
|
278
|
+
board_key = card_info.dig("sources", "fizzy", "board_key") || Config.board_key_for_project(ctx[:project_config])
|
|
279
|
+
|
|
280
|
+
# Post PR link comment
|
|
281
|
+
comment_body = "<p>PR merged into main: <a href=\"#{ctx[:pr_url]}\">#{ctx[:pr_title]}</a></p>" \
|
|
282
|
+
"<p>Branch: <code>#{ctx[:branch]}</code></p>"
|
|
283
|
+
run_cmd("fizzy", "comment", "create", "--card", card_number.to_s, "--body", comment_body,
|
|
284
|
+
chdir: repo_path, env: env)
|
|
285
|
+
|
|
286
|
+
# Move card to UAT
|
|
287
|
+
Helpers.move_card_to_column(card_number, "uat",
|
|
288
|
+
project_config: ctx[:project_config],
|
|
289
|
+
agent_name: card_agent,
|
|
290
|
+
board_key: board_key)
|
|
291
|
+
record_self_move(card_number)
|
|
292
|
+
|
|
293
|
+
# Clear deployment tracking
|
|
294
|
+
clear_deployment_for_card(card_number) if respond_to?(:clear_deployment_for_card)
|
|
295
|
+
|
|
296
|
+
# Dispatch UAT agent — off by default to save tokens. Globally
|
|
297
|
+
# enabled via "uat_agent": true, or per-card via the UAT tag
|
|
298
|
+
# (default "uat") even when the global flag is off.
|
|
299
|
+
if uat_agent_dispatch?(card_number, repo_path: repo_path, env: env)
|
|
300
|
+
dispatch_fizzy_uat_agent(ctx)
|
|
301
|
+
else
|
|
302
|
+
log_uat_dispatch_skipped(card_number)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Log that UAT dispatch was skipped for a card (global flag off, no tag).
|
|
307
|
+
def log_uat_dispatch_skipped(card_number)
|
|
308
|
+
return unless defined?(LOG)
|
|
309
|
+
|
|
310
|
+
LOG.info "[Fizzy] UAT agent disabled (set \"uat_agent\": true in fizzy.json, or add the " \
|
|
311
|
+
"\"#{Config.uat_agent_tag}\" tag to this card) — skipping dispatch for card ##{card_number}"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Decide whether to dispatch the UAT agent for a merged card.
|
|
315
|
+
# True when globally enabled, OR when the card carries the configured
|
|
316
|
+
# UAT tag (per-card opt-in). Card tags aren't in the pr_merged ctx, so
|
|
317
|
+
# we fetch them live — only when the global flag is off, to avoid an
|
|
318
|
+
# extra API call when it's already enabled.
|
|
319
|
+
def uat_agent_dispatch?(card_number, repo_path:, env: nil)
|
|
320
|
+
return true if Config.uat_agent_enabled?
|
|
321
|
+
|
|
322
|
+
tag = Config.uat_agent_tag
|
|
323
|
+
return false unless tag && card_number && repo_path
|
|
324
|
+
|
|
325
|
+
tags = Helpers.fetch_card_tags(card_number, repo_path: repo_path, env: env)
|
|
326
|
+
return false unless tags
|
|
327
|
+
|
|
328
|
+
has_tag = Helpers.card_has_tag?(tags, tag)
|
|
329
|
+
if has_tag && defined?(LOG)
|
|
330
|
+
LOG.info "[Fizzy] Card ##{card_number} has \"#{tag}\" tag — dispatching UAT agent despite global flag being off"
|
|
331
|
+
end
|
|
332
|
+
has_tag
|
|
333
|
+
end
|
|
334
|
+
|
|
295
335
|
def dispatch_fizzy_uat_agent(ctx)
|
|
296
336
|
card_number = ctx[:card_number]
|
|
297
337
|
card_info = ctx[:card_info]
|
|
@@ -62,10 +62,19 @@ module Brainiac
|
|
|
62
62
|
{{PR_TARGET_INSTRUCTION}}
|
|
63
63
|
|
|
64
64
|
**Response destination: Post your response as a comment on Fizzy card #{{CARD_NUMBER}}.**
|
|
65
|
-
Your comment MUST include a concise summary of what you did
|
|
65
|
+
Your comment MUST include a concise summary of what you did.
|
|
66
66
|
|
|
67
|
-
**MANDATORY: Always
|
|
68
|
-
|
|
67
|
+
**MANDATORY: Always end your comment with a References block containing all three of the following:**
|
|
68
|
+
- The branch name
|
|
69
|
+
- A link to the PR
|
|
70
|
+
- A link to the live deployed ephemeral environment (if this card was deployed — check the card's [deploy] tag / deployment; if not deployed, state "Not deployed")
|
|
71
|
+
|
|
72
|
+
Use this exact HTML format:
|
|
73
|
+
```
|
|
74
|
+
<p><strong>Branch:</strong> <code>{{BRANCH}}</code></p>
|
|
75
|
+
<p><strong>PR:</strong> <a href="PR_URL_HERE">PR_URL_HERE</a></p>
|
|
76
|
+
<p><strong>Deployment:</strong> <a href="DEPLOY_URL_HERE">DEPLOY_URL_HERE</a></p>
|
|
77
|
+
```
|
|
69
78
|
PROMPT
|
|
70
79
|
|
|
71
80
|
FOLLOWUP_WORKTREE = <<~'PROMPT'
|
|
@@ -123,11 +132,19 @@ module Brainiac
|
|
|
123
132
|
Read your memory file for this card and write a brief summary comment.
|
|
124
133
|
|
|
125
134
|
**Response destination: Post your response as a comment on Fizzy card #{{CARD_NUMBER}}.**
|
|
126
|
-
Include what you did
|
|
127
|
-
Keep it concise — this is a summary, not a full report.
|
|
135
|
+
Include what you did. Keep it concise — this is a summary, not a full report.
|
|
128
136
|
|
|
129
|
-
**MANDATORY: Always
|
|
130
|
-
|
|
137
|
+
**MANDATORY: Always end your comment with all three of the following:**
|
|
138
|
+
- The branch name
|
|
139
|
+
- A link to the PR
|
|
140
|
+
- A link to the live deployed ephemeral environment (or "Not deployed" if the card wasn't deployed)
|
|
141
|
+
|
|
142
|
+
Use this exact HTML format:
|
|
143
|
+
```
|
|
144
|
+
<p><strong>Branch:</strong> <code>{{BRANCH}}</code></p>
|
|
145
|
+
<p><strong>PR:</strong> <a href="PR_URL_HERE">PR_URL_HERE</a></p>
|
|
146
|
+
<p><strong>Deployment:</strong> <a href="DEPLOY_URL_HERE">DEPLOY_URL_HERE</a></p>
|
|
147
|
+
```
|
|
131
148
|
PROMPT
|
|
132
149
|
|
|
133
150
|
UAT_TESTING = <<~'PROMPT'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brainiac-fizzy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
@@ -92,7 +92,6 @@ files:
|
|
|
92
92
|
- lib/brainiac/plugins/fizzy/cli.rb
|
|
93
93
|
- lib/brainiac/plugins/fizzy/config.rb
|
|
94
94
|
- lib/brainiac/plugins/fizzy/delegators.rb
|
|
95
|
-
- lib/brainiac/plugins/fizzy/env_url.rb
|
|
96
95
|
- lib/brainiac/plugins/fizzy/handlers/assignment.rb
|
|
97
96
|
- lib/brainiac/plugins/fizzy/handlers/card_index.rb
|
|
98
97
|
- lib/brainiac/plugins/fizzy/handlers/comments.rb
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Brainiac
|
|
4
|
-
module Plugins
|
|
5
|
-
module Fizzy
|
|
6
|
-
# Derives the public URL of an ephemeral Belt environment from its
|
|
7
|
-
# terraform.tfvars. Kept separate from Helpers so the pure URL/HCL logic
|
|
8
|
-
# stays small and independently testable.
|
|
9
|
-
module EnvUrl
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
# URL of the ephemeral Belt environment for a card, if one is active.
|
|
13
|
-
# The env name is `fizzy-<card_number>`; the public URL is derived from the
|
|
14
|
-
# env's terraform.tfvars, matching the frontend_urls convention in the Belt
|
|
15
|
-
# infra module:
|
|
16
|
-
# parent set -> https://<env>.<parent>.<domain>
|
|
17
|
-
# prod -> https://<domain>
|
|
18
|
-
# otherwise -> https://<env>.<domain>
|
|
19
|
-
def for_card(card_number)
|
|
20
|
-
return nil unless defined?(BeltConfig)
|
|
21
|
-
|
|
22
|
-
env_name = BeltConfig.ephemeral_env_for_card(card_number)
|
|
23
|
-
return nil unless BeltConfig.ephemeral_env?(env_name)
|
|
24
|
-
|
|
25
|
-
info = BeltConfig.respond_to?(:ephemeral_env_info) ? BeltConfig.ephemeral_env_info(env_name) : nil
|
|
26
|
-
worktree = info && info["worktree"]
|
|
27
|
-
return nil unless worktree
|
|
28
|
-
|
|
29
|
-
tfvars = File.join(worktree, "infrastructure", env_name.to_s, "terraform.tfvars")
|
|
30
|
-
return nil unless File.exist?(tfvars)
|
|
31
|
-
|
|
32
|
-
from_tfvars(tfvars)
|
|
33
|
-
rescue StandardError => e
|
|
34
|
-
LOG.warn "[Fizzy] Could not detect env URL for card ##{card_number}: #{e.message}" if defined?(LOG)
|
|
35
|
-
nil
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def from_tfvars(tfvars_path)
|
|
39
|
-
vars = parse(File.read(tfvars_path))
|
|
40
|
-
domain = vars["domain"].to_s
|
|
41
|
-
return nil if domain.empty?
|
|
42
|
-
|
|
43
|
-
environment = vars["environment"].to_s
|
|
44
|
-
parent = vars["parent_environment"].to_s
|
|
45
|
-
|
|
46
|
-
host =
|
|
47
|
-
if !parent.empty?
|
|
48
|
-
"#{environment}.#{parent}.#{domain}"
|
|
49
|
-
elsif environment == "prod"
|
|
50
|
-
domain
|
|
51
|
-
else
|
|
52
|
-
"#{environment}.#{domain}"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
"https://#{host}"
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Minimal HCL parser for `key = "value"` lines in terraform.tfvars.
|
|
59
|
-
def parse(contents)
|
|
60
|
-
vars = {}
|
|
61
|
-
contents.each_line do |line|
|
|
62
|
-
stripped = line.strip
|
|
63
|
-
next if stripped.empty? || stripped.start_with?("#")
|
|
64
|
-
|
|
65
|
-
match = stripped.match(/\A(\w+)\s*=\s*"([^"]*)"/)
|
|
66
|
-
vars[match[1]] = match[2] if match
|
|
67
|
-
end
|
|
68
|
-
vars
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|