diff --git a/backend/controllers/github.go b/backend/controllers/github.go index a91297bb9..60f42deb0 100644 --- a/backend/controllers/github.go +++ b/backend/controllers/github.go @@ -34,9 +34,7 @@ import ( "net/http" "net/url" "os" - "path/filepath" "reflect" - "runtime/debug" "slices" "strconv" "strings" @@ -430,6 +428,7 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR return fmt.Errorf("error processing event") } } + diggerCommand, err := orchestrator_scheduler.GetCommandFromJob(jobsForImpactedProjects[0]) if err != nil { log.Printf("could not determine digger command from job: %v", jobsForImpactedProjects[0].Commands) @@ -523,6 +522,15 @@ func handlePullRequestEvent(gh utils.GithubClientProvider, payload *github.PullR } batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs) + + placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder") + if err != nil { + log.Printf("strconv.ParseInt error: %v", err) + commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err)) + return fmt.Errorf("comment reporter error: %v", err) + } + + batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGithub, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, installationId, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, &placeholderComment.Id, diggerYmlStr, 0) if err != nil { log.Printf("ConvertJobsToDiggerJobs error: %v", err) commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err)) @@ -931,6 +939,14 @@ func handleIssueCommentEvent(gh utils.GithubClientProvider, payload *github.Issu } batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, diggerYmlStr, 0, aiSummaryCommentId, config.ReportTerraformOutputs) + placeholderComment, err := ghService.PublishComment(issueNumber, "digger report placehoder") + if err != nil { + log.Printf("strconv.ParseInt error: %v", err) + commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err)) + return fmt.Errorf("comment reporter error: %v", err) + } + + batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, *branch, issueNumber, repoOwner, repoName, repoFullName, *commitSha, reporterCommentId, &placeholderComment.Id, diggerYmlStr, 0) if err != nil { log.Printf("ConvertJobsToDiggerJobs error: %v", err) commentReporterManager.UpdateComment(fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err)) diff --git a/backend/migrations/20250102194016.sql b/backend/migrations/20250102194016.sql new file mode 100644 index 000000000..8fcc81ecf --- /dev/null +++ b/backend/migrations/20250102194016.sql @@ -0,0 +1,2 @@ +-- Modify "digger_batches" table +ALTER TABLE "public"."digger_batches" ADD COLUMN "placeholder_comment_id_for_report" text NULL; diff --git a/backend/migrations/atlas.sum b/backend/migrations/atlas.sum index 4b13a3e4e..a1afdb4cf 100644 --- a/backend/migrations/atlas.sum +++ b/backend/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:kHWmqYJMe9ZbdcztvM02SVEs5lyw/RFbKzZmqgbmSIk= +h1:DtynCDyaa2VQhu0lgQmvTIZXoG31raLDJNBIyEWKRwg= 20231227132525.sql h1:43xn7XC0GoJsCnXIMczGXWis9d504FAWi4F1gViTIcw= 20240115170600.sql h1:IW8fF/8vc40+eWqP/xDK+R4K9jHJ9QBSGO6rN9LtfSA= 20240116123649.sql h1:R1JlUIgxxF6Cyob9HdtMqiKmx/BfnsctTl5rvOqssQw= @@ -35,3 +35,4 @@ h1:kHWmqYJMe9ZbdcztvM02SVEs5lyw/RFbKzZmqgbmSIk= 20241107172343.sql h1:E1j+7R5TZlyCKEpyYmH1mJ2zh+y5hVbtQ/PuEMJR7us= 20241114202249.sql h1:P2DhJK8MLe8gSAAz+Y5KNmsvKVw8KfLQPCncynYXEfM= 20241229112312.sql h1:Fr06uwt7LcQoLh6bjGzKB+uy9i8+uk8m6jfi+OBBbP4= +20250102194016.sql h1:Q6nnbPO/zA0PTdZXowv51sL0aUga1b8dA0yfVHgmAZs= diff --git a/backend/models/scheduler.go b/backend/models/scheduler.go index c371b106f..3ad780000 100644 --- a/backend/models/scheduler.go +++ b/backend/models/scheduler.go @@ -22,20 +22,21 @@ const DiggerVCSGithub DiggerVCSType = "github" const DiggerVCSGitlab DiggerVCSType = "gitlab" type DiggerBatch struct { - ID uuid.UUID `gorm:"primary_key"` - VCS DiggerVCSType - PrNumber int - CommentId *int64 + ID uuid.UUID `gorm:"primary_key"` + VCS DiggerVCSType + PrNumber int + CommentId *int64 AiSummaryCommentId string - Status orchestrator_scheduler.DiggerBatchStatus - BranchName string - DiggerConfig string - GithubInstallationId int64 - GitlabProjectId int - RepoFullName string - RepoOwner string - RepoName string - BatchType orchestrator_scheduler.DiggerCommand + PlaceholderCommentIdForReport *string + Status orchestrator_scheduler.DiggerBatchStatus + BranchName string + DiggerConfig string + GithubInstallationId int64 + GitlabProjectId int + RepoFullName string + RepoOwner string + RepoName string + BatchType orchestrator_scheduler.DiggerCommand ReportTerraformOutputs bool // used for module source grouping comments SourceDetails []byte diff --git a/backend/models/storage.go b/backend/models/storage.go index 146ada683..8f825e210 100644 --- a/backend/models/storage.go +++ b/backend/models/storage.go @@ -617,7 +617,7 @@ func (db *Database) GetDiggerBatch(batchId *uuid.UUID) (*DiggerBatch, error) { return batch, nil } -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) { +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, placeholderCommentIdForReport *string, gitlabProjectId int, aiSummaryCommentId string, reportTerraformOutputs bool) (*DiggerBatch, error) { uid := uuid.New() batch := &DiggerBatch{ ID: uid, @@ -628,6 +628,7 @@ func (db *Database) CreateDiggerBatch(vcsType DiggerVCSType, githubInstallationI RepoFullName: repoFullname, PrNumber: PRNumber, CommentId: commentId, + PlaceholderCommentIdForReport: placeholderCommentIdForReport, Status: scheduler.BatchJobCreated, BranchName: branchName, DiggerConfig: diggerConfig, diff --git a/backend/services/spec.go b/backend/services/spec.go index 152f7199c..1469d74b6 100644 --- a/backend/services/spec.go +++ b/backend/services/spec.go @@ -116,9 +116,9 @@ func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { CommentId: strconv.FormatInt(*batch.CommentId, 10), Job: jobSpec, Reporter: spec.ReporterSpec{ - ReportingStrategy: "comments_per_run", - ReporterType: "lazy", - ReportTerraformOutput: batch.ReportTerraformOutputs, + ReportingStrategy: "comments_per_run", + ReporterType: "basic", + ReportCommentId: job.Batch.PlaceholderCommentIdForReport, }, Lock: spec.LockSpec{ LockType: "noop", diff --git a/backend/utils/graphs.go b/backend/utils/graphs.go index 227c31907..aeac939c2 100644 --- a/backend/utils/graphs.go +++ b/backend/utils/graphs.go @@ -14,7 +14,7 @@ import ( ) // ConvertJobsToDiggerJobs jobs is map with project name as a key and a Job as a value -func ConvertJobsToDiggerJobs(jobType scheduler.DiggerCommand, vcsType models.DiggerVCSType, organisationId uint, jobsMap map[string]scheduler.Job, projectMap map[string]configuration.Project, projectsGraph graph.Graph[string, configuration.Project], githubInstallationId int64, branch string, prNumber int, repoOwner string, repoName string, repoFullName string, commitSha string, commentId int64, diggerConfigStr string, gitlabProjectId int, aiSummaryCommentId string, reportTerraformOutput bool) (*uuid.UUID, map[string]*models.DiggerJob, error) { +func ConvertJobsToDiggerJobs(jobType scheduler.DiggerCommand, vcsType models.DiggerVCSType, organisationId uint, jobsMap map[string]scheduler.Job, projectMap map[string]configuration.Project, projectsGraph graph.Graph[string, configuration.Project], githubInstallationId int64, branch string, prNumber int, repoOwner string, repoName string, repoFullName string, commitSha string, commentId int64, placeholderCommentIdForReport *string, diggerConfigStr string, gitlabProjectId int, aiSummaryCommentId string, reportTerraformOutput bool) (*uuid.UUID, map[string]*models.DiggerJob, error) { result := make(map[string]*models.DiggerJob) organisation, err := models.DB.GetOrganisationById(organisationId) if err != nil { @@ -43,7 +43,7 @@ func ConvertJobsToDiggerJobs(jobType scheduler.DiggerCommand, vcsType models.Dig log.Printf("marshalledJobsMap: %v\n", marshalledJobsMap) - batch, err := models.DB.CreateDiggerBatch(vcsType, githubInstallationId, repoOwner, repoName, repoFullName, prNumber, diggerConfigStr, branch, jobType, &commentId, gitlabProjectId, aiSummaryCommentId, reportTerraformOutput) + batch, err := models.DB.CreateDiggerBatch(vcsType, githubInstallationId, repoOwner, repoName, repoFullName, prNumber, diggerConfigStr, branch, jobType, &commentId, placeholderCommentIdForReport, gitlabProjectId, aiSummaryCommentId, reportTerraformOutput) if err != nil { return nil, nil, fmt.Errorf("failed to create batch: %v", err) } diff --git a/cli/cmd/digger/main_test.go b/cli/cmd/digger/main_test.go index 6b3e5352f..fa2e190a8 100644 --- a/cli/cmd/digger/main_test.go +++ b/cli/cmd/digger/main_test.go @@ -895,8 +895,11 @@ func TestGitHubNewPullRequestContext(t *testing.T) { impactedProjects, requestedProject, prNumber, err := dggithub.ProcessGitHubEvent(ghEvent, &diggerConfig, &prManager) reporter := &reporting.CiReporter{ - CiService: &prManager, - PrNumber: prNumber, + CiService: &prManager, + PrNumber: prNumber, + IsSupportMarkdown: true, + IsSuppressed: false, + ReportStrategy: reporting.NewMultipleCommentsStrategy(), } event := context.Event.(github.PullRequestEvent) @@ -923,8 +926,11 @@ func TestGitHubNewCommentContext(t *testing.T) { planStorage := storage.MockPlanStorage{} impactedProjects, requestedProject, prNumber, err := dggithub.ProcessGitHubEvent(ghEvent, &diggerConfig, &prManager) reporter := &reporting.CiReporter{ - CiService: &prManager, - PrNumber: prNumber, + CiService: &prManager, + PrNumber: prNumber, + ReportStrategy: reporting.NewMultipleCommentsStrategy(), + IsSuppressed: false, + IsSupportMarkdown: true, } policyChecker := policy.MockPolicyChecker{} diff --git a/cli/cmd/digger/root.go b/cli/cmd/digger/root.go index 8b6481a71..5afb3c551 100644 --- a/cli/cmd/digger/root.go +++ b/cli/cmd/digger/root.go @@ -14,7 +14,6 @@ import ( "log" "net/http" "os" - "time" ) type RunConfig struct { @@ -93,15 +92,11 @@ func PreRun(cmd *cobra.Command, args []string) { //PolicyChecker = policy.NewPolicyChecker(hostName, orgName, token) if os.Getenv("REPORTING_STRATEGY") == "comments_per_run" || os.Getenv("ACCUMULATE_PLANS") == "true" { - ReportStrategy = &reporting.CommentPerRunStrategy{ - TimeOfRun: time.Now(), - } - } else if os.Getenv("REPORTING_STRATEGY") == "latest_run_comment" { - ReportStrategy = &reporting.LatestRunCommentStrategy{ - TimeOfRun: time.Now(), - } + strategy := reporting.NewSingleCommentStrategy() + ReportStrategy = &strategy } else { - ReportStrategy = &reporting.MultipleCommentsStrategy{} + strategy := reporting.NewMultipleCommentsStrategy() + ReportStrategy = &strategy } var err error diff --git a/cli/pkg/digger/digger.go b/cli/pkg/digger/digger.go index b7c7a1e33..3179e0510 100644 --- a/cli/pkg/digger/digger.go +++ b/cli/pkg/digger/digger.go @@ -124,13 +124,14 @@ func RunJobs(jobs []orchestrator.Job, prService ci.PullRequestService, orgServic } if allAppliesSuccess == true && reportFinalStatusToBackend == true { - _, jobPrCommentUrl, err := reporter.Flush() + _, jobPrCommentUrls, err := reporter.Flush() if err != nil { log.Printf("error while sending job comments %v", err) return false, false, fmt.Errorf("error while sending job comments %v", err) } currentJob := jobs[0] + jobPrCommentUrl := jobPrCommentUrls[0] repoNameForBackendReporting := strings.ReplaceAll(currentJob.Namespace, "/", "-") projectNameForBackendReporting := currentJob.ProjectName // TODO: handle the apply result summary as well to report it to backend. Possibly reporting changed resources as well @@ -170,12 +171,12 @@ func RunJobs(jobs []orchestrator.Job, prService ci.PullRequestService, orgServic func reportPolicyError(projectName string, command string, requestedBy string, reporter reporting.Reporter) string { msg := fmt.Sprintf("User %s is not allowed to perform action: %s. Check your policies :x:", requestedBy, command) if reporter.SupportsMarkdown() { - _, _, err := reporter.Report(msg, coreutils.AsCollapsibleComment(fmt.Sprintf("Policy violation for %v - %v", projectName, command), false)) + err := reporter.Report(projectName, msg, coreutils.AsCollapsibleComment(fmt.Sprintf("Policy violation for %v - %v", projectName, command), false)) if err != nil { log.Printf("Error publishing comment: %v", err) } } else { - _, _, err := reporter.Report(msg, coreutils.AsComment(fmt.Sprintf("Policy violation for %v - %v", projectName, command))) + err := reporter.Report(projectName, msg, coreutils.AsComment(fmt.Sprintf("Policy violation for %v - %v", projectName, command))) if err != nil { log.Printf("Error publishing comment: %v", err) } @@ -284,7 +285,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org return nil, msg, fmt.Errorf(msg) } else if planPerformed { if isNonEmptyPlan { - reportTerraformPlanOutput(reporter, projectLock.LockId(), plan) + reportTerraformPlanOutput(reporter, job.ProjectName, plan) planIsAllowed, messages, err := policyChecker.CheckPlanPolicy(SCMrepository, SCMOrganisation, job.ProjectName, job.ProjectDir, planJsonOutput) if err != nil { msg := fmt.Sprintf("Failed to validate plan. %v", err) @@ -311,7 +312,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org preformattedMessaged = append(preformattedMessaged, fmt.Sprintf(" %v", message)) } planReportMessage = planReportMessage + strings.Join(preformattedMessaged, "
") - _, _, err = reporter.Report(planReportMessage, planPolicyFormatter) + err = reporter.Report(job.ProjectName, planReportMessage, planPolicyFormatter) if err != nil { log.Printf("Failed to report plan. %v", err) @@ -320,14 +321,14 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org log.Printf(msg) return nil, msg, fmt.Errorf(msg) } else { - _, _, err := reporter.Report("Terraform plan validation checks succeeded :white_check_mark:", planPolicyFormatter) + err := reporter.Report(job.ProjectName, "Terraform plan validation checks succeeded :white_check_mark:", planPolicyFormatter) if err != nil { log.Printf("Failed to report plan. %v", err) } - reportPlanSummary(reporter, planSummary) + reportPlanSummary(job.ProjectName, reporter, planSummary) } } else { - reportEmptyPlanOutput(reporter, projectLock.LockId()) + reportEmptyPlanOutput(job.ProjectName, reporter, projectLock.LockId()) } err := prService.SetStatus(*job.PullRequestNumber, "success", job.ProjectName+"/plan") if err != nil { @@ -370,7 +371,7 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org } log.Printf("PR status, mergeable: %v, merged: %v and skipMergeCheck %v\n", isMergeable, isMerged, job.SkipMergeCheck) if !isMergeable && !isMerged && !job.SkipMergeCheck { - comment := reportApplyMergeabilityError(reporter) + comment := reportApplyMergeabilityError(job.ProjectName, reporter) prService.SetStatus(*job.PullRequestNumber, "failure", job.ProjectName+"/apply") return nil, comment, fmt.Errorf(comment) @@ -491,17 +492,17 @@ func run(command string, job orchestrator.Job, policyChecker policy.Checker, org return &execution.DiggerExecutorResult{}, "", nil } -func reportApplyMergeabilityError(reporter reporting.Reporter) string { +func reportApplyMergeabilityError(projectName string, reporter reporting.Reporter) string { comment := "cannot perform Apply since the PR is not currently mergeable" log.Println(comment) if reporter.SupportsMarkdown() { - _, _, err := reporter.Report(comment, coreutils.AsCollapsibleComment("Apply error", false)) + err := reporter.Report(projectName, comment, coreutils.AsCollapsibleComment("Apply error", false)) if err != nil { log.Printf("error publishing comment: %v\n", err) } } else { - _, _, err := reporter.Report(comment, coreutils.AsComment("Apply error")) + err := reporter.Report(projectName, comment, coreutils.AsComment("Apply error")) if err != nil { log.Printf("error publishing comment: %v\n", err) } @@ -509,7 +510,7 @@ func reportApplyMergeabilityError(reporter reporting.Reporter) string { return comment } -func reportTerraformPlanOutput(reporter reporting.Reporter, projectId string, plan string) { +func reportTerraformPlanOutput(reporter reporting.Reporter, projectName string, plan string) { var formatter func(string) string if reporter.SupportsMarkdown() { @@ -518,13 +519,13 @@ func reportTerraformPlanOutput(reporter reporting.Reporter, projectId string, pl formatter = coreutils.GetTerraformOutputAsComment("Plan output") } - _, _, err := reporter.Report(plan, formatter) + err := reporter.Report(projectName, plan, formatter) if err != nil { log.Printf("Failed to report plan. %v", err) } } -func reportPlanSummary(reporter reporting.Reporter, summary string) { +func reportPlanSummary(projectName string, reporter reporting.Reporter, summary string) { var formatter func(string) string if reporter.SupportsMarkdown() { @@ -533,19 +534,19 @@ func reportPlanSummary(reporter reporting.Reporter, summary string) { formatter = coreutils.AsComment("Plan summary") } - _, _, err := reporter.Report("\n"+summary, formatter) + err := reporter.Report(projectName, "\n"+summary, formatter) if err != nil { log.Printf("Failed to report plan summary. %v", err) } } -func reportEmptyPlanOutput(reporter reporting.Reporter, projectId string) { +func reportEmptyPlanOutput(projectName string, reporter reporting.Reporter, projectId string) { identityFormatter := func(comment string) string { return comment } - _, _, err := reporter.Report("→ No changes in terraform output for "+projectId, identityFormatter) + err := reporter.Report(projectName, "→ No changes in terraform output for "+projectId, identityFormatter) // suppress the comment (if reporter is suppressible) - reporter.Suppress() + reporter.Suppress("") if err != nil { log.Printf("Failed to report plan. %v", err) } diff --git a/cli/pkg/digger/digger_test.go b/cli/pkg/digger/digger_test.go index aaba37658..0361a3112 100644 --- a/cli/pkg/digger/digger_test.go +++ b/cli/pkg/digger/digger_test.go @@ -86,7 +86,12 @@ func (m *MockPRManager) GetApprovals(prNumber int) ([]string, error) { func (m *MockPRManager) PublishComment(prNumber int, comment string) (*ci.Comment, error) { m.Commands = append(m.Commands, RunInfo{"PublishComment", strconv.Itoa(prNumber) + " " + comment, time.Now()}) - return nil, nil + return &ci.Comment{ + Id: "", + DiscussionId: "", + Body: nil, + Url: "", + }, nil } func (m *MockPRManager) ListIssues() ([]*ci.Issue, error) { @@ -241,16 +246,16 @@ func (m MockPlanPathProvider) LocalPlanFilePath() string { } func TestCorrectCommandExecutionWhenApplying(t *testing.T) { - commandRunner := &MockCommandRunner{} terraformExecutor := &MockTerraformExecutor{} prManager := &MockPRManager{} lock := &MockProjectLock{} planStorage := &MockPlanStorage{} + strategy := reporting.NewMultipleCommentsStrategy() reporter := &reporting.CiReporter{ CiService: prManager, PrNumber: 1, - ReportStrategy: &reporting.MultipleCommentsStrategy{}, + ReportStrategy: &strategy, IsSupportMarkdown: true, } planPathProvider := &MockPlanPathProvider{} @@ -287,7 +292,7 @@ func TestCorrectCommandExecutionWhenApplying(t *testing.T) { commandStrings := allCommandsInOrderWithParams(terraformExecutor, commandRunner, prManager, lock, planStorage, planPathProvider) - assert.Equal(t, []string{"RetrievePlan plan", "Init ", "Apply ", "PublishComment 1
Apply output\n\n```terraform\n\n```\n
", "Run echo"}, commandStrings) + assert.Equal(t, []string{"RetrievePlan plan", "Init ", "Apply ", "Run echo"}, commandStrings) } func TestCorrectCommandExecutionWhenDestroying(t *testing.T) { @@ -297,10 +302,11 @@ func TestCorrectCommandExecutionWhenDestroying(t *testing.T) { prManager := &MockPRManager{} lock := &MockProjectLock{} planStorage := &MockPlanStorage{} + strategy := reporting.NewMultipleCommentsStrategy() reporter := &reporting.CiReporter{ CiService: prManager, PrNumber: 1, - ReportStrategy: &reporting.MultipleCommentsStrategy{}, + ReportStrategy: &strategy, } planPathProvider := &MockPlanPathProvider{} executor := execution.DiggerExecutor{ diff --git a/cli/pkg/integration/integration_test.go b/cli/pkg/integration/integration_test.go index 9c45bf4a2..d9379b4b3 100644 --- a/cli/pkg/integration/integration_test.go +++ b/cli/pkg/integration/integration_test.go @@ -44,10 +44,11 @@ func getProjectLockForTests() (error, *locking.PullRequestLock) { repositoryName := "test_dynamodb_lock" ghToken := "token" githubPrService, _ := dg_github.GithubServiceProviderBasic{}.NewService(ghToken, repositoryName, repoOwner) + strategy := reporting.NewMultipleCommentsStrategy() reporter := reporting.CiReporter{ CiService: &githubPrService, PrNumber: 1, - ReportStrategy: &reporting.MultipleCommentsStrategy{}, + ReportStrategy: &strategy, } projectLock := &locking.PullRequestLock{ diff --git a/ee/backend/controllers/gitlab.go b/ee/backend/controllers/gitlab.go index e7c71aa24..cbd58664d 100644 --- a/ee/backend/controllers/gitlab.go +++ b/ee/backend/controllers/gitlab.go @@ -333,7 +333,7 @@ func handlePullRequestEvent(gitlabProvider utils.GitlabProvider, payload *gitlab log.Printf("strconv.ParseInt error: %v", err) utils.InitCommentReporter(glService, prNumber, fmt.Sprintf(":x: could not handle commentId: %v", err)) } - batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGitlab, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, 0, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, diggeryamlStr, projectId, "", false) + batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGitlab, organisationId, impactedJobsMap, impactedProjectsMap, projectsGraph, 0, branch, prNumber, repoOwner, repoName, repoFullName, commitSha, commentId, nil, diggeryamlStr, projectId, "", false) if err != nil { log.Printf("ConvertJobsToDiggerJobs error: %v", err) utils.InitCommentReporter(glService, prNumber, fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err)) @@ -524,7 +524,7 @@ func handleIssueCommentEvent(gitlabProvider utils.GitlabProvider, payload *gitla log.Printf("ParseInt err: %v", err) return fmt.Errorf("parseint error: %v", err) } - batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGitlab, organisationId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, 0, branch, issueNumber, repoOwner, repoName, repoFullName, commitSha, commentId64, diggerYmlStr, projectId, "", false) + batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, models.DiggerVCSGitlab, organisationId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, 0, branch, issueNumber, repoOwner, repoName, repoFullName, commitSha, commentId64, nil, diggerYmlStr, projectId, "", false) if err != nil { log.Printf("ConvertJobsToDiggerJobs error: %v", err) utils.InitCommentReporter(glService, issueNumber, fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err)) diff --git a/ee/backend/hooks/github.go b/ee/backend/hooks/github.go index d6fb07dc7..6f5f0fb61 100644 --- a/ee/backend/hooks/github.go +++ b/ee/backend/hooks/github.go @@ -151,7 +151,7 @@ var DriftReconcilliationHook ce_controllers.IssueCommentHook = func(gh utils.Git utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: could not handle commentId: %v", err)) } - batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, defaultBranch, issueNumber, repoOwner, repoName, repoFullName, "", reporterCommentId, diggerYmlStr, 0, "", false) + batchId, _, err := utils.ConvertJobsToDiggerJobs(*diggerCommand, "github", orgId, impactedProjectsJobMap, impactedProjectsMap, projectsGraph, installationId, defaultBranch, issueNumber, repoOwner, repoName, repoFullName, "", reporterCommentId, nil, diggerYmlStr, 0, "", false) if err != nil { log.Printf("ConvertJobsToDiggerJobs error: %v", err) utils.InitCommentReporter(ghService, issueNumber, fmt.Sprintf(":x: ConvertJobsToDiggerJobs error: %v", err)) diff --git a/ee/cli/cmd/digger/root.go b/ee/cli/cmd/digger/root.go index c89d932c2..3b2bba19f 100644 --- a/ee/cli/cmd/digger/root.go +++ b/ee/cli/cmd/digger/root.go @@ -14,7 +14,6 @@ import ( "log" "net/http" "os" - "time" ) type RunConfig struct { @@ -87,15 +86,11 @@ func PreRun(cmd *cobra.Command, args []string) { BackendApi = NewBackendApi(hostName, token) if os.Getenv("REPORTING_STRATEGY") == "comments_per_run" || os.Getenv("ACCUMULATE_PLANS") == "true" { - ReportStrategy = &reporting.CommentPerRunStrategy{ - TimeOfRun: time.Now(), - } - } else if os.Getenv("REPORTING_STRATEGY") == "latest_run_comment" { - ReportStrategy = &reporting.LatestRunCommentStrategy{ - TimeOfRun: time.Now(), - } + strategy := reporting.NewSingleCommentStrategy() + ReportStrategy = &strategy } else { - ReportStrategy = &reporting.MultipleCommentsStrategy{} + strategy := reporting.NewMultipleCommentsStrategy() + ReportStrategy = &strategy } var err error diff --git a/libs/comment_utils/reporting/core.go b/libs/comment_utils/reporting/core.go index adfdbdce1..9dd5bcdef 100644 --- a/libs/comment_utils/reporting/core.go +++ b/libs/comment_utils/reporting/core.go @@ -1,8 +1,8 @@ package reporting type Reporter interface { - Report(report string, reportFormatting func(report string) string) (commentId string, commentUrl string, error error) - Flush() (string, string, error) - Suppress() error + Report(projectName string, report string, reportFormatting func(report string) string) (error error) + Flush() ([]string, []string, error) + Suppress(projectName string) error SupportsMarkdown() bool } diff --git a/libs/comment_utils/reporting/mock.go b/libs/comment_utils/reporting/mock.go index b68c9eb28..6d7e4d943 100644 --- a/libs/comment_utils/reporting/mock.go +++ b/libs/comment_utils/reporting/mock.go @@ -4,16 +4,16 @@ type MockReporter struct { commands []string } -func (mockReporter *MockReporter) Report(report string, reportFormatting func(report string) string) (string, string, error) { +func (mockReporter *MockReporter) Report(projectName string, report string, reportFormatting func(report string) string) error { mockReporter.commands = append(mockReporter.commands, "Report") - return "", "", nil + return nil } -func (mockReporter *MockReporter) Flush() (string, string, error) { - return "", "", nil +func (mockReporter *MockReporter) Flush() ([]string, []string, error) { + return []string{}, []string{}, nil } -func (mockReporter *MockReporter) Suppress() error { +func (mockReporter *MockReporter) Suppress(string) error { return nil } diff --git a/libs/comment_utils/reporting/noop.go b/libs/comment_utils/reporting/noop.go index d0d68462d..db92a00fd 100644 --- a/libs/comment_utils/reporting/noop.go +++ b/libs/comment_utils/reporting/noop.go @@ -2,18 +2,18 @@ package reporting type NoopReporter struct{} -func (reporter NoopReporter) Report(report string, reportFormatting func(report string) string) (string, string, error) { - return "", "", nil +func (reporter NoopReporter) Report(projectName string, report string, reportFormatting func(report string) string) error { + return nil } -func (reporter NoopReporter) Flush() (string, string, error) { - return "", "", nil +func (reporter NoopReporter) Flush() ([]string, []string, error) { + return []string{}, []string{}, nil } func (reporter NoopReporter) SupportsMarkdown() bool { return false } -func (reporter NoopReporter) Suppress() error { +func (reporter NoopReporter) Suppress(string) error { return nil } diff --git a/libs/comment_utils/reporting/reporting.go b/libs/comment_utils/reporting/reporting.go index 927bb44d6..e69773fec 100644 --- a/libs/comment_utils/reporting/reporting.go +++ b/libs/comment_utils/reporting/reporting.go @@ -5,7 +5,6 @@ import ( "github.com/diggerhq/digger/libs/ci" "github.com/diggerhq/digger/libs/comment_utils/utils" "log" - "strings" "time" ) @@ -13,189 +12,177 @@ type CiReporter struct { CiService ci.PullRequestService PrNumber int IsSupportMarkdown bool + IsSuppressed bool ReportStrategy ReportStrategy } -func (ciReporter CiReporter) Report(report string, reportFormatting func(report string) string) (string, string, error) { - commentId, commentUrl, err := ciReporter.ReportStrategy.Report(ciReporter.CiService, ciReporter.PrNumber, report, reportFormatting, ciReporter.SupportsMarkdown()) - return commentId, commentUrl, err +func (ciReporter CiReporter) Report(projectName string, report string, reportFormatting func(report string) string) error { + err := ciReporter.ReportStrategy.Report(projectName, report, reportFormatting) + return err } -func (ciReporter CiReporter) Flush() (string, string, error) { - return "", "", nil +func (ciReporter CiReporter) Flush() ([]string, []string, error) { + commentIds, commentUrls, err := ciReporter.ReportStrategy.Flush(ciReporter.CiService, ciReporter.PrNumber, ciReporter.IsSupportMarkdown) + return commentIds, commentUrls, err } -func (ciReporter CiReporter) Suppress() error { - return nil +func (ciReporter CiReporter) Suppress(projectName string) error { + return ciReporter.ReportStrategy.Suppress(projectName) } func (ciReporter CiReporter) SupportsMarkdown() bool { return ciReporter.IsSupportMarkdown } -type CiReporterLazy struct { - CiReporter CiReporter - isSuppressed bool - reports []string - formatters []func(report string) string -} - -func NewCiReporterLazy(ciReporter CiReporter) *CiReporterLazy { - return &CiReporterLazy{ - CiReporter: ciReporter, - isSuppressed: false, - reports: []string{}, - formatters: []func(report string) string{}, - } -} - -func (lazyReporter *CiReporterLazy) Report(report string, reportFormatting func(report string) string) (string, string, error) { - lazyReporter.reports = append(lazyReporter.reports, report) - lazyReporter.formatters = append(lazyReporter.formatters, reportFormatting) - return "", "", nil -} - -func (lazyReporter *CiReporterLazy) Flush() (string, string, error) { - if lazyReporter.isSuppressed { - log.Printf("Reporter is suprresed, ignoring messages ...") - return "", "", nil - } - var commentId, commentUrl string - for i, _ := range lazyReporter.formatters { - var err error - commentId, commentUrl, err = lazyReporter.CiReporter.ReportStrategy.Report(lazyReporter.CiReporter.CiService, lazyReporter.CiReporter.PrNumber, lazyReporter.reports[i], lazyReporter.formatters[i], lazyReporter.SupportsMarkdown()) - if err != nil { - log.Printf("failed to report strategy: ") - return "", "", err - } - } - // clear the buffers - lazyReporter.formatters = []func(comment string) string{} - lazyReporter.reports = []string{} - return commentId, commentUrl, nil -} - -func (lazyReporter *CiReporterLazy) Suppress() error { - lazyReporter.isSuppressed = true - return nil -} - -func (lazyReporter *CiReporterLazy) SupportsMarkdown() bool { - return lazyReporter.CiReporter.IsSupportMarkdown -} - type StdOutReporter struct{} -func (reporter StdOutReporter) Report(report string, reportFormatting func(report string) string) (string, string, error) { +func (reporter StdOutReporter) Report(projectName string, report string, reportFormatting func(report string) string) error { log.Printf("Info: %v", report) - return "", "", nil + return nil } -func (reporter StdOutReporter) Flush() (string, string, error) { - return "", "", nil +func (reporter StdOutReporter) Flush() ([]string, []string, error) { + return []string{}, []string{}, nil } func (reporter StdOutReporter) SupportsMarkdown() bool { return false } -func (reporter StdOutReporter) Suppress() error { +func (reporter StdOutReporter) Suppress(string) error { return nil } type ReportStrategy interface { - Report(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) (commentId string, commentUrl string, error error) + Report(projectName string, report string, reportFormatter func(report string) string) (error error) + Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) (commentId []string, commentUrl []string, error error) + Suppress(projectName string) error } -type CommentPerRunStrategy struct { - Title string - TimeOfRun time.Time +type ReportFormat struct { + Report string + ReportFormatter func(report string) string } -func (strategy CommentPerRunStrategy) Report(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) (string, string, error) { - comments, err := ciService.GetComments(PrNumber) - if err != nil { - return "", "", fmt.Errorf("error getting comments: %v", err) - } +type SingleCommentStrategy struct { + TimeOfRun time.Time + formatters map[string][]ReportFormat +} - var reportTitle string - if strategy.Title != "" { - reportTitle = strategy.Title + " " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)") - } else { - reportTitle = "Digger run report at " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)") +func NewSingleCommentStrategy() SingleCommentStrategy { + return SingleCommentStrategy{ + TimeOfRun: time.Now(), + formatters: make(map[string][]ReportFormat), } - commentId, commentUrl, err := upsertComment(ciService, PrNumber, report, reportFormatter, comments, reportTitle, supportsCollapsibleComment) - return commentId, commentUrl, err -} - -func upsertComment(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, comments []ci.Comment, reportTitle string, supportsCollapsible bool) (string, string, error) { - report = reportFormatter(report) - commentIdForThisRun := "" - var commentBody string - var commentUrl string - for _, comment := range comments { - if strings.Contains(*comment.Body, reportTitle) { - commentIdForThisRun = comment.Id - commentBody = *comment.Body - commentUrl = comment.Url - break - } +} + +func (strategy SingleCommentStrategy) Report(projectName string, report string, reportFormatter func(report string) string) error { + if _, exists := strategy.formatters[projectName]; !exists { + strategy.formatters[projectName] = []ReportFormat{} } + strategy.formatters[projectName] = append(strategy.formatters[projectName], ReportFormat{ + Report: report, + ReportFormatter: reportFormatter, + }) - if commentIdForThisRun == "" { - var commentMessage string - if !supportsCollapsible { - commentMessage = utils.AsComment(reportTitle)(report) - } else { - commentMessage = utils.AsCollapsibleComment(reportTitle, false)(report) + return nil +} + +func (strategy SingleCommentStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) ([]string, []string, error) { + var completeComment = "" + for projectName, projectFormatters := range strategy.formatters { + projectTitle := "report for " + projectName + " " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)") + var projectComment = "" + for _, f := range projectFormatters { + report := f.ReportFormatter(f.Report) + projectComment = projectComment + "\n" + report } - comment, err := ciService.PublishComment(PrNumber, commentMessage) - if err != nil { - return "", "", fmt.Errorf("error publishing comment: %v", err) + if !supportsCollapsibleComment { + projectComment = utils.AsComment(projectTitle)(projectComment) + } else { + projectComment = utils.AsCollapsibleComment(projectTitle, false)(projectComment) } - return fmt.Sprintf("%v", comment.Id), comment.Url, nil + completeComment = completeComment + "\n" + projectComment } - // strip first and last lines - lines := strings.Split(commentBody, "\n") - lines = lines[1 : len(lines)-1] - commentBody = strings.Join(lines, "\n") - - commentBody = commentBody + "\n\n" + report + "\n" - - var completeComment string - if !supportsCollapsible { - completeComment = utils.AsComment(reportTitle)(commentBody) - } else { - completeComment = utils.AsCollapsibleComment(reportTitle, false)(commentBody) + c, err := ciService.PublishComment(PrNumber, completeComment) + if err != nil { + log.Printf("error while publishing reporter comment: %v", err) + return nil, nil, fmt.Errorf("error while publishing reporter comment: %v", err) } + return []string{c.Id}, []string{c.Url}, nil +} - err := ciService.EditComment(PrNumber, commentIdForThisRun, completeComment) +func (strategy SingleCommentStrategy) Suppress(projectName string) error { + // TODO: figure out how to suppress a particular project (probably pop it from the formatters map?) + return nil +} - if err != nil { - return "", "", fmt.Errorf("error editing comment: %v", err) - } - return fmt.Sprintf("%v", commentIdForThisRun), commentUrl, nil +type MultipleCommentsStrategy struct { + formatters map[string][]ReportFormat + TimeOfRun time.Time } -type LatestRunCommentStrategy struct { - TimeOfRun time.Time +func NewMultipleCommentsStrategy() MultipleCommentsStrategy { + return MultipleCommentsStrategy{ + TimeOfRun: time.Now(), + formatters: make(map[string][]ReportFormat), + } } -func (strategy LatestRunCommentStrategy) Report(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) (string, string, error) { - comments, err := ciService.GetComments(PrNumber) - if err != nil { - return "", "", fmt.Errorf("error getting comments: %v", err) +func (strategy MultipleCommentsStrategy) Report(projectName string, report string, reportFormatter func(report string) string) error { + if _, exists := strategy.formatters[projectName]; !exists { + strategy.formatters[projectName] = []ReportFormat{} } + strategy.formatters[projectName] = append(strategy.formatters[projectName], ReportFormat{ + Report: report, + ReportFormatter: reportFormatter, + }) - reportTitle := "Digger latest run report" - commentId, commentUrl, err := upsertComment(ciService, PrNumber, report, reportFormatter, comments, reportTitle, supportsCollapsibleComment) - return commentId, commentUrl, err + return nil } -type MultipleCommentsStrategy struct{} +func (strategy MultipleCommentsStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) ([]string, []string, error) { + hasError := false + var latestError error = nil + commentIds := make([]string, 0) + commentUrls := make([]string, 0) + for projectName, projectFormatters := range strategy.formatters { + projectTitle := "Digger run report for " + projectName + " " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)") + var projectComment = "" + for _, f := range projectFormatters { + report := f.ReportFormatter(f.Report) + projectComment = projectComment + "\n" + report + } + if !supportsCollapsibleComment { + projectComment = utils.AsComment(projectTitle)(projectComment) + } else { + projectComment = utils.AsCollapsibleComment(projectTitle, false)(projectComment) + } + c, err := ciService.PublishComment(PrNumber, projectComment) + if err != nil { + log.Printf("error while publishing reporter comment: %v", err) + hasError = true + latestError = err + // append placeholders + commentIds = append(commentIds, "0") + commentUrls = append(commentUrls, "") + + } else { + commentIds = append(commentIds, c.Id) + commentUrls = append(commentUrls, c.Url) + } + } + + if hasError { + log.Printf("could not publish all comments") + return commentIds, commentUrls, latestError + } -func (strategy MultipleCommentsStrategy) Report(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) (string, string, error) { - _, err := ciService.PublishComment(PrNumber, reportFormatter(report)) - return "", "", err + return commentIds, commentUrls, nil +} + +func (strategy MultipleCommentsStrategy) Suppress(projectName string) error { + // TODO: figure out how to suppress a particular project (probably pop it from the formatters map?) + return nil } diff --git a/libs/comment_utils/reporting/utils.go b/libs/comment_utils/reporting/utils.go index b48e8912e..63a31c355 100644 --- a/libs/comment_utils/reporting/utils.go +++ b/libs/comment_utils/reporting/utils.go @@ -5,7 +5,6 @@ import ( "github.com/diggerhq/digger/libs/ci" dg_configuration "github.com/diggerhq/digger/libs/digger_config" "log" - "time" ) type SourceDetails struct { @@ -28,15 +27,22 @@ func PostInitialSourceComments(ghService ci.PullRequestService, prNumber int, im reporter := CiReporter{ PrNumber: prNumber, CiService: ghService, - ReportStrategy: CommentPerRunStrategy{fmt.Sprintf("Report for location: %v", location), time.Now()}, + ReportStrategy: NewSingleCommentStrategy(), } - commentId, _, err := reporter.Report("Comment Reporter", func(report string) string { return "" }) + + err := reporter.Report(location, "Comment Reporter", func(report string) string { return "" }) + if err != nil { + log.Printf("Error reporting source module comment: %v", err) + return nil, fmt.Errorf("error reporting source module comment: %v", err) + } + + commentIds, _, err := reporter.Flush() if err != nil { log.Printf("Error reporting source module comment: %v", err) return nil, fmt.Errorf("error reporting source module comment: %v", err) } - sourceDetails = append(sourceDetails, SourceDetails{location, commentId, projects}) + sourceDetails = append(sourceDetails, SourceDetails{location, commentIds[0], projects}) } return sourceDetails, nil diff --git a/libs/execution/execution.go b/libs/execution/execution.go index a3dbea138..75444f8b2 100644 --- a/libs/execution/execution.go +++ b/libs/execution/execution.go @@ -223,7 +223,7 @@ func (d DiggerExecutor) Plan() (*iac_utils.IacSummary, bool, bool, string, strin if step.Action == "init" { _, stderr, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars) if err != nil { - reportError(d.Reporter, stderr) + reportError(d.ProjectName, d.Reporter, stderr) return nil, false, false, "", "", fmt.Errorf("error running init: %v", err) } } @@ -288,18 +288,18 @@ func (d DiggerExecutor) Plan() (*iac_utils.IacSummary, bool, bool, string, strin } } } - reportAdditionalOutput(d.Reporter, d.projectId()) + reportAdditionalOutput(d.Reporter, d.ProjectName) return planSummary, true, !isEmptyPlan, plan, terraformPlanOutput, nil } -func reportError(r reporting.Reporter, stderr string) { +func reportError(projectName string, r reporting.Reporter, stderr string) { if r.SupportsMarkdown() { - _, _, commentErr := r.Report(stderr, utils.AsCollapsibleComment("Error during init.", false)) + commentErr := r.Report(projectName, stderr, utils.AsCollapsibleComment("Error during init.", false)) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } } else { - _, _, commentErr := r.Report(stderr, utils.AsComment("Error during init.")) + commentErr := r.Report(projectName, stderr, utils.AsComment("Error during init.")) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } @@ -337,7 +337,7 @@ func (d DiggerExecutor) Apply() (*iac_utils.IacSummary, bool, string, error) { if step.Action == "init" { stdout, stderr, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars) if err != nil { - reportTerraformError(d.Reporter, stderr) + reportTerraformError(d.ProjectName, d.Reporter, stderr) return nil, false, stdout, fmt.Errorf("error running init: %v", err) } } @@ -346,9 +346,9 @@ func (d DiggerExecutor) Apply() (*iac_utils.IacSummary, bool, string, error) { stdout, stderr, err := d.TerraformExecutor.Apply(applyArgs, plansFilename, d.CommandEnvVars) applyOutput = cleanupTerraformApply(true, err, stdout, stderr) - reportTerraformApplyOutput(d.Reporter, d.projectId(), applyOutput) + reportTerraformApplyOutput(d.Reporter, d.ProjectName, applyOutput) if err != nil { - reportApplyError(d.Reporter, err) + reportApplyError(d.ProjectName, d.Reporter, err) return nil, false, stdout, fmt.Errorf("error executing apply: %v", err) } @@ -370,25 +370,25 @@ func (d DiggerExecutor) Apply() (*iac_utils.IacSummary, bool, string, error) { } } } - reportAdditionalOutput(d.Reporter, d.projectId()) + reportAdditionalOutput(d.Reporter, d.ProjectName) return &summary, true, applyOutput, nil } -func reportApplyError(r reporting.Reporter, err error) { +func reportApplyError(projectName string, r reporting.Reporter, err error) { if r.SupportsMarkdown() { - _, _, commentErr := r.Report(err.Error(), utils.AsCollapsibleComment("Error during applying.", false)) + commentErr := r.Report(projectName, err.Error(), utils.AsCollapsibleComment("Error during applying.", false)) if commentErr != nil { log.Printf("error publishing comment: %v", err) } } else { - _, _, commentErr := r.Report(err.Error(), utils.AsComment("Error during applying.")) + commentErr := r.Report(projectName, err.Error(), utils.AsComment("Error during applying.")) if commentErr != nil { log.Printf("error publishing comment: %v", err) } } } -func reportTerraformApplyOutput(r reporting.Reporter, projectId string, applyOutput string) { +func reportTerraformApplyOutput(r reporting.Reporter, projectName string, applyOutput string) { var formatter func(string) string if r.SupportsMarkdown() { formatter = utils.GetTerraformOutputAsCollapsibleComment("Apply output", false) @@ -396,39 +396,39 @@ func reportTerraformApplyOutput(r reporting.Reporter, projectId string, applyOut formatter = utils.GetTerraformOutputAsComment("Apply output") } - _, _, commentErr := r.Report(applyOutput, formatter) + commentErr := r.Report(projectName, applyOutput, formatter) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } } -func reportTerraformError(r reporting.Reporter, stderr string) { +func reportTerraformError(projectName string, r reporting.Reporter, stderr string) { if r.SupportsMarkdown() { - _, _, commentErr := r.Report(stderr, utils.GetTerraformOutputAsCollapsibleComment("Error during init.", false)) + commentErr := r.Report(projectName, stderr, utils.GetTerraformOutputAsCollapsibleComment("Error during init.", false)) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } } else { - _, _, commentErr := r.Report(stderr, utils.GetTerraformOutputAsComment("Error during init.")) + commentErr := r.Report(projectName, stderr, utils.GetTerraformOutputAsComment("Error during init.")) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } } } -func reportAdditionalOutput(r reporting.Reporter, projectId string) { +func reportAdditionalOutput(r reporting.Reporter, projectName string) { var formatter func(string) string if r.SupportsMarkdown() { - formatter = utils.GetTerraformOutputAsCollapsibleComment("Additional output for "+projectId+"", false) + formatter = utils.GetTerraformOutputAsCollapsibleComment("Additional output for "+projectName+"", false) } else { - formatter = utils.GetTerraformOutputAsComment("Additional output for " + projectId) + formatter = utils.GetTerraformOutputAsComment("Additional output for " + projectName) } diggerOutPath := os.Getenv("DIGGER_OUT") if _, err := os.Stat(diggerOutPath); err == nil { output, _ := os.ReadFile(diggerOutPath) outputStr := string(output) if len(outputStr) > 0 { - _, _, commentErr := r.Report(outputStr, formatter) + commentErr := r.Report(projectName, outputStr, formatter) if commentErr != nil { log.Printf("error publishing comment: %v", commentErr) } @@ -459,7 +459,7 @@ func (d DiggerExecutor) Destroy() (bool, error) { if step.Action == "init" { _, stderr, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars) if err != nil { - reportError(d.Reporter, stderr) + reportError(d.ProjectName, d.Reporter, stderr) return false, fmt.Errorf("error running init: %v", err) } } diff --git a/libs/locking/locking.go b/libs/locking/locking.go index 197059ebf..094b0fe04 100644 --- a/libs/locking/locking.go +++ b/libs/locking/locking.go @@ -74,7 +74,7 @@ func (projectLock *PullRequestLock) Lock() (bool, error) { transactionIdStr := strconv.Itoa(*existingLockTransactionId) comment := "Project " + projectLock.projectId() + " locked by another PR #" + transactionIdStr + " (failed to acquire lock " + projectLock.ProjectNamespace + "). The locking plan must be applied or discarded before future plans can execute" - reportLockingFailed(projectLock.Reporter, comment) + reportLockingFailed(projectLock.ProjectName, projectLock.Reporter, comment) return false, fmt.Errorf(comment) } } @@ -87,35 +87,35 @@ func (projectLock *PullRequestLock) Lock() (bool, error) { if lockAcquired && !isNoOpLock { comment := "Project " + projectLock.projectId() + " has been locked by PR #" + strconv.Itoa(projectLock.PrNumber) - reportingLockingSuccess(projectLock.Reporter, comment) + reportingLockingSuccess(projectLock.ProjectName, projectLock.Reporter, comment) log.Println("project " + projectLock.projectId() + " locked successfully. PR # " + strconv.Itoa(projectLock.PrNumber)) } return lockAcquired, nil } -func reportingLockingSuccess(r reporting.Reporter, comment string) { +func reportingLockingSuccess(projectName string, r reporting.Reporter, comment string) { if r.SupportsMarkdown() { - _, _, err := r.Report(comment, utils.AsCollapsibleComment("Locking successful", false)) + err := r.Report(projectName, comment, utils.AsCollapsibleComment("Locking successful", false)) if err != nil { log.Println("failed to publish comment: " + err.Error()) } } else { - _, _, err := r.Report(comment, utils.AsComment("Locking successful")) + err := r.Report(projectName, comment, utils.AsComment("Locking successful")) if err != nil { log.Println("failed to publish comment: " + err.Error()) } } } -func reportLockingFailed(r reporting.Reporter, comment string) { +func reportLockingFailed(projectName string, r reporting.Reporter, comment string) { if r.SupportsMarkdown() { - _, _, err := r.Report(comment, utils.AsCollapsibleComment("Locking failed", false)) + err := r.Report(projectName, comment, utils.AsCollapsibleComment("Locking failed", false)) if err != nil { log.Println("failed to publish comment: " + err.Error()) } } else { - _, _, err := r.Report(comment, utils.AsComment("Locking failed")) + err := r.Report(projectName, comment, utils.AsComment("Locking failed")) if err != nil { log.Println("failed to publish comment: " + err.Error()) } @@ -146,7 +146,7 @@ func (projectLock *PullRequestLock) verifyNoHangingLocks() (bool, error) { } transactionIdStr := strconv.Itoa(*transactionId) comment := "Project " + projectLock.projectId() + " locked by another PR #" + transactionIdStr + "(failed to acquire lock " + projectLock.ProjectName + "). The locking plan must be applied or discarded before future plans can execute" - reportLockingFailed(projectLock.Reporter, comment) + reportLockingFailed(projectLock.ProjectName, projectLock.Reporter, comment) return false, fmt.Errorf(comment) } return true, nil @@ -171,7 +171,7 @@ func (projectLock *PullRequestLock) Unlock() (bool, error) { } if lockReleased { comment := "Project unlocked (" + projectLock.projectId() + ")." - reportSuccessfulUnlocking(projectLock.Reporter, comment) + reportSuccessfulUnlocking(projectLock.ProjectName, projectLock.Reporter, comment) log.Println("Project unlocked") return true, nil @@ -181,14 +181,14 @@ func (projectLock *PullRequestLock) Unlock() (bool, error) { return false, nil } -func reportSuccessfulUnlocking(r reporting.Reporter, comment string) { +func reportSuccessfulUnlocking(projectName string, r reporting.Reporter, comment string) { if r.SupportsMarkdown() { - _, _, err := r.Report(comment, utils.AsCollapsibleComment("Unlocking successful", false)) + err := r.Report(projectName, comment, utils.AsCollapsibleComment("Unlocking successful", false)) if err != nil { log.Println("failed to publish comment: " + err.Error()) } } else { - _, _, err := r.Report(comment, utils.AsComment("Unlocking successful")) + err := r.Report(projectName, comment, utils.AsComment("Unlocking successful")) if err != nil { log.Println("failed to publish comment: " + err.Error()) } @@ -210,7 +210,7 @@ func (projectLock *PullRequestLock) ForceUnlock() error { if lockReleased { comment := "Project unlocked (" + projectLock.projectId() + ")." - reportSuccessfulUnlocking(projectLock.Reporter, comment) + reportSuccessfulUnlocking(projectLock.ProjectName, projectLock.Reporter, comment) log.Println("Project unlocked") } return nil diff --git a/libs/spec/models.go b/libs/spec/models.go index 3637f920b..8f11ae3f0 100644 --- a/libs/spec/models.go +++ b/libs/spec/models.go @@ -5,9 +5,10 @@ import ( ) type ReporterSpec struct { - ReporterType string `json:"reporter_type"` - ReportingStrategy string `json:"reporting_strategy"` - ReportTerraformOutput bool `json:"report_terraform_output"` + ReporterType string `json:"reporter_type"` + ReportingStrategy string `json:"reporting_strategy"` + ReportTerraformOutput bool `json:"report_terraform_output"` + ReportCommentId *string `json:"report_comment_id,omitempty"` } type CommentUpdaterSpec struct { diff --git a/libs/spec/providers.go b/libs/spec/providers.go index 6ad1aac49..fd8f807e7 100644 --- a/libs/spec/providers.go +++ b/libs/spec/providers.go @@ -27,7 +27,6 @@ import ( "net/http" "os" "strings" - "time" ) type JobSpecProvider struct{} @@ -117,16 +116,9 @@ func (r ReporterProvider) GetReporter(title string, reporterSpec ReporterSpec, c getStrategy := func(strategy string) reporting.ReportStrategy { switch reporterSpec.ReportingStrategy { case "comments_per_run": - return reporting.CommentPerRunStrategy{ - Title: title, - TimeOfRun: time.Now(), - } - case "latest_run_comment": - return reporting.LatestRunCommentStrategy{ - TimeOfRun: time.Now(), - } + return reporting.NewSingleCommentStrategy() default: - return reporting.MultipleCommentsStrategy{} + return reporting.NewMultipleCommentsStrategy() } } @@ -141,15 +133,6 @@ func (r ReporterProvider) GetReporter(title string, reporterSpec ReporterSpec, c IsSupportMarkdown: true, ReportStrategy: strategy, }, nil - case "lazy": - strategy := getStrategy(reporterSpec.ReportingStrategy) - ciReporter := reporting.CiReporter{ - CiService: ciService, - PrNumber: prNumber, - IsSupportMarkdown: true, - ReportStrategy: strategy, - } - return reporting.NewCiReporterLazy(ciReporter), nil default: return reporting.NoopReporter{}, nil }