@@ -24,6 +24,7 @@ import (
2424 "net/http"
2525 "os"
2626 "regexp"
27+ "sort"
2728 "strconv"
2829 "strings"
2930
@@ -212,7 +213,7 @@ func (c *Client) CommentOnIssue(ctx context.Context, r report.Report, issue *git
212213 commitMessage := c .getCommitMessage (ctx )
213214 prNumber := c .extractPRNumberFromCommitMessage (commitMessage )
214215
215- body := os .Expand (issueCommentTemplate , templateHelper (c .envVariables , r , prNumber ))
216+ body := os .Expand (issueCommentTemplate , templateHelper (ctx , c , c .envVariables , r , prNumber ))
216217
217218 issueComment , response , err := c .client .Issues .CreateComment (
218219 ctx ,
@@ -261,13 +262,46 @@ func getComponent(module string) string {
261262 return module
262263}
263264
264- func templateHelper (env map [string ]string , r report.Report , prNumber int ) func (string ) string {
265+ func templateHelper (ctx context. Context , c * Client , env map [string ]string , r report.Report , prNumber int ) func (string ) string {
265266 return func (param string ) string {
266267 switch param {
267268 case "jobName" :
268269 return "`" + env [githubWorkflow ] + "`"
269270 case "linkToBuild" :
270- return fmt .Sprintf ("%s/%s/%s/actions/runs/%s" , env [githubServerURL ], env [githubOwner ], env [githubRepository ], env [githubRunID ])
271+ runID , err := strconv .ParseInt (env [githubRunID ], 10 , 64 )
272+ if err != nil {
273+ c .logger .Warn ("Failed to parse run ID" , zap .Error (err ))
274+ return c .getRunURL (env [githubRunID ])
275+ }
276+
277+ failedJobs , err := c .getFailedJobURLs (ctx , runID )
278+ if err != nil {
279+ c .logger .Warn ("Failed to get failed job URLs" , zap .Error (err ))
280+ return c .getRunURL (strconv .FormatInt (runID , 10 ))
281+ }
282+
283+ if len (failedJobs ) == 0 {
284+ return c .getRunURL (strconv .FormatInt (runID , 10 ))
285+ }
286+
287+ if len (failedJobs ) == 1 {
288+ for _ , url := range failedJobs {
289+ return url
290+ }
291+ }
292+
293+ // Sort by name for deterministic output
294+ names := make ([]string , 0 , len (failedJobs ))
295+ for name := range failedJobs {
296+ names = append (names , name )
297+ }
298+ sort .Strings (names )
299+
300+ var sb strings.Builder
301+ for _ , name := range names {
302+ sb .WriteString (fmt .Sprintf ("\n - [`%s`](%s)" , name , failedJobs [name ]))
303+ }
304+ return sb .String ()
271305 case "failedTests" :
272306 return r .FailedTestsMD ()
273307 case "component" :
@@ -286,6 +320,38 @@ func templateHelper(env map[string]string, r report.Report, prNumber int) func(s
286320 }
287321}
288322
323+ // getFailedJobURLs fetches the names and URLs of the failed jobs in the current run.
324+ func (c * Client ) getFailedJobURLs (ctx context.Context , runID int64 ) (map [string ]string , error ) {
325+ failedJobs := make (map [string ]string )
326+ opts := & github.ListWorkflowJobsOptions {ListOptions : github.ListOptions {PerPage : 100 }}
327+ for {
328+ jobs , resp , err := c .client .Actions .ListWorkflowJobs (ctx , c .envVariables [githubOwner ], c .envVariables [githubRepository ], runID , opts )
329+ if err != nil {
330+ return nil , err
331+ }
332+ for _ , job := range jobs .Jobs {
333+ if job .GetConclusion () == "failure" || job .GetConclusion () == "timed_out" {
334+ failedJobs [job .GetName ()] = job .GetHTMLURL ()
335+ }
336+ }
337+ if resp .NextPage == 0 {
338+ break
339+ }
340+ opts .Page = resp .NextPage
341+ }
342+ return failedJobs , nil
343+ }
344+
345+ // getRunURL returns the generic workflow run URL.
346+ func (c * Client ) getRunURL (runID string ) string {
347+ return fmt .Sprintf ("%s/%s/%s/actions/runs/%s" ,
348+ c .envVariables [githubServerURL ],
349+ c .envVariables [githubOwner ],
350+ c .envVariables [githubRepository ],
351+ runID ,
352+ )
353+ }
354+
289355// shortSha returns the first 7 characters of a full Git commit SHA
290356func shortSha (sha string ) string {
291357 if len (sha ) >= 7 {
@@ -442,7 +508,7 @@ func (c *Client) CreateIssue(ctx context.Context, r report.Report) *github.Issue
442508 commitMessage := c .getCommitMessage (ctx )
443509 prNumber := c .extractPRNumberFromCommitMessage (commitMessage )
444510
445- body := os .Expand (issueBodyTemplate , templateHelper (c .envVariables , r , prNumber ))
511+ body := os .Expand (issueBodyTemplate , templateHelper (ctx , c , c .envVariables , r , prNumber ))
446512 componentName := getComponent (trimmedModule )
447513
448514 issueLabels := c .cfg .labelsCopy ()
0 commit comments