@@ -19,6 +19,8 @@ import (
1919
2020 "github.com/gin-gonic/gin"
2121 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+ "k8s.io/client-go/dynamic"
23+ "k8s.io/client-go/kubernetes"
2224)
2325
2426// SyncProjectBugFixWorkflowToJira handles POST /api/projects/:projectName/bugfix-workflows/:id/sync-jira
@@ -106,6 +108,9 @@ func SyncProjectBugFixWorkflowToJira(c *gin.Context) {
106108 }
107109 } else {
108110 jiraTaskURL = fmt .Sprintf ("%s/browse/%s" , strings .TrimRight (jiraURL , "/" ), jiraTaskKey )
111+ // Refresh attachments for updates (in case new Gists were added)
112+ jiraBase := strings .TrimRight (jiraURL , "/" )
113+ attachGistsToJira (c , workflow , jiraBase , jiraTaskKey , authHeader , reqK8s , reqDyn , project )
109114 }
110115 }
111116
@@ -160,7 +165,8 @@ func SyncProjectBugFixWorkflowToJira(c *gin.Context) {
160165 // T056: Handle various HTTP status codes properly
161166 switch resp .StatusCode {
162167 case 201 :
163- // Success
168+ // Success - continue to parse response
169+ fmt .Printf ("Jira API success (201), response body length: %d bytes\n " , len (body ))
164170 case 401 , 403 :
165171 websocket .BroadcastBugFixJiraSyncFailed (workflowID , workflow .GithubIssueNumber , "Jira authentication failed" )
166172 c .JSON (http .StatusUnauthorized , gin.H {"error" : "Jira authentication failed" , "details" : string (body )})
@@ -175,10 +181,19 @@ func SyncProjectBugFixWorkflowToJira(c *gin.Context) {
175181 return
176182 }
177183
184+ // Parse JSON response
178185 var result map [string ]interface {}
179186 if err := json .Unmarshal (body , & result ); err != nil {
187+ // Log the raw response for debugging
188+ fmt .Printf ("ERROR: Failed to parse Jira response as JSON: %v\n " , err )
189+ bodyLen := len (body )
190+ fmt .Printf ("Response body (first 500 chars): %s\n " , string (body [:min (500 , bodyLen )]))
180191 websocket .BroadcastBugFixJiraSyncFailed (workflowID , workflow .GithubIssueNumber , "Invalid Jira response" )
181- c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to parse Jira response" , "details" : err .Error ()})
192+ c .JSON (http .StatusInternalServerError , gin.H {
193+ "error" : "Failed to parse Jira response" ,
194+ "details" : err .Error (),
195+ "responsePreview" : string (body [:min (200 , bodyLen )]),
196+ })
182197 return
183198 }
184199
@@ -199,19 +214,45 @@ func SyncProjectBugFixWorkflowToJira(c *gin.Context) {
199214 fmt .Printf ("Warning: Failed to create Jira remote link: %v\n " , err )
200215 }
201216
217+ // Attach Gist markdown files to Jira issue
218+ attachGistsToJira (c , workflow , jiraBase , jiraTaskKey , authHeader , reqK8s , reqDyn , project )
219+
202220 // T054: Add comment to GitHub Issue with Jira link
203221 userID , _ := c .Get ("userID" )
204222 userIDStr , _ := userID .(string )
205223 githubToken , err := git .GetGitHubToken (c .Request .Context (), reqK8s , reqDyn , project , userIDStr )
206224 if err == nil && githubToken != "" {
207225 owner , repo , issueNumber , err := github .ParseGitHubIssueURL (workflow .GithubIssueURL )
208226 if err == nil {
209- comment := formatGitHubJiraLinkComment (jiraTaskKey , jiraTaskURL )
227+ comment := formatGitHubJiraLinkComment (jiraTaskKey , jiraTaskURL , workflow )
210228 ctx := context .Background ()
211229 _ , err = github .AddComment (ctx , owner , repo , issueNumber , githubToken , comment )
212230 if err != nil {
213231 // Non-fatal: Log but continue
214232 fmt .Printf ("Warning: Failed to add Jira link to GitHub Issue: %v\n " , err )
233+ } else {
234+ fmt .Printf ("Posted Jira link comment to GitHub Issue #%d\n " , issueNumber )
235+ }
236+ }
237+ }
238+ }
239+
240+ // Also post GitHub comment for updates (not just creates)
241+ if isUpdate {
242+ userID , _ := c .Get ("userID" )
243+ userIDStr , _ := userID .(string )
244+ githubToken , err := git .GetGitHubToken (c .Request .Context (), reqK8s , reqDyn , project , userIDStr )
245+ if err == nil && githubToken != "" {
246+ owner , repo , issueNumber , err := github .ParseGitHubIssueURL (workflow .GithubIssueURL )
247+ if err == nil {
248+ comment := formatGitHubJiraUpdateComment (jiraTaskKey , jiraTaskURL , workflow )
249+ ctx := context .Background ()
250+ _ , err = github .AddComment (ctx , owner , repo , issueNumber , githubToken , comment )
251+ if err != nil {
252+ // Non-fatal: Log but continue
253+ fmt .Printf ("Warning: Failed to add Jira update to GitHub Issue: %v\n " , err )
254+ } else {
255+ fmt .Printf ("Posted Jira update comment to GitHub Issue #%d\n " , issueNumber )
215256 }
216257 }
217258 }
@@ -244,34 +285,165 @@ func SyncProjectBugFixWorkflowToJira(c *gin.Context) {
244285}
245286
246287// buildJiraDescription builds the Jira issue description from the workflow
288+ // Includes comprehensive information and references to attached Gist files
247289func buildJiraDescription (workflow * types.BugFixWorkflow ) string {
248290 var desc strings.Builder
249291
250- desc .WriteString ("This issue is synchronized from GitHub Issue:\n " )
292+ // Header with source
293+ desc .WriteString ("h1. Bug Report\n \n " )
294+ desc .WriteString ("*Source:* [GitHub Issue #" )
295+ desc .WriteString (fmt .Sprintf ("%d" , workflow .GithubIssueNumber ))
296+ desc .WriteString ("|" )
251297 desc .WriteString (workflow .GithubIssueURL )
252- desc .WriteString ("\n \n " )
298+ desc .WriteString ("]\n \n " )
299+ desc .WriteString ("----\n \n " )
253300
301+ // Description
254302 if workflow .Description != "" {
255- desc .WriteString ("## Description\n " )
303+ desc .WriteString ("h2. Description\n \n " )
256304 desc .WriteString (workflow .Description )
257305 desc .WriteString ("\n \n " )
258306 }
259307
260- desc .WriteString ("## Details\n " )
261- desc .WriteString (fmt .Sprintf ("- GitHub Issue: #%d\n " , workflow .GithubIssueNumber ))
262- desc .WriteString (fmt .Sprintf ("- Branch: %s\n " , workflow .BranchName ))
263- desc .WriteString (fmt .Sprintf ("- Created: %s\n " , workflow .CreatedAt ))
308+ // Repository and branch information
309+ desc .WriteString ("h2. Repository Information\n \n " )
310+ desc .WriteString (fmt .Sprintf ("* *Repository:* %s\n " , workflow .ImplementationRepo .URL ))
311+ desc .WriteString (fmt .Sprintf ("* *Base Branch:* {{%s}}\n " , workflow .ImplementationRepo .Branch ))
312+ desc .WriteString (fmt .Sprintf ("* *Feature Branch:* {{%s}}\n " , workflow .BranchName ))
313+ desc .WriteString ("\n " )
314+
315+ // Status information
316+ desc .WriteString ("h2. Workflow Status\n \n " )
317+ desc .WriteString (fmt .Sprintf ("* *Created:* %s\n " , workflow .CreatedAt ))
318+ desc .WriteString (fmt .Sprintf ("* *Phase:* %s\n " , workflow .Phase ))
319+
320+ if workflow .AssessmentStatus != "" {
321+ desc .WriteString (fmt .Sprintf ("* *Assessment:* %s\n " , workflow .AssessmentStatus ))
322+ }
323+ if workflow .ImplementationCompleted {
324+ desc .WriteString ("* *Implementation:* {color:green}✓ Complete{color}\n " )
325+ } else {
326+ desc .WriteString ("* *Implementation:* Pending\n " )
327+ }
328+ desc .WriteString ("\n " )
329+
330+ // Analysis documents section
331+ hasGists := false
332+ if workflow .Annotations != nil {
333+ if bugReviewGist := workflow .Annotations ["bug-review-gist-url" ]; bugReviewGist != "" {
334+ hasGists = true
335+ }
336+ if implGist := workflow .Annotations ["implementation-gist-url" ]; implGist != "" {
337+ hasGists = true
338+ }
339+ }
340+
341+ if hasGists {
342+ desc .WriteString ("h2. Analysis Documents\n \n " )
343+ desc .WriteString ("_Detailed analysis reports are attached to this issue as markdown files. Original Gist links:_\n \n " )
344+
345+ if workflow .Annotations != nil {
346+ if bugReviewGist := workflow .Annotations ["bug-review-gist-url" ]; bugReviewGist != "" {
347+ desc .WriteString ("* *Bug Review & Assessment:* [bug-review.md attachment|" )
348+ desc .WriteString (bugReviewGist )
349+ desc .WriteString ("]\n " )
350+ }
351+ if implGist := workflow .Annotations ["implementation-gist-url" ]; implGist != "" {
352+ desc .WriteString ("* *Implementation Details:* [implementation.md attachment|" )
353+ desc .WriteString (implGist )
354+ desc .WriteString ("]\n " )
355+ }
356+ }
357+ desc .WriteString ("\n " )
358+ }
359+
360+ // PR information if available
361+ if workflow .Annotations != nil {
362+ if prURL := workflow .Annotations ["github-pr-url" ]; prURL != "" {
363+ prNumber := workflow .Annotations ["github-pr-number" ]
364+ prState := workflow .Annotations ["github-pr-state" ]
365+ desc .WriteString ("h2. Pull Request\n \n " )
366+ desc .WriteString (fmt .Sprintf ("* *PR:* [#%s|%s]\n " , prNumber , prURL ))
367+ desc .WriteString (fmt .Sprintf ("* *State:* %s\n " , prState ))
368+ desc .WriteString ("\n " )
369+ }
370+ }
264371
265- desc .WriteString ("\n ---\n " )
266- desc .WriteString ("*This issue is automatically synchronized from vTeam BugFix Workspace*\n " )
267- desc .WriteString ("*Note: Currently created as Feature Request. Will use proper Bug/Task type after Jira Cloud migration.*\n " )
372+ // Footer
373+ desc .WriteString ("----\n " )
374+ desc .WriteString ("_Synchronized from vTeam BugFix Workspace | [View in vTeam|" )
375+ desc .WriteString (workflow .GithubIssueURL )
376+ desc .WriteString ("]_\n " )
268377
269378 return desc .String ()
270379}
271380
272- // formatGitHubJiraLinkComment formats the comment to post on GitHub Issue
273- func formatGitHubJiraLinkComment (jiraTaskKey , jiraTaskURL string ) string {
274- return fmt .Sprintf ("## 🔗 Jira Task Created\n \n This bug has been synchronized to Jira:\n - **Task**: [%s](%s)\n \n *Synchronized by vTeam BugFix Workspace*" , jiraTaskKey , jiraTaskURL )
381+ // formatGitHubJiraLinkComment formats the comment to post on GitHub Issue when creating new Jira task
382+ func formatGitHubJiraLinkComment (jiraTaskKey , jiraTaskURL string , workflow * types.BugFixWorkflow ) string {
383+ var comment strings.Builder
384+
385+ comment .WriteString ("## 🔗 Jira Task Created\n \n " )
386+ comment .WriteString (fmt .Sprintf ("This bug has been synchronized to Jira: [**%s**](%s)\n \n " , jiraTaskKey , jiraTaskURL ))
387+
388+ // Add links to analysis documents if available
389+ if workflow .Annotations != nil {
390+ hasGists := false
391+ if bugReviewGist := workflow .Annotations ["bug-review-gist-url" ]; bugReviewGist != "" {
392+ if ! hasGists {
393+ comment .WriteString ("### 📄 Analysis Documents\n \n " )
394+ hasGists = true
395+ }
396+ comment .WriteString (fmt .Sprintf ("- [Bug Review & Assessment](%s)\n " , bugReviewGist ))
397+ }
398+ if implGist := workflow .Annotations ["implementation-gist-url" ]; implGist != "" {
399+ if ! hasGists {
400+ comment .WriteString ("### 📄 Analysis Documents\n \n " )
401+ hasGists = true
402+ }
403+ comment .WriteString (fmt .Sprintf ("- [Implementation Details](%s)\n " , implGist ))
404+ }
405+ if hasGists {
406+ comment .WriteString ("\n " )
407+ }
408+ }
409+
410+ comment .WriteString ("*Synchronized by vTeam BugFix Workspace*" )
411+
412+ return comment .String ()
413+ }
414+
415+ // formatGitHubJiraUpdateComment formats the comment to post on GitHub Issue when updating Jira task
416+ func formatGitHubJiraUpdateComment (jiraTaskKey , jiraTaskURL string , workflow * types.BugFixWorkflow ) string {
417+ var comment strings.Builder
418+
419+ comment .WriteString ("## 🔄 Jira Task Updated\n \n " )
420+ comment .WriteString (fmt .Sprintf ("Jira task [**%s**](%s) has been updated with the latest information.\n \n " , jiraTaskKey , jiraTaskURL ))
421+
422+ // Add links to analysis documents if available
423+ if workflow .Annotations != nil {
424+ hasGists := false
425+ if bugReviewGist := workflow .Annotations ["bug-review-gist-url" ]; bugReviewGist != "" {
426+ if ! hasGists {
427+ comment .WriteString ("### 📄 Latest Analysis\n \n " )
428+ hasGists = true
429+ }
430+ comment .WriteString (fmt .Sprintf ("- [Bug Review & Assessment](%s)\n " , bugReviewGist ))
431+ }
432+ if implGist := workflow .Annotations ["implementation-gist-url" ]; implGist != "" {
433+ if ! hasGists {
434+ comment .WriteString ("### 📄 Latest Analysis\n \n " )
435+ hasGists = true
436+ }
437+ comment .WriteString (fmt .Sprintf ("- [Implementation Details](%s)\n " , implGist ))
438+ }
439+ if hasGists {
440+ comment .WriteString ("\n " )
441+ }
442+ }
443+
444+ comment .WriteString ("*Synchronized by vTeam BugFix Workspace*" )
445+
446+ return comment .String ()
275447}
276448
277449// getSuccessMessage returns appropriate success message
@@ -280,4 +452,54 @@ func getSuccessMessage(created bool, jiraTaskKey string) string {
280452 return fmt .Sprintf ("Created Jira task %s" , jiraTaskKey )
281453 }
282454 return fmt .Sprintf ("Updated Jira task %s" , jiraTaskKey )
455+ }
456+
457+ // attachGistsToJira fetches Gist content and attaches it as markdown files to the Jira issue
458+ func attachGistsToJira (c * gin.Context , workflow * types.BugFixWorkflow , jiraBase , jiraTaskKey , authHeader string , reqK8s * kubernetes.Clientset , reqDyn dynamic.Interface , project string ) {
459+ if workflow .Annotations == nil {
460+ return
461+ }
462+
463+ // Get GitHub token for fetching Gists
464+ userID , _ := c .Get ("userID" )
465+ userIDStr , _ := userID .(string )
466+ githubToken , err := git .GetGitHubToken (c .Request .Context (), reqK8s , reqDyn , project , userIDStr )
467+ if err != nil || githubToken == "" {
468+ fmt .Printf ("Warning: Cannot attach Gists - failed to get GitHub token: %v\n " , err )
469+ return
470+ }
471+
472+ ctx := c .Request .Context ()
473+
474+ // Attach bug-review Gist if available
475+ if bugReviewGist := workflow .Annotations ["bug-review-gist-url" ]; bugReviewGist != "" {
476+ fmt .Printf ("Fetching bug-review Gist from %s\n " , bugReviewGist )
477+ gistContent , err := github .GetGist (ctx , bugReviewGist , githubToken )
478+ if err != nil {
479+ fmt .Printf ("Warning: Failed to fetch bug-review Gist: %v\n " , err )
480+ } else {
481+ filename := fmt .Sprintf ("bug-review-issue-%d.md" , workflow .GithubIssueNumber )
482+ if attachErr := jira .AttachFileToJiraIssue (ctx , jiraBase , jiraTaskKey , authHeader , filename , []byte (gistContent )); attachErr != nil {
483+ fmt .Printf ("Warning: Failed to attach bug-review.md to %s: %v\n " , jiraTaskKey , attachErr )
484+ } else {
485+ fmt .Printf ("Successfully attached %s to %s\n " , filename , jiraTaskKey )
486+ }
487+ }
488+ }
489+
490+ // Attach implementation Gist if available
491+ if implGist := workflow .Annotations ["implementation-gist-url" ]; implGist != "" {
492+ fmt .Printf ("Fetching implementation Gist from %s\n " , implGist )
493+ gistContent , err := github .GetGist (ctx , implGist , githubToken )
494+ if err != nil {
495+ fmt .Printf ("Warning: Failed to fetch implementation Gist: %v\n " , err )
496+ } else {
497+ filename := fmt .Sprintf ("implementation-issue-%d.md" , workflow .GithubIssueNumber )
498+ if attachErr := jira .AttachFileToJiraIssue (ctx , jiraBase , jiraTaskKey , authHeader , filename , []byte (gistContent )); attachErr != nil {
499+ fmt .Printf ("Warning: Failed to attach implementation.md to %s: %v\n " , jiraTaskKey , attachErr )
500+ } else {
501+ fmt .Printf ("Successfully attached %s to %s\n " , filename , jiraTaskKey )
502+ }
503+ }
504+ }
283505}
0 commit comments