brainiac-fizzy 0.0.27 → 0.0.28
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/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/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: e5d55859eafd2aeccf63a2083999340a1ce4a77c01b81a8297adf136b0e553b3
|
|
4
|
+
data.tar.gz: b1dbff19165dec2cf391f4c258ae2a207457cdc53088ee44062ffda050530a48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9438b9143636ab612da09a19be5b672ada809095317ea7ce72d9045acbebe6601d5485ba4bfd234b85edcdb54a6d676edd330a74b4241cc85c0605d590fe523d
|
|
7
|
+
data.tar.gz: 93fbe26b2a546b1b72e1025f7af334ac6539efe0c4144338bd6d2929b336e2ed21752cc8a1d4ef2949d380cc72635e945e0dad819afe043570c483ddc889d2d7
|
|
@@ -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
|
|
@@ -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.28
|
|
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
|