@@ -10,37 +10,37 @@ 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"
2737 "path"
38+ "path/filepath"
2839 "reflect"
2940 "runtime/debug"
3041 "slices"
3142 "strconv"
3243 "strings"
33-
34- "github.com/diggerhq/digger/backend/middleware"
35- "github.com/diggerhq/digger/backend/models"
36- "github.com/diggerhq/digger/backend/utils"
37- dg_github "github.com/diggerhq/digger/libs/ci/github"
38- dg_configuration "github.com/diggerhq/digger/libs/digger_config"
39- "github.com/dominikbraun/graph"
40- "github.com/gin-gonic/gin"
41- "github.com/google/go-github/v61/github"
42- "github.com/samber/lo"
43- "golang.org/x/oauth2"
4444)
4545
4646type IssueCommentHook func (gh utils.GithubClientProvider , payload * github.IssueCommentEvent , ciBackendProvider ci_backends.CiBackendProvider ) error
@@ -765,6 +765,158 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
765765 return fmt .Errorf ("error getting digger config" )
766766 }
767767
768+ // terraform code generator
769+ if os .Getenv ("DIGGER_GENERATION_ENABLED" ) == "1" {
770+ if strings .HasPrefix (* payload .Comment .Body , "digger generate" ) {
771+ projectName := ci .ParseProjectName (* payload .Comment .Body )
772+ if projectName == "" {
773+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: generate requires argument -p <project_name>: %v" , err ))
774+ log .Printf ("missing project in command: %v" , * payload .Comment .Body )
775+ return fmt .Errorf ("generate requires argument -p <project_name>: %v" , err )
776+ }
777+
778+ project := config .GetProject (projectName )
779+ if project == nil {
780+ commentReporterManager .UpdateComment (fmt .Sprintf ("could not find project %v in digger.yml" , projectName ))
781+ log .Printf ("could not find project %v in digger.yml" , projectName )
782+ return fmt .Errorf ("could not find project %v in digger.yml" , projectName )
783+ }
784+
785+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Successfully loaded project" ))
786+
787+ generationEndpoint := os .Getenv ("DIGGER_GENERATION_ENDPOINT" )
788+ if generationEndpoint == "" {
789+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: server does not have generation endpoint configured, please verify" ))
790+ log .Printf ("server does not have generation endpoint configured, please verify" )
791+ return fmt .Errorf ("server does not have generation endpoint configured, please verify" )
792+ }
793+ webhookSecret := os .Getenv ("DIGGER_GENERATION_WEBHOOK_SECRET" )
794+
795+ // Get all code content from the repository at a specific commit
796+ getCodeFromCommit := func (ghService * dg_github.GithubService , repoOwner , repoName string , commitSha * string , projectDir string ) (string , error ) {
797+ const MaxPatchSize = 1024 * 1024 // 1MB limit
798+
799+ // Get the commit's changes compared to default branch
800+ comparison , _ , err := ghService .Client .Repositories .CompareCommits (
801+ context .Background (),
802+ repoOwner ,
803+ repoName ,
804+ defaultBranch ,
805+ * commitSha ,
806+ nil ,
807+ )
808+ if err != nil {
809+ return "" , fmt .Errorf ("error comparing commits: %v" , err )
810+ }
811+
812+ var appCode strings.Builder
813+ for _ , file := range comparison .Files {
814+ if file .Patch == nil {
815+ continue // Skip files without patches
816+ }
817+ log .Printf ("Processing patch for file: %s" , * file .Filename )
818+ if * file .Additions > 0 {
819+ lines := strings .Split (* file .Patch , "\n " )
820+ for _ , line := range lines {
821+ if strings .HasPrefix (line , "+" ) && ! strings .HasPrefix (line , "+++" ) {
822+ appCode .WriteString (strings .TrimPrefix (line , "+" ))
823+ appCode .WriteString ("\n " )
824+ }
825+ }
826+ }
827+ appCode .WriteString ("\n " )
828+ }
829+
830+ if appCode .Len () == 0 {
831+ return "" , fmt .Errorf ("no code changes found in commit %s. Please ensure the PR contains added or modified code" , * commitSha )
832+ }
833+
834+ return appCode .String (), nil
835+ }
836+
837+ appCode , err := getCodeFromCommit (ghService , repoOwner , repoName , commitSha , project .Dir )
838+ if err != nil {
839+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to get code content: %v" , err ))
840+ log .Printf ("Error getting code content: %v" , err )
841+ return fmt .Errorf ("error getting code content: %v" , err )
842+ }
843+
844+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Successfully loaded code from commit" ))
845+
846+ log .Printf ("the app code is: %v" , appCode )
847+
848+ commentReporterManager .UpdateComment (fmt .Sprintf ("Generating terraform..." ))
849+ terraformCode , err := utils .GenerateTerraformCode (appCode , generationEndpoint , webhookSecret )
850+ if err != nil {
851+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: could not generate terraform code: %v" , err ))
852+ log .Printf ("could not generate terraform code: %v" , err )
853+ return fmt .Errorf ("could not generate terraform code: %v" , err )
854+ }
855+
856+ commentReporterManager .UpdateComment (fmt .Sprintf (":white_check_mark: Generated terraform" ))
857+
858+ // comment terraform code to project dir
859+ //project.Dir
860+ log .Printf ("terraform code is %v" , terraformCode )
861+
862+ baseTree , _ , err := ghService .Client .Git .GetTree (context .Background (), repoOwner , repoName , * commitSha , false )
863+ if err != nil {
864+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to get base tree: %v" , err ))
865+ log .Printf ("Error getting base tree: %v" , err )
866+ return fmt .Errorf ("error getting base tree: %v" , err )
867+ }
868+
869+ // Create a new tree with the new file
870+ treeEntries := []* github.TreeEntry {
871+ {
872+ Path : github .String (filepath .Join (project .Dir , fmt .Sprintf ("generated_%v.tf" , issueNumber ))),
873+ Mode : github .String ("100644" ),
874+ Type : github .String ("blob" ),
875+ Content : github .String (terraformCode ),
876+ },
877+ }
878+
879+ newTree , _ , err := ghService .Client .Git .CreateTree (context .Background (), repoOwner , repoName , * baseTree .SHA , treeEntries )
880+ if err != nil {
881+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to create new tree: %v" , err ))
882+ log .Printf ("Error creating new tree: %v" , err )
883+ return fmt .Errorf ("error creating new tree: %v" , err )
884+ }
885+
886+ // Create the commit
887+ commitMsg := fmt .Sprintf ("Add generated Terraform code for %v" , projectName )
888+ commit := & github.Commit {
889+ Message : & commitMsg ,
890+ Tree : newTree ,
891+ Parents : []* github.Commit {{SHA : commitSha }},
892+ }
893+
894+ newCommit , _ , err := ghService .Client .Git .CreateCommit (context .Background (), repoOwner , repoName , commit , nil )
895+ if err != nil {
896+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to commit Terraform file: %v" , err ))
897+ log .Printf ("Error committing Terraform file: %v" , err )
898+ return fmt .Errorf ("error committing Terraform file: %v" , err )
899+ }
900+
901+ // Update the reference to point to the new commit
902+ ref := & github.Reference {
903+ Ref : github .String (fmt .Sprintf ("refs/heads/%s" , * branch )),
904+ Object : & github.GitObject {
905+ SHA : newCommit .SHA ,
906+ },
907+ }
908+ _ , _ , err = ghService .Client .Git .UpdateRef (context .Background (), repoOwner , repoName , ref , false )
909+ if err != nil {
910+ commentReporterManager .UpdateComment (fmt .Sprintf (":x: Failed to update branch reference: %v" , err ))
911+ log .Printf ("Error updating branch reference: %v" , err )
912+ return fmt .Errorf ("error updating branch reference: %v" , err )
913+ }
914+
915+ commentReporterManager .UpdateComment (":white_check_mark: Successfully generated and committed Terraform code" )
916+ return nil
917+ }
918+ }
919+
768920 commentIdStr := strconv .FormatInt (userCommentId , 10 )
769921 err = ghService .CreateCommentReaction (commentIdStr , string (dg_github .GithubCommentEyesReaction ))
770922 if err != nil {
0 commit comments