@@ -10,36 +10,36 @@ import (
1010 "github.com/diggerhq/digger/backend/ci_backends"
1111 config2 "github.com/diggerhq/digger/backend/config"
1212 "github.com/diggerhq/digger/backend/locking"
13+ "github.com/diggerhq/digger/backend/middleware"
14+ "github.com/diggerhq/digger/backend/models"
1315 "github.com/diggerhq/digger/backend/segment"
1416 "github.com/diggerhq/digger/backend/services"
17+ "github.com/diggerhq/digger/backend/utils"
1518 "github.com/diggerhq/digger/libs/ci"
1619 "github.com/diggerhq/digger/libs/ci/generic"
20+ dg_github "github.com/diggerhq/digger/libs/ci/github"
1721 comment_updater "github.com/diggerhq/digger/libs/comment_utils/reporting"
22+ dg_configuration "github.com/diggerhq/digger/libs/digger_config"
1823 dg_locking "github.com/diggerhq/digger/libs/locking"
1924 orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
25+ "github.com/dominikbraun/graph"
26+ "github.com/gin-gonic/gin"
27+ "github.com/google/go-github/v61/github"
2028 "github.com/google/uuid"
29+ "github.com/samber/lo"
30+ "golang.org/x/oauth2"
2131 "gorm.io/gorm"
2232 "log"
2333 "math/rand"
2434 "net/http"
2535 "net/url"
2636 "os"
27- "path"
37+ "path/filepath "
2838 "reflect"
39+ "runtime/debug"
2940 "slices"
3041 "strconv"
3142 "strings"
32-
33- "github.com/diggerhq/digger/backend/middleware"
34- "github.com/diggerhq/digger/backend/models"
35- "github.com/diggerhq/digger/backend/utils"
36- dg_github "github.com/diggerhq/digger/libs/ci/github"
37- dg_configuration "github.com/diggerhq/digger/libs/digger_config"
38- "github.com/dominikbraun/graph"
39- "github.com/gin-gonic/gin"
40- "github.com/google/go-github/v61/github"
41- "github.com/samber/lo"
42- "golang.org/x/oauth2"
4343)
4444
4545type IssueCommentHook func (gh utils.GithubClientProvider , payload * github.IssueCommentEvent , ciBackendProvider ci_backends.CiBackendProvider ) error
@@ -309,6 +309,16 @@ func handleInstallationDeletedEvent(installation *github.InstallationEvent, appI
309309}
310310
311311func handlePullRequestEvent (gh utils.GithubClientProvider , payload * github.PullRequestEvent , ciBackendProvider ci_backends.CiBackendProvider , appId int64 ) error {
312+ defer func () {
313+ if r := recover (); r != nil {
314+ log .Printf ("Recovered from panic in handlePullRequestEvent handler: %v" , r )
315+ log .Printf ("\n === PANIC RECOVERED ===\n " )
316+ log .Printf ("Error: %v\n " , r )
317+ log .Printf ("Stack Trace:\n %s" , string (debug .Stack ()))
318+ log .Printf ("=== END PANIC ===\n " )
319+ }
320+ }()
321+
312322 installationId := * payload .Installation .ID
313323 repoName := * payload .Repo .Name
314324 repoOwner := * payload .Repo .Owner .Login
@@ -376,6 +386,7 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
376386 diggerYmlStr , ghService , config , projectsGraph , _ , _ , changedFiles , err := getDiggerConfigForPR (gh , organisationId , prLabelsStr , installationId , repoFullName , repoOwner , repoName , cloneURL , prNumber )
377387 if err != nil {
378388 log .Printf ("getDiggerConfigForPR error: %v" , err )
389+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Error loading digger config: %v" , err ))
379390 return fmt .Errorf ("error getting digger config" )
380391 }
381392
@@ -501,6 +512,22 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
501512 commentReporterManager .UpdateComment (fmt .Sprintf (":x: could not handle commentId: %v" , err ))
502513 }
503514
515+
516+
517+ var aiSummaryCommentId = ""
518+ if config .Reporting .AiSummary {
519+ aiSummaryComment , err := ghService .PublishComment (prNumber , "AI Summary will be posted here after completion" )
520+ if err != nil {
521+ log .Printf ("could not post ai summary comment: %v" , err )
522+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: could not post ai comment summary comment id: %v" , err ))
523+ return fmt .Errorf ("could not post ai summary comment: %v" , err )
524+ }
525+ aiSummaryCommentId = aiSummaryComment .Id
526+ }
527+
528+ batchId , _ , err := utils .ConvertJobsToDiggerJobs (* diggerCommand , models .DiggerVCSGithub , organisationId , impactedJobsMap , impactedProjectsMap , projectsGraph , installationId , branch , prNumber , repoOwner , repoName , repoFullName , commitSha , commentId , diggerYmlStr , 0 , aiSummaryCommentId , config .ReportTerraformOutputs )
529+
530+
504531 placeholderComment , err := ghService .PublishComment (prNumber , "digger report placehoder" )
505532 if err != nil {
506533 log .Printf ("strconv.ParseInt error: %v" , err )
@@ -509,6 +536,8 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
509536 }
510537
511538 batchId , _ , err := utils .ConvertJobsToDiggerJobs (* diggerCommand , models .DiggerVCSGithub , organisationId , impactedJobsMap , impactedProjectsMap , projectsGraph , installationId , branch , prNumber , repoOwner , repoName , repoFullName , commitSha , commentId , & placeholderComment .Id , diggerYmlStr , 0 )
539+
540+
512541 if err != nil {
513542 log .Printf ("ConvertJobsToDiggerJobs error: %v" , err )
514543 commentReporterManager .UpdateComment (fmt .Sprintf (":x: ConvertJobsToDiggerJobs error: %v" , err ))
@@ -582,8 +611,11 @@ func GetDiggerConfigForBranch(gh utils.GithubClientProvider, installationId int6
582611 var dependencyGraph graph.Graph [string , dg_configuration.Project ]
583612
584613 err = utils .CloneGitRepoAndDoAction (cloneUrl , branch , "" , * token , func (dir string ) error {
585- diggerYmlBytes , err := os .ReadFile (path .Join (dir , "digger.yml" ))
586- diggerYmlStr = string (diggerYmlBytes )
614+ diggerYmlStr , err = dg_configuration .ReadDiggerYmlFileContents (dir )
615+ if err != nil {
616+ log .Printf ("could not load digger config: %v" , err )
617+ return err
618+ }
587619 config , _ , dependencyGraph , err = dg_configuration .LoadDiggerConfig (dir , true , changedFiles )
588620 if err != nil {
589621 log .Printf ("Error loading digger config: %v" , err )
@@ -692,6 +724,16 @@ func getBatchType(jobs []orchestrator_scheduler.Job) orchestrator_scheduler.Digg
692724}
693725
694726func handleIssueCommentEvent (gh utils.GithubClientProvider , payload * github.IssueCommentEvent , ciBackendProvider ci_backends.CiBackendProvider , appId int64 , postCommentHooks []IssueCommentHook ) error {
727+ defer func () {
728+ if r := recover (); r != nil {
729+ log .Printf ("Recovered from panic in handleIssueCommentEvent handler: %v" , r )
730+ log .Printf ("\n === PANIC RECOVERED ===\n " )
731+ log .Printf ("Error: %v\n " , r )
732+ log .Printf ("Stack Trace:\n %s" , string (debug .Stack ()))
733+ log .Printf ("=== END PANIC ===\n " )
734+ }
735+ }()
736+
695737 installationId := * payload .Installation .ID
696738 repoName := * payload .Repo .Name
697739 repoOwner := * payload .Repo .Owner .Login
@@ -752,6 +794,15 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
752794 return fmt .Errorf ("error getting digger config" )
753795 }
754796
797+ // terraform code generator
798+ if os .Getenv ("DIGGER_GENERATION_ENABLED" ) == "1" {
799+ err = GenerateTerraformFromCode (payload , commentReporterManager , config , defaultBranch , ghService , repoOwner , repoName , commitSha , issueNumber , branch )
800+ if err != nil {
801+ log .Printf ("terraform generation failed: %v" , err )
802+ return err
803+ }
804+ }
805+
755806 commentIdStr := strconv .FormatInt (userCommentId , 10 )
756807 err = ghService .CreateCommentReaction (commentIdStr , string (dg_github .GithubCommentEyesReaction ))
757808 if err != nil {
@@ -883,6 +934,20 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
883934 return fmt .Errorf ("comment reporter error: %v" , err )
884935 }
885936
937+
938+ var aiSummaryCommentId = ""
939+ if config .Reporting .AiSummary {
940+ aiSummaryComment , err := ghService .PublishComment (issueNumber , "AI Summary will be posted here after completion" )
941+ if err != nil {
942+ log .Printf ("could not post ai summary comment: %v" , err )
943+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: could not post ai comment summary comment id: %v" , err ))
944+ return fmt .Errorf ("could not post ai summary comment: %v" , err )
945+ }
946+ aiSummaryCommentId = aiSummaryComment .Id
947+ }
948+
949+ batchId , _ , err := utils .ConvertJobsToDiggerJobs (* diggerCommand , "github" , orgId , impactedProjectsJobMap , impactedProjectsMap , projectsGraph , installationId , * branch , issueNumber , repoOwner , repoName , repoFullName , * commitSha , reporterCommentId , diggerYmlStr , 0 , aiSummaryCommentId , config .ReportTerraformOutputs )
950+
886951 placeholderComment , err := ghService .PublishComment (issueNumber , "digger report placehoder" )
887952 if err != nil {
888953 log .Printf ("strconv.ParseInt error: %v" , err )
@@ -891,6 +956,7 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
891956 }
892957
893958 batchId , _ , err := utils .ConvertJobsToDiggerJobs (* diggerCommand , "github" , orgId , impactedProjectsJobMap , impactedProjectsMap , projectsGraph , installationId , * branch , issueNumber , repoOwner , repoName , repoFullName , * commitSha , reporterCommentId , & placeholderComment .Id , diggerYmlStr , 0 )
959+
894960 if err != nil {
895961 log .Printf ("ConvertJobsToDiggerJobs error: %v" , err )
896962 commentReporterManager .UpdateComment (fmt .Sprintf (":x: ConvertJobsToDiggerJobs error: %v" , err ))
@@ -962,6 +1028,158 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
9621028 return nil
9631029}
9641030
1031+ func GenerateTerraformFromCode (payload * github.IssueCommentEvent , commentReporterManager utils.CommentReporterManager , config * dg_configuration.DiggerConfig , defaultBranch string , ghService * dg_github.GithubService , repoOwner string , repoName string , commitSha * string , issueNumber int , branch * string ) error {
1032+ if strings .HasPrefix (* payload .Comment .Body , "digger generate" ) {
1033+ projectName := ci .ParseProjectName (* payload .Comment .Body )
1034+ if projectName == "" {
1035+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: generate requires argument -p <project_name>" ))
1036+ log .Printf ("missing project in command: %v" , * payload .Comment .Body )
1037+ return fmt .Errorf ("generate requires argument -p <project_name>" )
1038+ }
1039+
1040+ project := config .GetProject (projectName )
1041+ if project == nil {
1042+ commentReporterManager .UpdateComment (fmt .Sprintf ("could not find project %v in digger.yml" , projectName ))
1043+ log .Printf ("could not find project %v in digger.yml" , projectName )
1044+ return fmt .Errorf ("could not find project %v in digger.yml" , projectName )
1045+ }
1046+
1047+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Successfully loaded project" ))
1048+
1049+ generationEndpoint := os .Getenv ("DIGGER_GENERATION_ENDPOINT" )
1050+ if generationEndpoint == "" {
1051+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: server does not have generation endpoint configured, please verify" ))
1052+ log .Printf ("server does not have generation endpoint configured, please verify" )
1053+ return fmt .Errorf ("server does not have generation endpoint configured, please verify" )
1054+ }
1055+ apiToken := os .Getenv ("DIGGER_GENERATION_API_TOKEN" )
1056+
1057+ // Get all code content from the repository at a specific commit
1058+ getCodeFromCommit := func (ghService * dg_github.GithubService , repoOwner , repoName string , commitSha * string , projectDir string ) (string , error ) {
1059+ const MaxPatchSize = 1024 * 1024 // 1MB limit
1060+
1061+ // Get the commit's changes compared to default branch
1062+ comparison , _ , err := ghService .Client .Repositories .CompareCommits (
1063+ context .Background (),
1064+ repoOwner ,
1065+ repoName ,
1066+ defaultBranch ,
1067+ * commitSha ,
1068+ nil ,
1069+ )
1070+ if err != nil {
1071+ return "" , fmt .Errorf ("error comparing commits: %v" , err )
1072+ }
1073+
1074+ var appCode strings.Builder
1075+ for _ , file := range comparison .Files {
1076+ if file .Patch == nil {
1077+ continue // Skip files without patches
1078+ }
1079+ log .Printf ("Processing patch for file: %s" , * file .Filename )
1080+ if * file .Additions > 0 {
1081+ lines := strings .Split (* file .Patch , "\n " )
1082+ for _ , line := range lines {
1083+ if strings .HasPrefix (line , "+" ) && ! strings .HasPrefix (line , "+++" ) {
1084+ appCode .WriteString (strings .TrimPrefix (line , "+" ))
1085+ appCode .WriteString ("\n " )
1086+ }
1087+ }
1088+ }
1089+ appCode .WriteString ("\n " )
1090+ }
1091+
1092+ if appCode .Len () == 0 {
1093+ return "" , fmt .Errorf ("no code changes found in commit %s. Please ensure the PR contains added or modified code" , * commitSha )
1094+ }
1095+
1096+ return appCode .String (), nil
1097+ }
1098+
1099+ appCode , err := getCodeFromCommit (ghService , repoOwner , repoName , commitSha , project .Dir )
1100+ if err != nil {
1101+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to get code content: %v" , err ))
1102+ log .Printf ("Error getting code content: %v" , err )
1103+ return fmt .Errorf ("error getting code content: %v" , err )
1104+ }
1105+
1106+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Successfully loaded code from commit" ))
1107+
1108+ log .Printf ("the app code is: %v" , appCode )
1109+
1110+ commentReporterManager .UpdateComment (fmt .Sprintf ("Generating terraform..." ))
1111+ terraformCode , err := utils .GenerateTerraformCode (appCode , generationEndpoint , apiToken )
1112+ if err != nil {
1113+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: could not generate terraform code: %v" , err ))
1114+ log .Printf ("could not generate terraform code: %v" , err )
1115+ return fmt .Errorf ("could not generate terraform code: %v" , err )
1116+ }
1117+
1118+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Generated terraform" ))
1119+
1120+ // comment terraform code to project dir
1121+ //project.Dir
1122+ log .Printf ("terraform code is %v" , terraformCode )
1123+
1124+ baseTree , _ , err := ghService .Client .Git .GetTree (context .Background (), repoOwner , repoName , * commitSha , false )
1125+ if err != nil {
1126+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to get base tree: %v" , err ))
1127+ log .Printf ("Error getting base tree: %v" , err )
1128+ return fmt .Errorf ("error getting base tree: %v" , err )
1129+ }
1130+
1131+ // Create a new tree with the new file
1132+ treeEntries := []* github.TreeEntry {
1133+ {
1134+ Path : github .String (filepath .Join (project .Dir , fmt .Sprintf ("generated_%v.tf" , issueNumber ))),
1135+ Mode : github .String ("100644" ),
1136+ Type : github .String ("blob" ),
1137+ Content : github .String (terraformCode ),
1138+ },
1139+ }
1140+
1141+ newTree , _ , err := ghService .Client .Git .CreateTree (context .Background (), repoOwner , repoName , * baseTree .SHA , treeEntries )
1142+ if err != nil {
1143+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to create new tree: %v" , err ))
1144+ log .Printf ("Error creating new tree: %v" , err )
1145+ return fmt .Errorf ("error creating new tree: %v" , err )
1146+ }
1147+
1148+ // Create the commit
1149+ commitMsg := fmt .Sprintf ("Add generated Terraform code for %v" , projectName )
1150+ commit := & github.Commit {
1151+ Message : & commitMsg ,
1152+ Tree : newTree ,
1153+ Parents : []* github.Commit {{SHA : commitSha }},
1154+ }
1155+
1156+ newCommit , _ , err := ghService .Client .Git .CreateCommit (context .Background (), repoOwner , repoName , commit , nil )
1157+ if err != nil {
1158+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to commit Terraform file: %v" , err ))
1159+ log .Printf ("Error committing Terraform file: %v" , err )
1160+ return fmt .Errorf ("error committing Terraform file: %v" , err )
1161+ }
1162+
1163+ // Update the reference to point to the new commit
1164+ ref := & github.Reference {
1165+ Ref : github .String (fmt .Sprintf ("refs/heads/%s" , * branch )),
1166+ Object : & github.GitObject {
1167+ SHA : newCommit .SHA ,
1168+ },
1169+ }
1170+ _ , _ , err = ghService .Client .Git .UpdateRef (context .Background (), repoOwner , repoName , ref , false )
1171+ if err != nil {
1172+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to update branch reference: %v" , err ))
1173+ log .Printf ("Error updating branch reference: %v" , err )
1174+ return fmt .Errorf ("error updating branch reference: %v" , err )
1175+ }
1176+
1177+ commentReporterManager .UpdateComment (":white_check_mark: Successfully generated and committed Terraform code" )
1178+ return nil
1179+ }
1180+ return nil
1181+ }
1182+
9651183func TriggerDiggerJobs (ciBackend ci_backends.CiBackend , repoFullName string , repoOwner string , repoName string , batchId * uuid.UUID , prNumber int , prService ci.PullRequestService , gh utils.GithubClientProvider ) error {
9661184 _ , err := models .DB .GetDiggerBatch (batchId )
9671185 if err != nil {
0 commit comments