Skip to content

Commit 9599562

Browse files
authored
publish ai summaries (#1864)
* publish ai summaries
1 parent 34d1db1 commit 9599562

27 files changed

+531
-700
lines changed

backend/controllers/github.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434
"net/http"
3535
"net/url"
3636
"os"
37-
"path"
3837
"path/filepath"
3938
"reflect"
4039
"runtime/debug"
@@ -511,7 +510,19 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR
511510
log.Printf("strconv.ParseInt error: %v", err)
512511
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not handle commentId: %v", err))
513512
}
514-
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggerYmlStr, 0)
513+
514+
var aiSummaryCommentId = ""
515+
if config.Reporting.AiSummary {
516+
aiSummaryComment, err := ghService.PublishComment(prNumber, "AI Summary will be posted here after completion")
517+
if err != nil {
518+
log.Printf("could not post ai summary comment: %v", err)
519+
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not post ai comment summary comment id: %v", err))
520+
return fmt.Errorf("could not post ai summary comment: %v", err)
521+
}
522+
aiSummaryCommentId = aiSummaryComment.Id
523+
}
524+
525+
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs)
515526
if err != nil {
516527
log.Printf("ConvertJobsToDiggerJobs error: %v", err)
517528
commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err))
@@ -585,8 +596,11 @@ func GetDiggerConfigForBranch(gh utils.GithubClientProvider, installationId int6
585596
var dependencyGraph graph.Graph[string, dg_configuration.Project]
586597

587598
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, func(dir string) error {
588-
diggerYmlBytes, err := os.ReadFile(path.Join(dir, "digger.yml"))
589-
diggerYmlStr = string(diggerYmlBytes)
599+
diggerYmlStr, err = dg_configuration.ReadDiggerYmlFileContents(dir)
600+
if err != nil {
601+
log.Printf("could not load digger config: %v", err)
602+
return err
603+
}
590604
config, _, dependencyGraph, err = dg_configuration.LoadDiggerConfig(dir, true, changedFiles)
591605
if err != nil {
592606
log.Printf("Error loading digger config: %v", err)
@@ -768,7 +782,10 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
768782
// terraform code generator
769783
if os.Getenv("DIGGER_GENERATION_ENABLED") == "1" {
770784
err = GenerateTerraformFromCode(payload, commentReporterManager, config, defaultBranch, ghService, repoOwner, repoName, commitSha, issueNumber, branch)
771-
return err
785+
if err != nil {
786+
log.Printf("terraform generation failed: %v", err)
787+
return err
788+
}
772789
}
773790

774791
commentIdStr := strconv.FormatInt(userCommentId, 10)
@@ -902,7 +919,18 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu
902919
return fmt.Errorf("comment reporter error: %v", err)
903920
}
904921

905-
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, diggerYmlStr, 0)
922+
var aiSummaryCommentId = ""
923+
if config.Reporting.AiSummary {
924+
aiSummaryComment, err := ghService.PublishComment(issueNumber, "AI Summary will be posted here after completion")
925+
if err != nil {
926+
log.Printf("could not post ai summary comment: %v", err)
927+
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not post ai comment summary comment id: %v", err))
928+
return fmt.Errorf("could not post ai summary comment: %v", err)
929+
}
930+
aiSummaryCommentId = aiSummaryComment.Id
931+
}
932+
933+
batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs)
906934
if err != nil {
907935
log.Printf("ConvertJobsToDiggerJobs error: %v", err)
908936
commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err))

backend/controllers/github_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ func TestJobsTreeWithOneJobsAndTwoProjects(t *testing.T) {
731731
graph, err := configuration.CreateProjectDependencyGraph(projects)
732732
assert.NoError(t, err)
733733

734-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 41584295, "", 2, "diggerhq", "parallel_jobs_demo", "diggerhq/parallel_jobs_demo", "", 123, "test", 0)
734+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 41584295, "", 2, "diggerhq", "parallel_jobs_demo", "diggerhq/parallel_jobs_demo", "", 123, "test", 0, "", false)
735735
assert.NoError(t, err)
736736
assert.Equal(t, 1, len(result))
737737
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["dev"].DiggerJobID)
@@ -760,7 +760,7 @@ func TestJobsTreeWithTwoDependantJobs(t *testing.T) {
760760
projectMap["dev"] = project1
761761
projectMap["prod"] = project2
762762

763-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0)
763+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
764764
assert.NoError(t, err)
765765
assert.Equal(t, 2, len(result))
766766

@@ -793,7 +793,7 @@ func TestJobsTreeWithTwoIndependentJobs(t *testing.T) {
793793
projectMap["dev"] = project1
794794
projectMap["prod"] = project2
795795

796-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0)
796+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
797797
assert.NoError(t, err)
798798
assert.Equal(t, 2, len(result))
799799
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["dev"].DiggerJobID)
@@ -838,7 +838,7 @@ func TestJobsTreeWithThreeLevels(t *testing.T) {
838838
projectMap["555"] = project5
839839
projectMap["666"] = project6
840840

841-
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0)
841+
_, result, err := utils.ConvertJobsToDiggerJobs("", "github", 1, jobs, projectMap, graph, 123, "", 2, "", "", "test", "", 123, "test", 0, "", false)
842842
assert.NoError(t, err)
843843
assert.Equal(t, 6, len(result))
844844
parentLinks, err := models.DB.GetDiggerJobParentLinksChildId(&result["111"].DiggerJobID)

backend/controllers/projects.go

+69-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"gorm.io/gorm"
1818
"log"
1919
"net/http"
20+
"os"
2021
"strconv"
2122
"strings"
2223
"time"
@@ -323,7 +324,6 @@ type SetJobStatusRequest struct {
323324
Footprint *iac_utils.IacPlanFootprint `json:"job_plan_footprint"`
324325
PrCommentUrl string `json:"pr_comment_url"`
325326
TerraformOutput string `json:"terraform_output"`
326-
327327
}
328328

329329
func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
@@ -482,6 +482,11 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
482482
return
483483
}
484484

485+
err = CreateTerraformOutputsSummary(d.GithubClientProvider, batch)
486+
if err != nil {
487+
log.Printf("could not generate terraform plans summary: %v", err)
488+
}
489+
485490
err = AutomergePRforBatchIfEnabled(d.GithubClientProvider, batch)
486491
if err != nil {
487492
log.Printf("Error merging PR with automerge option: %v", err)
@@ -493,7 +498,6 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
493498
if err != nil {
494499
log.Printf("Error getting batch details: %v", err)
495500
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error getting batch details"})
496-
497501
}
498502

499503
UpdateCommentsForBatchGroup(d.GithubClientProvider, batch, res.Jobs)
@@ -648,6 +652,69 @@ func GetPrServiceFromBatch(batch *models.DiggerBatch, gh utils.GithubClientProvi
648652
return nil, fmt.Errorf("could not retrieive a service for %v", batch.VCS)
649653
}
650654

655+
func CreateTerraformOutputsSummary(gh utils.GithubClientProvider, batch *models.DiggerBatch) error {
656+
diggerYmlString := batch.DiggerConfig
657+
diggerConfigYml, err := digger_config.LoadDiggerConfigYamlFromString(diggerYmlString)
658+
if err != nil {
659+
log.Printf("Error loading digger config from batch: %v", err)
660+
return fmt.Errorf("error loading digger config from batch: %v", err)
661+
}
662+
663+
config, _, err := digger_config.ConvertDiggerYamlToConfig(diggerConfigYml)
664+
665+
if batch.Status == orchestrator_scheduler.BatchJobSucceeded && config.Reporting.AiSummary == true {
666+
prService, err := GetPrServiceFromBatch(batch, gh)
667+
if err != nil {
668+
log.Printf("Error getting github service: %v", err)
669+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, ":x: could not generate AI summary \n\n could not communicate with github")
670+
return fmt.Errorf("error getting github service: %v", err)
671+
}
672+
673+
if batch.AiSummaryCommentId == "" {
674+
log.Printf("could not post summary comment, initial comment not found")
675+
prService.PublishComment(batch.PrNumber, ":x: could not generate AI summary \n\n could not communicate with github")
676+
return fmt.Errorf("could not post summary comment, initial comment not found")
677+
}
678+
679+
summaryEndpoint := os.Getenv("DIGGER_AI_SUMMARY_ENDPOINT")
680+
if summaryEndpoint == "" {
681+
log.Printf("could not generate AI summary, ai summary endpoint missing")
682+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, ":x: could not generate AI summary \n\n comment ID is missing ")
683+
return fmt.Errorf("could not generate AI summary, ai summary endpoint missing")
684+
}
685+
apiToken := os.Getenv("DIGGER_AI_SUMMARY_API_TOKEN")
686+
687+
jobs, err := models.DB.GetDiggerJobsForBatch(batch.ID)
688+
if err != nil {
689+
log.Printf("could not get jobs for batch: %v", err)
690+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, ":x: could not generate AI summary \n\n error fetching jobs ")
691+
return fmt.Errorf("could not get jobs for batch: %v", err)
692+
}
693+
694+
var terraformOutputs = "## AI Summary for terraform runs\n\n"
695+
for _, job := range jobs {
696+
var jobSpec orchestrator_scheduler.JobJson
697+
err := json.Unmarshal(job.SerializedJobSpec, &jobSpec)
698+
if err != nil {
699+
log.Printf("could not summarise plans due to unmarshalling error: %v", err)
700+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, ":x: could not generate AI summary \n\n error fetching job spec")
701+
return fmt.Errorf("could not summarise plans due to unmarshalling error: %v", err)
702+
}
703+
projectName := jobSpec.ProjectName
704+
terraformOutputs += fmt.Sprintf("terraform output for %v \n\n", projectName) + job.TerraformOutput
705+
}
706+
summary, err := utils.GetAiSummaryFromTerraformPlans(terraformOutputs, summaryEndpoint, apiToken)
707+
if err != nil {
708+
log.Printf("could not summarise terraform outputs: %v", err)
709+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, ":x: could not generate AI summary \n\n error generating summary from plans")
710+
return fmt.Errorf("could not summarise terraform outputs: %v", err)
711+
}
712+
713+
prService.EditComment(batch.PrNumber, batch.AiSummaryCommentId, summary)
714+
}
715+
return nil
716+
}
717+
651718
func AutomergePRforBatchIfEnabled(gh utils.GithubClientProvider, batch *models.DiggerBatch) error {
652719
diggerYmlString := batch.DiggerConfig
653720
diggerConfigYml, err := digger_config.LoadDiggerConfigYamlFromString(diggerYmlString)
@@ -663,13 +730,6 @@ func AutomergePRforBatchIfEnabled(gh utils.GithubClientProvider, batch *models.D
663730
automerge = false
664731
}
665732
if batch.Status == orchestrator_scheduler.BatchJobSucceeded && batch.BatchType == orchestrator_scheduler.DiggerCommandApply && automerge == true {
666-
//ghService, _, err := utils.GetGithubService(
667-
// gh,
668-
// batch.GithubInstallationId,
669-
// batch.RepoFullName,
670-
// batch.RepoOwner,
671-
// batch.RepoName,
672-
//)
673733
prService, err := GetPrServiceFromBatch(batch, gh)
674734
if err != nil {
675735
log.Printf("Error getting github service: %v", err)

backend/migrations/20241229112312.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "digger_batches" table
2+
ALTER TABLE "public"."digger_batches" ADD COLUMN "ai_summary_comment_id" text NULL, ADD COLUMN "report_terraform_outputs" boolean NULL;

backend/migrations/atlas.sum

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:+aJac+1voE00wJ5clz52DJ/fLljzr4l1KNsP8e3ZZYA=
1+
h1:kHWmqYJMe9ZbdcztvM02SVEs5lyw/RFbKzZmqgbmSIk=
22
20231227132525.sql h1:43xn7XC0GoJsCnXIMczGXWis9d504FAWi4F1gViTIcw=
33
20240115170600.sql h1:IW8fF/8vc40+eWqP/xDK+R4K9jHJ9QBSGO6rN9LtfSA=
44
20240116123649.sql h1:R1JlUIgxxF6Cyob9HdtMqiKmx/BfnsctTl5rvOqssQw=
@@ -34,3 +34,4 @@ h1:+aJac+1voE00wJ5clz52DJ/fLljzr4l1KNsP8e3ZZYA=
3434
20241107163722.sql h1:D5+D4TJxs80GIYrKbf70P1Vc2FUuiFVGxlu424kxYbg=
3535
20241107172343.sql h1:E1j+7R5TZlyCKEpyYmH1mJ2zh+y5hVbtQ/PuEMJR7us=
3636
20241114202249.sql h1:P2DhJK8MLe8gSAAz+Y5KNmsvKVw8KfLQPCncynYXEfM=
37+
20241229112312.sql h1:Fr06uwt7LcQoLh6bjGzKB+uy9i8+uk8m6jfi+OBBbP4=

backend/models/scheduler.go

+15-13
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ const DiggerVCSGithub DiggerVCSType = "github"
2222
const DiggerVCSGitlab DiggerVCSType = "gitlab"
2323

2424
type DiggerBatch struct {
25-
ID uuid.UUID `gorm:"primary_key"`
26-
VCS DiggerVCSType
27-
PrNumber int
28-
CommentId *int64
29-
Status orchestrator_scheduler.DiggerBatchStatus
30-
BranchName string
31-
DiggerConfig string
32-
GithubInstallationId int64
33-
GitlabProjectId int
34-
RepoFullName string
35-
RepoOwner string
36-
RepoName string
37-
BatchType orchestrator_scheduler.DiggerCommand
25+
ID uuid.UUID `gorm:"primary_key"`
26+
VCS DiggerVCSType
27+
PrNumber int
28+
CommentId *int64
29+
AiSummaryCommentId string
30+
Status orchestrator_scheduler.DiggerBatchStatus
31+
BranchName string
32+
DiggerConfig string
33+
GithubInstallationId int64
34+
GitlabProjectId int
35+
RepoFullName string
36+
RepoOwner string
37+
RepoName string
38+
BatchType orchestrator_scheduler.DiggerCommand
39+
ReportTerraformOutputs bool
3840
// used for module source grouping comments
3941
SourceDetails []byte
4042
}

backend/models/storage.go

+16-14
Original file line numberDiff line numberDiff line change
@@ -617,22 +617,24 @@ func (db *Database) GetDiggerBatch(batchId *uuid.UUID) (*DiggerBatch, error) {
617617
return batch, nil
618618
}
619619

620-
func (db *Database) CreateDiggerBatch(vcsType DiggerVCSType, githubInstallationId int64, repoOwner string, repoName string, repoFullname string, PRNumber int, diggerConfig string, branchName string, batchType scheduler.DiggerCommand, commentId *int64, gitlabProjectId int) (*DiggerBatch, error) {
620+
func (db *Database) CreateDiggerBatch(vcsType DiggerVCSType, githubInstallationId int64, repoOwner string, repoName string, repoFullname string, PRNumber int, diggerConfig string, branchName string, batchType scheduler.DiggerCommand, commentId *int64, gitlabProjectId int, aiSummaryCommentId string, reportTerraformOutputs bool) (*DiggerBatch, error) {
621621
uid := uuid.New()
622622
batch := &DiggerBatch{
623-
ID: uid,
624-
VCS: vcsType,
625-
GithubInstallationId: githubInstallationId,
626-
RepoOwner: repoOwner,
627-
RepoName: repoName,
628-
RepoFullName: repoFullname,
629-
PrNumber: PRNumber,
630-
CommentId: commentId,
631-
Status: scheduler.BatchJobCreated,
632-
BranchName: branchName,
633-
DiggerConfig: diggerConfig,
634-
BatchType: batchType,
635-
GitlabProjectId: gitlabProjectId,
623+
ID: uid,
624+
VCS: vcsType,
625+
GithubInstallationId: githubInstallationId,
626+
RepoOwner: repoOwner,
627+
RepoName: repoName,
628+
RepoFullName: repoFullname,
629+
PrNumber: PRNumber,
630+
CommentId: commentId,
631+
Status: scheduler.BatchJobCreated,
632+
BranchName: branchName,
633+
DiggerConfig: diggerConfig,
634+
BatchType: batchType,
635+
GitlabProjectId: gitlabProjectId,
636+
AiSummaryCommentId: aiSummaryCommentId,
637+
ReportTerraformOutputs: reportTerraformOutputs,
636638
}
637639
result := db.GormDB.Save(batch)
638640
if result.Error != nil {

backend/models/storage_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func TestGetDiggerJobsForBatchPreloadsSummary(t *testing.T) {
151151
resourcesUpdated := uint(2)
152152
resourcesDeleted := uint(3)
153153

154-
batch, err := DB.CreateDiggerBatch(DiggerVCSGithub, 123, repoOwner, repoName, repoFullName, prNumber, diggerconfig, branchName, batchType, &commentId, 0)
154+
batch, err := DB.CreateDiggerBatch(DiggerVCSGithub, 123, repoOwner, repoName, repoFullName, prNumber, diggerconfig, branchName, batchType, &commentId, 0, "", false)
155155
assert.NoError(t, err)
156156

157157
job, err := DB.CreateDiggerJob(batch.ID, []byte(jobSpec), "workflow_file.yml")

backend/services/spec.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) {
116116
CommentId: strconv.FormatInt(*batch.CommentId, 10),
117117
Job: jobSpec,
118118
Reporter: spec.ReporterSpec{
119-
ReportingStrategy: "comments_per_run",
120-
ReporterType: "lazy",
119+
ReportingStrategy: "comments_per_run",
120+
ReporterType: "lazy",
121+
ReportTerraformOutput: batch.ReportTerraformOutputs,
121122
},
122123
Lock: spec.LockSpec{
123124
LockType: "noop",

backend/tasks/runs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func TestThatRunQueueItemMovesFromQueuedToPlanningAfterPickup(t *testing.T) {
143143

144144
for i, testParam := range testParameters {
145145
ciService := github2.MockCiService{}
146-
batch, _ := models.DB.CreateDiggerBatch(models.DiggerVCSGithub, 123, "", "", "", 22, "", "", "", nil, 0)
146+
batch, _ := models.DB.CreateDiggerBatch(models.DiggerVCSGithub, 123, "", "", "", 22, "", "", "", nil, 0, "", false)
147147
project, _ := models.DB.CreateProject(fmt.Sprintf("test%v", i), nil, nil, false, false)
148148
planStage, _ := models.DB.CreateDiggerRunStage(batch.ID.String())
149149
applyStage, _ := models.DB.CreateDiggerRunStage(batch.ID.String())

0 commit comments

Comments
 (0)