brainiac-fizzy 0.0.26 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b9d6e5b3e6e6ea9306dc6fd9ce800dffc01e46f99b05f2861e0acf096793fe5
4
- data.tar.gz: ec15a0b6f7a498de7662b8bd662fb519ae45f8a14d66e5912cf6dd540cc3726b
3
+ metadata.gz: e5d55859eafd2aeccf63a2083999340a1ce4a77c01b81a8297adf136b0e553b3
4
+ data.tar.gz: b1dbff19165dec2cf391f4c258ae2a207457cdc53088ee44062ffda050530a48
5
5
  SHA512:
6
- metadata.gz: 51284c7c4cca09f1bde28d3589fc7c17b29314e898f4de8bd7345fa1fd930baf6501ef47e42a241b40f17baede635780676154e7c6f456a70ea6f1d9902da59b
7
- data.tar.gz: 868fc4f7e922d67138125385c486f87bc221766ddadf45e640ad880ee57914f04a772bdd3b688087c505a66cfa59f7643e1134f5731eb0ef67f3e42b6f80a700
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,20 +171,13 @@ module Brainiac
171
171
  last_agent_comment = agent_comments.last
172
172
  return false unless last_agent_comment
173
173
 
174
- # Append footer if applicable
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
- unless body.include?("<em>Branch:")
177
- branch = detect_branch_from_comment(body, card_number)
178
- if branch
179
- pr_url = detect_pr_url(branch, project_config)
180
- footer = "<p><em>Branch: <code>#{branch}</code>"
181
- footer += " | <a href=\"#{pr_url}\">PR</a>" if pr_url
182
- footer += "</em></p>"
183
-
184
- updated_body = body + footer
185
- run_cmd("fizzy", "comment", "update", last_agent_comment["id"], "--card", card_number.to_s,
186
- "--body", updated_body, chdir: repo_path, env: env)
187
- 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)
188
181
  end
189
182
 
190
183
  true
@@ -193,6 +186,42 @@ module Brainiac
193
186
  false
194
187
  end
195
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(" &middot; ")}</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
+
196
225
  def ensure_fizzy_yaml!(chdir, project_config)
197
226
  fizzy_yaml_dest = File.join(chdir, ".fizzy.yaml")
198
227
  return if File.exist?(fizzy_yaml_dest)
@@ -331,8 +360,24 @@ module Brainiac
331
360
  repo = project_config["github_repo"]
332
361
  return nil unless repo
333
362
 
363
+ # Prefer the existing open PR for this branch. `pull/new/<branch>` only
364
+ # opens the "create PR" page, which is wrong once a PR already exists.
365
+ existing = existing_pr_url(branch, repo, project_config["repo_path"])
366
+ return existing if existing
367
+
334
368
  "https://github.com/#{repo}/pull/new/#{branch}"
335
369
  end
370
+
371
+ # Look up the URL of an already-open PR for `branch` via the gh CLI.
372
+ # Returns nil if gh fails or no PR exists (caller falls back to new-PR link).
373
+ def existing_pr_url(branch, repo, repo_path)
374
+ output = run_cmd("gh", "pr", "view", branch, "--repo", repo, "--json", "url",
375
+ "--jq", ".url", chdir: repo_path)
376
+ url = output.to_s.strip
377
+ url.empty? ? nil : url
378
+ rescue StandardError
379
+ nil
380
+ end
336
381
  end
337
382
  end
338
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, a PR link, and the branch name.
65
+ Your comment MUST include a concise summary of what you did.
66
66
 
67
- **MANDATORY: Always include the branch name in your comment.** Use this format:
68
- `<p><strong>Branch:</strong> <code>{{BRANCH}}</code></p>`
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, any PR link, and the branch name.
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 include the branch name in your comment.** Use this format:
130
- `<p><strong>Branch:</strong> <code>{{BRANCH}}</code></p>`
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'
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Fizzy
6
- VERSION = "0.0.26"
6
+ VERSION = "0.0.28"
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-fizzy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.26
4
+ version: 0.0.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis