Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: support single comment summary with orchestrator mode #1850

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from

Conversation

motatoes
Copy link
Contributor

@motatoes motatoes commented Dec 4, 2024

Summary by CodeRabbit

Based on the comprehensive summary, here are the release notes:

  • New Features

    • Enhanced AI-powered reporting capabilities for pull requests and issue comments
    • Improved error handling and logging mechanisms across various components
    • Added support for more flexible comment reporting strategies
  • Improvements

    • Streamlined reporting process with new comment management strategies
    • Updated reporting interfaces to provide more context and clarity
    • Refined locking and job execution error reporting
  • Bug Fixes

    • Improved error handling in GitHub and GitLab webhook event processing
    • Enhanced comment reporting mechanisms to prevent potential reporting issues
  • Technical Updates

    • Refactored reporting strategies from lazy to basic comment management
    • Updated method signatures across multiple libraries to support more robust reporting
    • Added new fields for tracking placeholder comments and reporting outputs

Copy link
Contributor

coderabbitai bot commented Dec 4, 2024

Walkthrough

The pull request introduces significant enhancements to Digger's reporting and commenting mechanisms across multiple components. The changes primarily focus on improving error handling, reporting strategies, and comment management. Key modifications include updating reporting interfaces, refining comment strategies, and adding new fields to track placeholder comments and report details. The changes streamline the reporting process, provide more flexibility in comment handling, and enhance error tracking across GitHub and GitLab integrations.

Changes

File Change Summary
backend/controllers/github.go Added panic recovery, AI summary comment publishing, and updated job conversion logic
backend/models/scheduler.go Added new fields: AiSummaryCommentId, PlaceholderCommentIdForReport, ReportTerraformOutputs
libs/comment_utils/reporting/reporting.go Substantial changes to reporting strategies, removed lazy reporting, updated comment handling
libs/spec/models.go Added ReportCommentId field to ReporterSpec
backend/migrations/20250102194016.sql Added placeholder_comment_id_for_report column to digger_batches table

Poem

🐰 Digger's Reporting Rhapsody

Comments flow like rivers wide,
Strategies shift with rabbit's pride,
Markdown dancing, errors bright,
Reporting now with newfound might!

— CodeRabbit's Reporting Poet 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (5)
libs/comment_utils/reporting/reporting.go (1)

110-114: Use 'ID' instead of 'Id' in struct field names

In Go, it's standard practice to use 'ID' instead of 'Id' in struct field names. Consider renaming CommentId to CommentID in AlwaysSameCommentStrategy for consistency.

 type AlwaysSameCommentStrategy struct {
 	Title     string
-	CommentId string
+	CommentID string
 	TimeOfRun time.Time
 }
backend/controllers/github.go (2)

503-508: Fix typo in placeholder comment text

There's a typo in the placeholder comment text: "placehoder" should be "placeholder".

 placeholderComment, err := ghService.PublishComment(prNumber, "<digger report placehoder>")
+placeholderComment, err := ghService.PublishComment(prNumber, "<digger report placeholder>")

885-890: Correct typo in placeholder comment text

The comment text contains a typo: "placehoder" should be "placeholder".

 placeholderComment, err := ghService.PublishComment(issueNumber, "<digger report placehoder>")
+placeholderComment, err := ghService.PublishComment(issueNumber, "<digger report placeholder>")
libs/spec/models.go (1)

8-11: Use 'ID' instead of 'Id' in struct field names

It's conventional in Go to use 'ID' instead of 'Id' for identifiers. Consider renaming ReportCommentId to ReportCommentID in ReporterSpec.

 type ReporterSpec struct {
 	ReporterType          string  `json:"reporter_type"`
 	ReportingStrategy     string  `json:"reporting_strategy"`
 	ReportTerraformOutput bool    `json:"report_terraform_output"`
-	ReportCommentId       *string `json:"report_comment_id,omitempty"`
+	ReportCommentID       *string `json:"report_comment_id,omitempty"`
 }
backend/models/scheduler.go (1)

25-38: Use 'ID' instead of 'Id' in struct field names

For consistency with Go naming conventions, consider renaming PlaceholderCommentIdForReport to PlaceholderCommentIDForReport in DiggerBatch.

 type DiggerBatch struct {
 	ID                            uuid.UUID `gorm:"primary_key"`
 	VCS                           DiggerVCSType
 	PrNumber                      int
 	CommentId                     *int64
-	PlaceholderCommentIdForReport *string
+	PlaceholderCommentIDForReport *string
 	Status                        orchestrator_scheduler.DiggerBatchStatus
 	BranchName                    string
 	DiggerConfig                  string
 	GithubInstallationId          int64
 	GitlabProjectId               int
 	RepoFullName                  string
 	RepoOwner                     string
 	RepoName                      string
 	BatchType                     orchestrator_scheduler.DiggerCommand
 	// used for module source grouping comments
 	SourceDetails []byte
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c9a0625 and e340b53.

📒 Files selected for processing (10)
  • backend/controllers/github.go (2 hunks)
  • backend/models/scheduler.go (1 hunks)
  • backend/models/storage.go (1 hunks)
  • backend/services/spec.go (1 hunks)
  • backend/utils/graphs.go (2 hunks)
  • ee/backend/controllers/gitlab.go (2 hunks)
  • ee/backend/hooks/github.go (1 hunks)
  • libs/comment_utils/reporting/reporting.go (2 hunks)
  • libs/spec/models.go (1 hunks)
  • libs/spec/providers.go (1 hunks)
🔇 Additional comments (7)
ee/backend/controllers/gitlab.go (2)

336-339: Changes appear correct for updated function signature

The updated call to ConvertJobsToDiggerJobs reflects the new function signature. Parameters are adjusted appropriately.


527-530: Function call updated appropriately

The changes to ConvertJobsToDiggerJobs parameters align with the updated function definition.

backend/services/spec.go (1)

119-122: Verify the impact of changing the reporting strategy

The change from comments_per_run to always_same_comment represents a significant shift in how comments are handled. This could affect existing integrations and workflows.

Consider:

  1. Adding a feature flag to gradually roll out this change
  2. Documenting the new behavior in the codebase
  3. Ensuring backward compatibility for existing integrations
backend/utils/graphs.go (1)

17-17: ⚠️ Potential issue

Verify all callers of ConvertJobsToDiggerJobs are updated

The function signature has been modified to include placeholderCommentIdForReport *string. This is a breaking change that requires updates to all callers.

Consider:

  1. Adding a version suffix to the function name (e.g., ConvertJobsToDiggerJobsV2)
  2. Providing a wrapper function with the old signature for backward compatibility
  3. Adding migration documentation

Also applies to: 46-46

✅ Verification successful

All callers have been properly updated with the new parameter

Based on the search results, all callers of ConvertJobsToDiggerJobs are passing the new placeholderCommentIdForReport parameter correctly:

  • In backend/controllers/github.go, it's passing &placeholderComment.Id
  • In ee/backend/hooks/github.go and ee/backend/controllers/gitlab.go, it's passing nil
  • In test files, it's passing "test" as a placeholder
  • In next/services/scheduler.go, it's passing an empty string ""

The breaking change has been properly handled across the codebase, and there's no need for versioning or backward compatibility measures.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all callers of ConvertJobsToDiggerJobs
echo "Checking for all callers..."
rg "ConvertJobsToDiggerJobs\(" --type go -A 3

# Look for potential missed updates
echo "Checking for potential missed updates..."
ast-grep --pattern 'ConvertJobsToDiggerJobs($$$)'

Length of output: 7767

ee/backend/hooks/github.go (1)

154-154: 🛠️ Refactor suggestion

Clarify the intention behind passing nil for placeholderCommentIdForReport

While the code has been updated to match the new function signature, passing nil for placeholderCommentIdForReport needs clarification:

  1. Is this intentional for drift reconciliation?
  2. Should we fall back to a default behavior?

Consider:

  1. Adding a comment explaining why nil is acceptable here
  2. Using a default value if appropriate
  3. Adding error handling for the nil case in the reporting logic
✅ Verification successful

Passing nil for placeholderCommentIdForReport is correct in this context

Based on the code analysis, passing nil for placeholderCommentIdForReport in the drift reconciliation hook is intentional and correct:

  1. The parameter is properly typed as *string in the function signature, explicitly allowing nil values
  2. It's used consistently across different VCS implementations (both GitHub and GitLab handlers)
  3. The parameter is simply passed through to CreateDiggerBatch for storage, with no validation requirements
  4. This parameter is specifically for placeholder comment IDs in reporting, which isn't relevant for drift reconciliation workflows
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for similar patterns in other handlers
echo "Checking for similar patterns in other handlers..."
rg "ConvertJobsToDiggerJobs.*nil.*diggerYmlStr" --type go

# Look for documentation or comments about this behavior
echo "Checking for related documentation..."
rg "placeholderCommentIdForReport" --type md

Length of output: 906


Script:

#!/bin/bash
# Let's check the ConvertJobsToDiggerJobs function definition and usage context
ast-grep --pattern 'func ConvertJobsToDiggerJobs($$$)'

# Check for any comments or documentation around the parameter
rg -B 2 -A 2 "placeholderCommentIdForReport" --type go

# Look for any error handling related to this parameter
rg "if.*placeholderCommentIdForReport.*nil" --type go
rg "placeholderCommentIdForReport.*==.*nil" --type go

Length of output: 12328

libs/spec/providers.go (1)

128-134: LGTM! Clean implementation of the new reporting strategy

The new case for "always_same_comment" follows the existing pattern and integrates well with the reporting system.

backend/models/storage.go (1)

620-636: LGTM! Clean implementation of the placeholder comment ID

The changes properly integrate the new placeholderCommentIdForReport field into the batch creation process, maintaining consistency with the existing codebase structure.

Comment on lines 204 to 225
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {

// strip first and last lines
lines := strings.Split(commentBody, "\n")
lines := strings.Split(existingCommentBody, "\n")
lines = lines[1 : len(lines)-1]
commentBody = strings.Join(lines, "\n")
existingCommentBody = strings.Join(lines, "\n")

commentBody = commentBody + "\n\n" + report + "\n"
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"

var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(commentBody)
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(commentBody)
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}

err := ciService.EditComment(PrNumber, commentIdForThisRun, completeComment)
err := ciService.EditComment(prNumber, commentId, completeComment)

if err != nil {
return "", "", fmt.Errorf("error editing comment: %v", err)
return fmt.Errorf("error editing comment: %v", err)
}
return fmt.Sprintf("%v", commentIdForThisRun), commentUrl, nil
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent slice bounds out of range in appendToExistingComment

If existingCommentBody has fewer than two lines, slicing lines[1:len(lines)-1] will cause a panic. Ensure that lines has at least two elements before slicing.

 	// strip first and last lines
 	lines := strings.Split(existingCommentBody, "\n")
+	if len(lines) >= 2 {
 		lines = lines[1 : len(lines)-1]
+	} else {
+		lines = []string{}
+	}
 	existingCommentBody = strings.Join(lines, "\n")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
// strip first and last lines
lines := strings.Split(commentBody, "\n")
lines := strings.Split(existingCommentBody, "\n")
lines = lines[1 : len(lines)-1]
commentBody = strings.Join(lines, "\n")
existingCommentBody = strings.Join(lines, "\n")
commentBody = commentBody + "\n\n" + report + "\n"
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"
var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(commentBody)
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(commentBody)
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}
err := ciService.EditComment(PrNumber, commentIdForThisRun, completeComment)
err := ciService.EditComment(prNumber, commentId, completeComment)
if err != nil {
return "", "", fmt.Errorf("error editing comment: %v", err)
return fmt.Errorf("error editing comment: %v", err)
}
return fmt.Sprintf("%v", commentIdForThisRun), commentUrl, nil
return nil
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
// strip first and last lines
lines := strings.Split(existingCommentBody, "\n")
if len(lines) >= 2 {
lines = lines[1 : len(lines)-1]
} else {
lines = []string{}
}
existingCommentBody = strings.Join(lines, "\n")
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"
var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}
err := ciService.EditComment(prNumber, commentId, completeComment)
if err != nil {
return fmt.Errorf("error editing comment: %v", err)
}
return nil

Comment on lines 116 to 144
func (strategy AlwaysSameCommentStrategy) 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)
}

var commentBody *string
var commentUrl string
for _, comment := range comments {
if comment.Id == strategy.CommentId {
commentBody = comment.Body
commentUrl = comment.Url
}
}

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)")
}

err = appendToExistingComment(ciService, PrNumber, *commentBody, report, reportTitle, strategy.CommentId, supportsCollapsibleComment)
if err != nil {
return "", "", fmt.Errorf("error while adding to existing comment: %v", err)
}

return strategy.CommentId, commentUrl, err
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential nil pointer dereference in AlwaysSameCommentStrategy.Report method

If no comment matching strategy.CommentId is found, commentBody remains nil. Dereferencing it at line 138 will cause a panic. Ensure commentBody is not nil before dereferencing.

 	for _, comment := range comments {
 		if comment.Id == strategy.CommentId {
 			commentBody = comment.Body
 			commentUrl = comment.Url
 		}
 	}

+	if commentBody == nil {
+		return "", "", fmt.Errorf("no comment found with CommentId: %v", strategy.CommentId)
+	}

 	err = appendToExistingComment(ciService, PrNumber, *commentBody, report, reportTitle, strategy.CommentId, supportsCollapsibleComment)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (strategy AlwaysSameCommentStrategy) 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)
}
var commentBody *string
var commentUrl string
for _, comment := range comments {
if comment.Id == strategy.CommentId {
commentBody = comment.Body
commentUrl = comment.Url
}
}
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)")
}
err = appendToExistingComment(ciService, PrNumber, *commentBody, report, reportTitle, strategy.CommentId, supportsCollapsibleComment)
if err != nil {
return "", "", fmt.Errorf("error while adding to existing comment: %v", err)
}
return strategy.CommentId, commentUrl, err
}
func (strategy AlwaysSameCommentStrategy) 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)
}
var commentBody *string
var commentUrl string
for _, comment := range comments {
if comment.Id == strategy.CommentId {
commentBody = comment.Body
commentUrl = comment.Url
}
}
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)")
}
if commentBody == nil {
return "", "", fmt.Errorf("no comment found with CommentId: %v", strategy.CommentId)
}
err = appendToExistingComment(ciService, PrNumber, *commentBody, report, reportTitle, strategy.CommentId, supportsCollapsibleComment)
if err != nil {
return "", "", fmt.Errorf("error while adding to existing comment: %v", err)
}
return strategy.CommentId, commentUrl, err
}

Comment on lines 128 to 134
case "always_same_comment":
return reporting.AlwaysSameCommentStrategy{
Title: title,
TimeOfRun: time.Now(),
CommentId: *reporterSpec.ReportCommentId,
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add nil check for ReportCommentId

The code dereferences reporterSpec.ReportCommentId without checking if it's nil, which could lead to a panic.

Add a nil check before using the pointer:

 case "always_same_comment":
+    if reporterSpec.ReportCommentId == nil {
+        return reporting.MultipleCommentsStrategy{}
+    }
     return reporting.AlwaysSameCommentStrategy{
         Title:     title,
         TimeOfRun: time.Now(),
         CommentId: *reporterSpec.ReportCommentId,
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "always_same_comment":
return reporting.AlwaysSameCommentStrategy{
Title: title,
TimeOfRun: time.Now(),
CommentId: *reporterSpec.ReportCommentId,
}
case "always_same_comment":
if reporterSpec.ReportCommentId == nil {
return reporting.MultipleCommentsStrategy{}
}
return reporting.AlwaysSameCommentStrategy{
Title: title,
TimeOfRun: time.Now(),
CommentId: *reporterSpec.ReportCommentId,
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
libs/comment_utils/reporting/reporting.go (2)

131-136: Consider extracting time format constant

The time format string "2006-01-02 15:04:05 (MST)" is hardcoded in multiple places. Consider extracting it to a package-level constant.

+const (
+    TimeFormat = "2006-01-02 15:04:05 (MST)"
+)
+
 	if strategy.Title != "" {
-		reportTitle = strategy.Title + " " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)")
+		reportTitle = strategy.Title + " " + strategy.TimeOfRun.Format(TimeFormat)
 	} else {
-		reportTitle = "Digger run report at " + strategy.TimeOfRun.Format("2006-01-02 15:04:05 (MST)")
+		reportTitle = "Digger run report at " + strategy.TimeOfRun.Format(TimeFormat)
 	}

204-227: Add function documentation and improve error handling

The function lacks documentation and could benefit from more specific error handling.

Add documentation and improve error handling:

+// appendToExistingComment appends a new report to an existing comment while maintaining its formatting.
+// It strips the comment markers, appends the new report, and re-applies the formatting.
+// Parameters:
+//   - ciService: Service for interacting with the CI platform
+//   - prNumber: Pull request number
+//   - existingCommentBody: Current content of the comment
+//   - reportToAppend: New report to append
+//   - reportTitle: Title for the comment
+//   - commentId: ID of the comment to edit
+//   - supportsCollapsible: Whether to use collapsible comment formatting
+// Returns an error if the comment update fails
 func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
 	// strip first and last lines
 	lines := strings.Split(existingCommentBody, "\n")
 	if len(lines) > 1 {
 		lines = lines[1 : len(lines)-1]
 	}
 	existingCommentBody = strings.Join(lines, "\n")

 	existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"

 	var completeComment string
 	if !supportsCollapsible {
 		completeComment = utils.AsComment(reportTitle)(existingCommentBody)
 	} else {
 		completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
 	}

 	err := ciService.EditComment(prNumber, commentId, completeComment)
 	if err != nil {
-		return fmt.Errorf("error editing comment: %v", err)
+		return fmt.Errorf("failed to edit comment %s: %w", commentId, err)
 	}
 	return nil
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e340b53 and 151068c.

⛔ Files ignored due to path filters (1)
  • backend/migrations/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • backend/migrations/20241204194016.sql (1 hunks)
  • libs/comment_utils/reporting/reporting.go (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/migrations/20241204194016.sql
🔇 Additional comments (3)
libs/comment_utils/reporting/reporting.go (3)

110-114: LGTM: Well-structured struct definition

The struct fields are appropriately named and typed, following Go conventions.


195-202: LGTM: Clean error handling and consistent approach

The changes maintain good error handling practices and integrate well with the new append functionality.


116-144: ⚠️ Potential issue

Fix potential nil pointer dereference

The code dereferences commentBody at line 138 without checking if it's nil. This could cause a panic if no matching comment is found.

Apply this fix:

 	var commentBody *string
 	var commentUrl string
 	for _, comment := range comments {
 		if comment.Id == strategy.CommentId {
 			commentBody = comment.Body
 			commentUrl = comment.Url
 		}
 	}
+
+	if commentBody == nil {
+		return "", "", fmt.Errorf("no comment found with ID %s", strategy.CommentId)
+	}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
backend/controllers/github.go (1)

503-503: Fix typo in placeholder comment message

The word "placeholder" is misspelled as "placehoder" in both locations.

Apply this fix:

-	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder")
+	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placeholder")

Also applies to: 886-886

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 151068c and 60a7a13.

📒 Files selected for processing (2)
  • backend/controllers/github.go (2 hunks)
  • libs/comment_utils/reporting/reporting.go (2 hunks)
🔇 Additional comments (3)
libs/comment_utils/reporting/reporting.go (2)

110-114: LGTM: Clear and well-structured struct definition

The struct fields are appropriately named and have clear purposes for managing comment updates.


116-144: ⚠️ Potential issue

Fix potential nil pointer dereference in Report method

The code dereferences commentBody at line 138 without checking if it's nil, which could happen if no comment with the matching ID is found.

Apply this fix:

 	var commentBody *string
 	var commentUrl string
 	for _, comment := range comments {
 		if comment.Id == strategy.CommentId {
 			commentBody = comment.Body
 			commentUrl = comment.Url
 		}
 	}
+
+	if commentBody == nil {
+		return "", "", fmt.Errorf("no comment found with ID: %s", strategy.CommentId)
+	}

Likely invalid or redundant comment.

backend/controllers/github.go (1)

885-890: ⚠️ Potential issue

Fix incorrect error message in placeholder comment creation (duplicate)

The same error message issue exists in the issue comment event handler.

Apply the same fix as above to correct the error message.

Comment on lines 204 to 227
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {

// strip first and last lines
lines := strings.Split(commentBody, "\n")
lines = lines[1 : len(lines)-1]
commentBody = strings.Join(lines, "\n")
lines := strings.Split(existingCommentBody, "\n")
//if len(lines) > 1 {
// lines = lines[1 : len(lines)-1]
//}
existingCommentBody = strings.Join(lines, "\n")

commentBody = commentBody + "\n\n" + report + "\n"
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"

var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(commentBody)
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(commentBody)
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}

err := ciService.EditComment(PrNumber, commentIdForThisRun, completeComment)
err := ciService.EditComment(prNumber, commentId, completeComment)

if err != nil {
return "", "", fmt.Errorf("error editing comment: %v", err)
return fmt.Errorf("error editing comment: %v", err)
}
return fmt.Sprintf("%v", commentIdForThisRun), commentUrl, nil
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clean up commented code and improve error handling

  1. The commented out code for stripping lines should either be implemented or removed
  2. The error message could be more descriptive by including the comment ID

Apply these improvements:

 func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
 	// strip first and last lines
 	lines := strings.Split(existingCommentBody, "\n")
-	//if len(lines) > 1 {
-	//	lines = lines[1 : len(lines)-1]
-	//}
 	existingCommentBody = strings.Join(lines, "\n")
 	existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"

 	var completeComment string
 	if !supportsCollapsible {
 		completeComment = utils.AsComment(reportTitle)(existingCommentBody)
 	} else {
 		completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
 	}

 	err := ciService.EditComment(prNumber, commentId, completeComment)
 	if err != nil {
-		return fmt.Errorf("error editing comment: %v", err)
+		return fmt.Errorf("error editing comment %s: %v", commentId, err)
 	}
 	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
// strip first and last lines
lines := strings.Split(commentBody, "\n")
lines = lines[1 : len(lines)-1]
commentBody = strings.Join(lines, "\n")
lines := strings.Split(existingCommentBody, "\n")
//if len(lines) > 1 {
// lines = lines[1 : len(lines)-1]
//}
existingCommentBody = strings.Join(lines, "\n")
commentBody = commentBody + "\n\n" + report + "\n"
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"
var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(commentBody)
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(commentBody)
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}
err := ciService.EditComment(PrNumber, commentIdForThisRun, completeComment)
err := ciService.EditComment(prNumber, commentId, completeComment)
if err != nil {
return "", "", fmt.Errorf("error editing comment: %v", err)
return fmt.Errorf("error editing comment: %v", err)
}
return fmt.Sprintf("%v", commentIdForThisRun), commentUrl, nil
return nil
func appendToExistingComment(ciService ci.PullRequestService, prNumber int, existingCommentBody string, reportToAppend string, reportTitle string, commentId string, supportsCollapsible bool) error {
// strip first and last lines
lines := strings.Split(existingCommentBody, "\n")
existingCommentBody = strings.Join(lines, "\n")
existingCommentBody = existingCommentBody + "\n\n" + reportToAppend + "\n"
var completeComment string
if !supportsCollapsible {
completeComment = utils.AsComment(reportTitle)(existingCommentBody)
} else {
completeComment = utils.AsCollapsibleComment(reportTitle, false)(existingCommentBody)
}
err := ciService.EditComment(prNumber, commentId, completeComment)
if err != nil {
return fmt.Errorf("error editing comment %s: %v", commentId, err)
}
return nil
}

Comment on lines +502 to +508

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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect error message in placeholder comment creation

The error message incorrectly references "strconv.ParseInt" when the error is from publishing a comment.

Apply this fix:

 	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder")
 	if err != nil {
-		log.Printf("strconv.ParseInt error: %v", err)
+		log.Printf("failed to publish placeholder comment: %v", err)
 		commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
 		return fmt.Errorf("comment reporter error: %v", err)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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)
}
placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder")
if err != nil {
log.Printf("failed to publish placeholder comment: %v", err)
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
return fmt.Errorf("comment reporter error: %v", err)
}

Copy link

delve-auditor bot commented Dec 19, 2024

No security or compliance issues detected. Reviewed everything up to 5a33821.

Security Overview
  • 🔎 Scanned files: 21 changed file(s)
Detected Code Changes
Change Type Relevant files
Enhancement ► github.go
    Add placeholder comment for report
► reporting.go
    Refactor reporting strategy implementation
► models/scheduler.go
    Add placeholder comment ID field
Configuration changes ► migrations/20241204194016.sql
    Add placeholder_comment_id_for_report column
Refactor ► digger_config/terragrunt/atlantis/generate.go
    Refactor project creation logic
► comment_utils/reporting/core.go
    Simplify reporter interface
Bug Fix ► digger_config/digger_config.go
    Fix pattern normalization
Diagnostics
  • Code Scan ID: eee4bb6e-b27a-4ea8-8e51-40b6571e7f8d

Reply to this PR with @delve-auditor followed by a description of what change you want and we'll auto-submit a change to this PR to implement it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (13)
libs/comment_utils/reporting/mock.go (1)

7-9: Consider enhancing test coverage for comment ID handling

While the implementation correctly follows the new interface, consider adding test cases to verify the new placeholder comment mechanism works correctly with this mock implementation.

Consider adding a field to track comment IDs for testing:

 type MockReporter struct {
 	commands []string
+	// Add fields to track placeholder comment IDs for testing
+	placeholderCommentId string
 }

 func (mockReporter *MockReporter) Report(report string, reportFormatting func(report string) string) error {
 	mockReporter.commands = append(mockReporter.commands, "Report")
+	// Track the placeholder comment ID if needed for tests
 	return nil
 }
libs/comment_utils/reporting/utils.go (2)

33-37: Consider adding context to error wrapping

While the error handling is correct, consider adding more context when wrapping the error to aid in debugging.

-			return nil, fmt.Errorf("error reporting source module comment: %v", err)
+			return nil, fmt.Errorf("error reporting source module comment for location %v: %v", location, err)

33-39: Potential race condition in Report-then-Flush pattern

The new pattern of calling Report followed by Flush introduces a potential race condition. Between these calls, another process could modify the comment, causing the Flush to operate on an inconsistent state.

Consider either:

  1. Making the Report-Flush operation atomic within the CiReporter
  2. Adding a mechanism to detect and handle concurrent modifications
libs/comment_utils/reporting/reporting.go (3)

19-21: Simplify error handling in Report method

The error handling can be simplified further since we're just passing through the error.

 func (ciReporter CiReporter) Report(report string, reportFormatting func(report string) string) error {
-	_, _, err := ciReporter.ReportStrategy.Report(ciReporter.CiService, ciReporter.PrNumber, report, reportFormatting, ciReporter.SupportsMarkdown())
-	return err
+	return ciReporter.ReportStrategy.Report(ciReporter.CiService, ciReporter.PrNumber, report, reportFormatting, ciReporter.SupportsMarkdown())
 }

81-83: Empty Flush implementations need documentation

The empty Flush implementations in both strategies need documentation explaining why they return empty values. This is especially important as it's part of the interface contract.

Add documentation comments explaining the empty implementations:

+// Flush returns empty values as CommentPerRunStrategy doesn't buffer comments
 func (s CommentPerRunStrategy) Flush() (string, string, error) {
 	return "", "", nil
 }

+// Flush returns empty values as MultipleCommentsStrategy doesn't buffer comments
 func (s MultipleCommentsStrategy) Flush() (string, string, error) {
 	return "", "", nil
 }

Also applies to: 142-144


57-57: Document the Flush method in ReportStrategy interface

The new Flush method in the ReportStrategy interface lacks documentation explaining its purpose and contract.

Add interface documentation:

 type ReportStrategy interface {
 	Report(ciService ci.PullRequestService, PrNumber int, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) (commentId string, commentUrl string, error error)
+	// Flush finalizes any buffered comments and returns the last comment's ID and URL
 	Flush() (string, string, error)
 }
libs/execution/execution.go (5)

297-304: Enhance error logging message

Consider improving the error logging message to include more context about where the error occurred and what was being reported.

-			log.Printf("error publishing comment: %v", commentErr)
+			log.Printf("error publishing init error comment: %v", commentErr)

399-402: Enhance apply output error logging

Consider improving the error logging message to include more context.

-		log.Printf("error publishing comment: %v", commentErr)
+		log.Printf("error publishing terraform apply output comment: %v", commentErr)

407-415: Enhance terraform error logging

Consider improving the error logging message to include more context.

-			log.Printf("error publishing comment: %v", commentErr)
+			log.Printf("error publishing terraform init error comment: %v", commentErr)

431-434: Enhance additional output error logging

Consider improving the error logging message to include more context.

-				log.Printf("error publishing comment: %v", commentErr)
+				log.Printf("error publishing additional output comment: %v", commentErr)

Line range hint 297-434: Consider refactoring reporting functions to reduce duplication

The reporting functions share similar patterns and could be refactored into a common helper function to reduce code duplication and ensure consistent error handling.

Example implementation:

func reportWithFormat(r reporting.Reporter, content string, title string, isCollapsible bool) {
    var formatter func(string) string
    if r.SupportsMarkdown() {
        formatter = utils.AsCollapsibleComment(title, isCollapsible)
    } else {
        formatter = utils.AsComment(title)
    }
    
    if err := r.Report(content, formatter); err != nil {
        log.Printf("error publishing %s comment: %v", title, err)
    }
}

This helper function could be used to simplify all the reporting functions while maintaining consistent error handling and logging.

cli/pkg/digger/digger.go (2)

173-181: Refactor error handling for consistency and reusability.

The error handling pattern is duplicated across multiple reporting functions. Consider creating a helper function to handle the common pattern.

+func handleReportError(err error, context string) {
+    if err != nil {
+        log.Printf("Error publishing %s: %v", context, err)
+    }
+}

 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 <b>%v - %v</b>", projectName, command), false))
-        if err != nil {
-            log.Printf("Error publishing comment: %v", err)
-        }
+        err := reporter.Report(msg, coreutils.AsCollapsibleComment(fmt.Sprintf("Policy violation for <b>%v - %v</b>", projectName, command), false))
+        handleReportError(err, "policy violation")
     } else {
         // Similar changes for the else block
     }
     return msg
 }

Also applies to: 499-507, 521-524, 536-539, 546-551


Line range hint 314-327: Enhance plan validation reporting with structured messages.

The plan validation reporting could be improved to provide more structured and informative messages.

-    planReportMessage := "Terraform plan failed validation checks :x:<br>"
-    preformattedMessaged := make([]string, 0)
-    for _, message := range messages {
-        preformattedMessaged = append(preformattedMessaged, fmt.Sprintf("    %v", message))
-    }
-    planReportMessage = planReportMessage + strings.Join(preformattedMessaged, "<br>")
+    planReportMessage := strings.Builder{}
+    planReportMessage.WriteString("### Terraform Plan Validation Results :x:\n\n")
+    planReportMessage.WriteString("The following policy violations were found:\n\n")
+    for _, message := range messages {
+        planReportMessage.WriteString(fmt.Sprintf("- %v\n", message))
+    }
+    planReportMessage.WriteString("\nPlease address these violations before proceeding.")

     err = reporter.Report(planReportMessage, planPolicyFormatter)

Also, enhance the success message to include validation details:

-    err := reporter.Report("Terraform plan validation checks succeeded :white_check_mark:", planPolicyFormatter)
+    successMsg := fmt.Sprintf("### Terraform Plan Validation Succeeded :white_check_mark:\n\n" +
+        "- All policy checks passed\n" +
+        "- Project: %s\n" +
+        "- Validation time: %s\n",
+        job.ProjectName,
+        time.Now().Format(time.RFC3339))
+    err := reporter.Report(successMsg, planPolicyFormatter)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffa6e55 and dfe91e9.

📒 Files selected for processing (11)
  • cli/cmd/digger/root.go (0 hunks)
  • cli/pkg/digger/digger.go (7 hunks)
  • ee/cli/cmd/digger/root.go (0 hunks)
  • libs/comment_utils/reporting/core.go (1 hunks)
  • libs/comment_utils/reporting/mock.go (1 hunks)
  • libs/comment_utils/reporting/noop.go (1 hunks)
  • libs/comment_utils/reporting/reporting.go (5 hunks)
  • libs/comment_utils/reporting/utils.go (1 hunks)
  • libs/execution/execution.go (4 hunks)
  • libs/locking/locking.go (3 hunks)
  • libs/spec/providers.go (1 hunks)
💤 Files with no reviewable changes (2)
  • ee/cli/cmd/digger/root.go
  • cli/cmd/digger/root.go
✅ Files skipped from review due to trivial changes (1)
  • libs/locking/locking.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/spec/providers.go
🔇 Additional comments (4)
libs/comment_utils/reporting/core.go (1)

4-4: Verify the impact of removing commentId and commentUrl returns

The simplified signature suggests a change in how comment references are managed. Please ensure that:

  1. Consumers of this interface aren't relying on the removed return values
  2. Comment ID tracking is handled elsewhere in the codebase
  3. The change aligns with the new placeholder comment mechanism mentioned in the PR summary
✅ Verification successful

Based on the search results, I can now generate the final response:

The removal of commentId and commentUrl returns is safe

The codebase search reveals that:

  • All Reporter interface implementations (CiReporter, StdOutReporter, NoopReporter, MockReporter) already discard these values, returning empty strings or nil
  • No direct consumers are extracting or using the commentId/commentUrl from Report() method calls
  • The actual comment ID management is handled at a lower level by the ReportStrategy interface in reporting.go
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining uses of the old signature pattern
rg -A 2 "Report.*string.*string.*error" --type go

# Look for potential consumers that might expect comment IDs
ast-grep --pattern 'Report($$$).($id|$url)'

Length of output: 9247

libs/comment_utils/reporting/noop.go (1)

5-6: LGTM! Implementation correctly follows the new interface

The NoopReporter maintains its purpose while adhering to the simplified interface.

libs/comment_utils/reporting/reporting.go (1)

Line range hint 115-117: Prevent slice bounds out of range in comment processing

The slice operation lines[1 : len(lines)-1] will panic if lines has fewer than 2 elements.

This issue was previously reported and needs to be addressed. The fix involves adding a length check before slicing:

 	lines := strings.Split(commentBody, "\n")
+	if len(lines) >= 2 {
 		lines = lines[1 : len(lines)-1]
+	} else {
+		lines = []string{}
+	}
 	commentBody = strings.Join(lines, "\n")
cli/pkg/digger/digger.go (1)

Line range hint 595-597: Review AWS credentials handling for security best practices.

The PopulateAwsCredentialsEnvVarsForJob function is handling AWS credentials. Ensure that:

  1. Credentials are not logged or exposed in error messages
  2. Credentials are properly rotated and managed
  3. The minimum required IAM permissions are used
✅ Verification successful

Let me analyze the AWS credentials handling in the codebase based on the search results. I can now generate the final response:

AWS credentials are handled securely with appropriate safeguards in place

The codebase demonstrates secure AWS credentials handling practices:

  • Uses AWS SDK's recommended credential providers and configuration patterns
  • Credentials are not logged or exposed in error messages
  • Supports multiple authentication methods including environment variables, OIDC, and role assumption
  • Implements proper credential rotation through session tokens and temporary credentials
  • Uses AWS SDK's secure credential chain for resolving credentials
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential credential exposure in logs
rg -i "aws.*key|secret.*key|password|credential" -A 5 -B 5

Length of output: 78483

Comment on lines 379 to 387
commentErr := r.Report(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(err.Error(), utils.AsComment("Error during applying."))
if commentErr != nil {
log.Printf("error publishing comment: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect error variable in log message

There's a bug in the error logging where it uses the wrong error variable.

-			log.Printf("error publishing comment: %v", err)
+			log.Printf("error publishing apply error comment: %v", commentErr)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
commentErr := r.Report(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(err.Error(), utils.AsComment("Error during applying."))
if commentErr != nil {
log.Printf("error publishing comment: %v", err)
}
commentErr := r.Report(err.Error(), utils.AsCollapsibleComment("Error during applying.", false))
if commentErr != nil {
log.Printf("error publishing apply error comment: %v", commentErr)
}
} else {
commentErr := r.Report(err.Error(), utils.AsComment("Error during applying."))
if commentErr != nil {
log.Printf("error publishing apply error comment: %v", commentErr)
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
libs/comment_utils/reporting/reporting.go (2)

19-21: Simplify error handling in Report method

The method unnecessarily discards two return values from ReportStrategy.Report. Consider updating the interface to match the simplified error-only return type.

-	_, _, err := ciReporter.ReportStrategy.Report("", ciReporter.CiService, ciReporter.PrNumber, report, reportFormatting, ciReporter.SupportsMarkdown())
+	return ciReporter.ReportStrategy.Report("", ciReporter.CiService, ciReporter.PrNumber, report, reportFormatting, ciReporter.SupportsMarkdown())

60-63: Add validation for ReportFormat fields

The ReportFormat struct lacks validation for required fields. Consider adding validation to ensure ReportFormatter is not nil.

+func NewReportFormat(title string, formatter func(report string) string) (*ReportFormat, error) {
+	if formatter == nil {
+		return nil, fmt.Errorf("formatter cannot be nil")
+	}
+	return &ReportFormat{
+		ReportTitle:     title,
+		ReportFormatter: formatter,
+	}, nil
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfe91e9 and 5a33821.

📒 Files selected for processing (5)
  • cli/cmd/digger/root.go (1 hunks)
  • ee/cli/cmd/digger/root.go (1 hunks)
  • libs/comment_utils/reporting/reporting.go (5 hunks)
  • libs/comment_utils/reporting/utils.go (1 hunks)
  • libs/spec/providers.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • ee/cli/cmd/digger/root.go
  • libs/spec/providers.go
  • cli/cmd/digger/root.go
🔇 Additional comments (2)
libs/comment_utils/reporting/utils.go (1)

33-37: 🛠️ Refactor suggestion

Improve error handling and resource cleanup

The error handling could be improved by:

  1. Adding more context to the error message
  2. Ensuring proper cleanup of resources on error
-		err := reporter.Report("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)
-		}
+		if err := reporter.Report("Comment Reporter", func(report string) string { return "" }); err != nil {
+			log.Printf("Error reporting source module comment for location %v: %v", location, err)
+			return nil, fmt.Errorf("error reporting source module comment for location %v: %v", location, err)
+		}

Likely invalid or redundant comment.

libs/comment_utils/reporting/reporting.go (1)

93-93: ⚠️ Potential issue

Remove unreachable code in SingleCommentStrategy.Flush

Line 93 contains unreachable code as it follows a return statement.

-	return "", "", nil

Likely invalid or redundant comment.

@@ -28,9 +28,15 @@ 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: SingleCommentStrategy{fmt.Sprintf("Report for location: %v", location), time.Now()},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider initializing the Formatters map in SingleCommentStrategy

The SingleCommentStrategy struct requires a Formatters map, but it's not being initialized here. This could lead to a nil map panic when Report is called.

-			ReportStrategy: SingleCommentStrategy{fmt.Sprintf("Report for location: %v", location), time.Now()},
+			ReportStrategy: SingleCommentStrategy{
+				Title:      fmt.Sprintf("Report for location: %v", location),
+				TimeOfRun:  time.Now(),
+				Formatters: make(map[string]ReportFormat),
+			},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ReportStrategy: SingleCommentStrategy{fmt.Sprintf("Report for location: %v", location), time.Now()},
ReportStrategy: SingleCommentStrategy{
Title: fmt.Sprintf("Report for location: %v", location),
TimeOfRun: time.Now(),
Formatters: make(map[string]ReportFormat),
},

Comment on lines 150 to 155
func (strategy MultipleCommentsStrategy) Report(projectName string, report string, reportFormatter func(report string) string) error {
strategy.Formatters[projectName] = ReportFormat{
ReportTitle: 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add thread safety to map access in MultipleCommentsStrategy

Similar to SingleCommentStrategy, the Formatters map in MultipleCommentsStrategy needs synchronization.

+type MultipleCommentsStrategy struct {
+	Formatters map[string]ReportFormat
+	mu         sync.RWMutex
+}

 func (strategy MultipleCommentsStrategy) Report(projectName string, report string, reportFormatter func(report string) string) error {
+	strategy.mu.Lock()
+	defer strategy.mu.Unlock()
 	strategy.Formatters[projectName] = ReportFormat{
 		ReportTitle:     report,
 		ReportFormatter: reportFormatter,
 	}
 	return nil
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 158 to 159
func (strategy MultipleCommentsStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) (string, string, error) {
return "", "", nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement proper Flush behavior in MultipleCommentsStrategy

The Flush method currently returns empty values without any implementation. Consider implementing proper behavior or documenting why it's intentionally empty.

Either implement the method properly:

 func (strategy MultipleCommentsStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) (string, string, error) {
-	return "", "", nil
+	strategy.mu.RLock()
+	defer strategy.mu.RUnlock()
+	
+	var errors []error
+	for projectName, format := range strategy.Formatters {
+		// Implement flushing logic for each project
+		if err := flushProject(ciService, PrNumber, projectName, format, supportsCollapsibleComment); err != nil {
+			errors = append(errors, err)
+		}
+	}
+	
+	if len(errors) > 0 {
+		return "", "", fmt.Errorf("multiple flush errors: %v", errors)
+	}
+	return "", "", nil
 }

Or document the intentional no-op:

+// Flush is a no-op for MultipleCommentsStrategy as comments are posted immediately in Report
 func (strategy MultipleCommentsStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) (string, string, error) {
 	return "", "", nil
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 71 to 77
func (strategy SingleCommentStrategy) Report(projectName string, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) error {
strategy.Formatters[projectName] = ReportFormat{
ReportTitle: report,
ReportFormatter: reportFormatter,
}
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add thread safety to map access in SingleCommentStrategy

The Formatters map is accessed without synchronization, which could lead to race conditions in concurrent usage.

+type SingleCommentStrategy struct {
+	Title      string
+	TimeOfRun  time.Time
+	Formatters map[string]ReportFormat
+	mu         sync.RWMutex
+}

 func (strategy SingleCommentStrategy) Report(projectName string, report string, reportFormatter func(report string) string, supportsCollapsibleComment bool) error {
+	strategy.mu.Lock()
+	defer strategy.mu.Unlock()
 	strategy.Formatters[projectName] = ReportFormat{
 		ReportTitle:     report,
 		ReportFormatter: reportFormatter,
 	}
 	return nil
 }

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
cli/cmd/digger/main_test.go (1)

929-933: Maintain consistent field ordering in struct initialization

The field ordering in this CiReporter initialization differs from the one in TestGitHubNewPullRequestContext. Also, like the previous instance, it uses MultipleCommentsStrategy when the PR aims to support single comment summary.

 reporter := &reporting.CiReporter{
 	CiService:         &prManager,
 	PrNumber:          prNumber,
-	ReportStrategy:    reporting.NewMultipleCommentsStrategy(),
-	IsSuppressed:      false,
 	IsSupportMarkdown: true,
+	IsSuppressed:      false,
+	ReportStrategy:    reporting.NewSingleCommentStrategy(),
 }
libs/comment_utils/reporting/noop.go (1)

17-17: Name the parameter in Suppress method

The parameter in the Suppress method should be named for better code readability.

-func (reporter NoopReporter) Suppress(string) error {
+func (reporter NoopReporter) Suppress(projectName string) error {
libs/comment_utils/reporting/mock.go (1)

4-6: Consider documenting the new reporting architecture

The interface changes suggest a significant architectural shift in how comments are handled. Consider:

  1. Adding documentation explaining the new comment lifecycle
  2. Providing examples of how the batch processing works
  3. Documenting the relationship between projectName and comment handling

Would you like me to help draft the documentation for these architectural changes?

libs/comment_utils/reporting/reporting.go (4)

15-15: Add documentation for the new IsSuppressed field

The purpose and usage of this new field should be documented to help other developers understand when and how to use it.

+	// IsSuppressed indicates whether reporting should be suppressed for this reporter
 	IsSuppressed      bool

62-65: Document the ReportFormat struct

Add documentation to explain the purpose of this struct and its fields.

+// ReportFormat encapsulates a report's content and its formatting function
 type ReportFormat struct {
+	// Report contains the raw report content
 	Report          string
+	// ReportFormatter is a function that formats the report content
 	ReportFormatter func(report string) string
 }

116-119: Implement the Suppress method

The TODO comment should be addressed by implementing the suppression logic.

Would you like me to help implement the suppression logic for this method?


145-183: Improve error handling in Flush method

The current error handling approach using boolean flags and a single error variable could be improved:

  1. Consider using error groups for concurrent operations
  2. Return early on errors instead of continuing with placeholders
 func (strategy MultipleCommentsStrategy) Flush(ciService ci.PullRequestService, PrNumber int, supportsCollapsibleComment bool) ([]string, []string, error) {
+	var commentIds []string
+	var commentUrls []string
-	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, "")
+			return commentIds, commentUrls, fmt.Errorf("failed to publish comment for project %s: %v", projectName, err)
 		} else {
 			commentIds = append(commentIds, c.Id)
 			commentUrls = append(commentUrls, c.Url)
 		}
 	}
-	if hasError {
-		log.Printf("could not publish all comments")
-		return commentIds, commentUrls, latestError
-	}
 	return commentIds, commentUrls, nil
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a33821 and eac4651.

📒 Files selected for processing (14)
  • cli/cmd/digger/main_test.go (2 hunks)
  • cli/cmd/digger/root.go (1 hunks)
  • cli/pkg/digger/digger.go (9 hunks)
  • cli/pkg/digger/digger_test.go (2 hunks)
  • cli/pkg/integration/integration_test.go (1 hunks)
  • ee/cli/cmd/digger/root.go (1 hunks)
  • libs/comment_utils/reporting/core.go (1 hunks)
  • libs/comment_utils/reporting/mock.go (1 hunks)
  • libs/comment_utils/reporting/noop.go (1 hunks)
  • libs/comment_utils/reporting/reporting.go (1 hunks)
  • libs/comment_utils/reporting/utils.go (1 hunks)
  • libs/execution/execution.go (6 hunks)
  • libs/locking/locking.go (6 hunks)
  • libs/spec/providers.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • libs/comment_utils/reporting/utils.go
  • libs/spec/providers.go
  • ee/cli/cmd/digger/root.go
  • libs/locking/locking.go
  • cli/cmd/digger/root.go
  • libs/execution/execution.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
cli/pkg/digger/digger.go

549-549: Error return value of reporter.Suppress is not checked

(errcheck)

🔇 Additional comments (8)
cli/pkg/digger/digger.go (2)

174-179: LGTM! Consistent error handling pattern

The error handling pattern is well-implemented across all reporting functions. Errors are properly logged without interrupting the execution flow, which is appropriate for a reporting system.

Also applies to: 288-328, 331-331, 374-374


Line range hint 495-542: LGTM! Well-structured reporting functions

The reporting functions are well-structured with consistent patterns for:

  • Markdown support checking
  • Error handling
  • Message formatting
libs/comment_utils/reporting/core.go (1)

4-6: Verify the impact of Reporter interface changes across the codebase

The interface changes suggest a significant architectural shift:

  1. Addition of projectName parameter indicates multi-project support
  2. Return type changes in Flush() suggest batch processing of comments
  3. Removal of commentId and commentUrl from Report() implies a different comment handling strategy

Let's verify the impact of these changes:

✅ Verification successful

Let me generate additional scripts to verify the interface implementations and their usage patterns:


Let me try one more search pattern to find the implementations:


Interface changes are properly implemented across the codebase

Based on the verification results:

  1. All implementations (NoopReporter, CiReporter, StdOutReporter, SingleCommentStrategy, MultipleCommentsStrategy, MockReporter) correctly implement the updated interface with the new signatures
  2. All callers of these methods (from previous search) are using the new parameter structure
  3. The Flush() return type change to ([]string, []string, error) is consistently implemented
  4. The projectName parameter is properly handled in all implementations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all implementations of the Reporter interface
ast-grep --pattern 'type $_ struct {
  $$$
}

func ($_ $_) Report($$$) $_ {
  $$$
}

func ($_ $_) Flush() ($$$) {
  $$$
}

func ($_ $_) Suppress($$$) $_ {
  $$$
}'

# Search for existing calls to these methods that might need updates
rg -A 2 '\.(Report|Flush|Suppress)\('

Length of output: 7628


Script:

#!/bin/bash
# Search for Reporter interface implementations
ast-grep --pattern 'type $name struct {
  $$$
}

func ($r $_) Report(projectName string, report string, reportFormatting func(report string) string) error {
  $$$
}'

# Search for Flush implementations to verify return type changes
ast-grep --pattern 'func ($r $_) Flush() ([]string, []string, error) {
  $$$
}'

# Search for old interface signatures that might need updates
rg 'func.*Report.*\(.*\).*\(string,\s*string,\s*error\)'
rg 'func.*Flush.*\(.*\).*\(string,\s*string,\s*error\)'

Length of output: 366


Script:

#!/bin/bash
# Search for struct types that might implement Reporter
ast-grep --pattern 'type $name struct {
  $$$
}'

# Search for all Report method implementations
rg -A 5 '^func.*Report\('

# Search for all Flush method implementations
rg -A 5 '^func.*Flush\('

# Search for all Suppress method implementations
rg -A 5 '^func.*Suppress\('

Length of output: 8860

libs/comment_utils/reporting/reporting.go (2)

121-123: ⚠️ Potential issue

Add mutex for thread-safe map access

Similar to SingleCommentStrategy, the formatters map needs synchronization.

 type MultipleCommentsStrategy struct {
+	mu         sync.RWMutex
 	formatters map[string][]ReportFormat
 	TimeOfRun  time.Time
 }

Likely invalid or redundant comment.


67-70: ⚠️ Potential issue

Add mutex for thread-safe map access

The formatters map is accessed from multiple methods without synchronization, which could lead to race conditions.

 type SingleCommentStrategy struct {
 	TimeOfRun  time.Time
+	mu         sync.RWMutex
 	formatters map[string][]ReportFormat
 }

Likely invalid or redundant comment.

cli/pkg/digger/digger_test.go (2)

250-256: LGTM!

The changes correctly initialize the MultipleCommentsStrategy for testing.


301-306: LGTM!

The changes correctly initialize the MultipleCommentsStrategy for the destroy test case.

cli/pkg/integration/integration_test.go (1)

47-52: LGTM!

The changes correctly initialize the MultipleCommentsStrategy for integration testing.

Comment on lines +543 to 552
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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle error return value from reporter.Suppress

The error return value from reporter.Suppress is not checked, which could hide important errors.

Add error handling:

 func reportEmptyPlanOutput(projectName string, reporter reporting.Reporter, projectId string) {
     identityFormatter := func(comment string) string {
         return comment
     }
     err := reporter.Report(projectName, "→ No changes in terraform output for "+projectId, identityFormatter)
-    reporter.Suppress("")
+    if err := reporter.Suppress(""); err != nil {
+        log.Printf("Failed to suppress report: %v", err)
+    }
     if err != nil {
         log.Printf("Failed to report plan. %v", err)
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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)
}
func reportEmptyPlanOutput(projectName string, reporter reporting.Reporter, projectId string) {
identityFormatter := func(comment string) string {
return comment
}
err := reporter.Report(projectName, "→ No changes in terraform output for "+projectId, identityFormatter)
// suppress the comment (if reporter is suppressible)
if err := reporter.Suppress(""); err != nil {
log.Printf("Failed to suppress report: %v", err)
}
if err != nil {
log.Printf("Failed to report plan. %v", err)
}
🧰 Tools
🪛 golangci-lint (1.62.2)

549-549: Error return value of reporter.Suppress is not checked

(errcheck)

Comment on lines +127 to +134
_, 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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add bounds checking for jobPrCommentUrls array

The code assumes jobPrCommentUrls has at least one element. This could lead to a panic if the array is empty.

Add a bounds check before accessing the first element:

 _, 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)
 }
+if len(jobPrCommentUrls) == 0 {
+    return false, false, fmt.Errorf("no comment URLs returned from reporter")
+}
 currentJob := jobs[0]
 jobPrCommentUrl := jobPrCommentUrls[0]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_, 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]
_, 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)
}
if len(jobPrCommentUrls) == 0 {
return false, false, fmt.Errorf("no comment URLs returned from reporter")
}
currentJob := jobs[0]
jobPrCommentUrl := jobPrCommentUrls[0]

Comment on lines +898 to +902
CiService: &prManager,
PrNumber: prNumber,
IsSupportMarkdown: true,
IsSuppressed: false,
ReportStrategy: reporting.NewMultipleCommentsStrategy(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add test coverage for single comment strategy

The PR aims to support single comment summary, but the test initializes MultipleCommentsStrategy. Consider:

  1. Adding test cases that verify the single comment strategy
  2. Validating the reporting behavior with different strategies
 reporter := &reporting.CiReporter{
 	CiService:         &prManager,
 	PrNumber:          prNumber,
 	IsSupportMarkdown: true,
 	IsSuppressed:      false,
-	ReportStrategy:    reporting.NewMultipleCommentsStrategy(),
+	ReportStrategy:    reporting.NewSingleCommentStrategy(),
 }

+func TestGitHubNewPullRequestContextWithSingleComment(t *testing.T) {
+	// Add test case for single comment strategy
+	// Verify the reporting behavior
+}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +7 to +9
func (mockReporter *MockReporter) Report(projectName string, report string, reportFormatting func(report string) string) error {
mockReporter.commands = append(mockReporter.commands, "Report")
return "", "", nil
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance mock implementation consistency

The mock implementation has several areas for improvement:

  1. Command tracking is inconsistent across methods
  2. The Suppress method parameter is unnamed
  3. Flush method should track command execution
 func (mockReporter *MockReporter) Report(projectName string, report string, reportFormatting func(report string) string) error {
 	mockReporter.commands = append(mockReporter.commands, "Report")
 	return nil
 }

 func (mockReporter *MockReporter) Flush() ([]string, []string, error) {
+	mockReporter.commands = append(mockReporter.commands, "Flush")
 	return []string{}, []string{}, nil
 }

-func (mockReporter *MockReporter) Suppress(string) error {
+func (mockReporter *MockReporter) Suppress(projectName string) error {
+	mockReporter.commands = append(mockReporter.commands, "Suppress")
 	return nil
 }

Also applies to: 12-13, 16-16

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
backend/controllers/github.go (1)

503-509: ⚠️ Potential issue

Fix incorrect error message in placeholder comment creation

The error message incorrectly references "strconv.ParseInt" when the error is from publishing a comment.

Apply this fix:

 	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder")
 	if err != nil {
-		log.Printf("strconv.ParseInt error: %v", err)
+		log.Printf("failed to publish placeholder comment: %v", err)
 		commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
 		return fmt.Errorf("comment reporter error: %v", err)
 	}
🧹 Nitpick comments (1)
backend/controllers/github.go (1)

504-504: Fix typo in placeholder comment text

The word "placehoder" is misspelled in the placeholder comment text. It should be "placeholder".

Apply this fix in both locations:

-	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placehoder")
+	placeholderComment, err := ghService.PublishComment(prNumber, "digger report placeholder")

Also applies to: 887-887

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6df110b and 44c1aff.

📒 Files selected for processing (3)
  • backend/controllers/github.go (3 hunks)
  • backend/services/spec.go (1 hunks)
  • libs/spec/providers.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/spec/providers.go
  • backend/services/spec.go

Comment on lines +886 to +891
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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Fix incorrect error message and consider refactoring duplicated code

  1. The error message incorrectly references "strconv.ParseInt" when the error is from publishing a comment.
  2. This code block is duplicated from the handlePullRequestEvent function.

First, fix the error message:

 	placeholderComment, err := ghService.PublishComment(issueNumber, "digger report placehoder")
 	if err != nil {
-		log.Printf("strconv.ParseInt error: %v", err)
+		log.Printf("failed to publish placeholder comment: %v", err)
 		commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
 		return fmt.Errorf("comment reporter error: %v", err)
 	}

Consider extracting the placeholder comment creation into a shared helper function to avoid code duplication:

func createPlaceholderComment(ghService *dg_github.GithubService, number int, commentReporterManager *utils.CommentReporterManager) (*github.IssueComment, error) {
    placeholderComment, err := ghService.PublishComment(number, "digger report placeholder")
    if err != nil {
        log.Printf("failed to publish placeholder comment: %v", err)
        commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
        return nil, fmt.Errorf("comment reporter error: %v", err)
    }
    return placeholderComment, nil
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (1)
backend/controllers/github.go (1)

531-536: ⚠️ Potential issue

Fix incorrect error message and duplicate code

  1. The error message incorrectly references "strconv.ParseInt" when the error is from publishing a comment
  2. This code block is duplicated between handlePullRequestEvent and handleIssueCommentEvent

Also applies to: 951-956

🧹 Nitpick comments (22)
backend/utils/ai.go (5)

12-16: Ensure HTTP client timeouts are set for reliability

You might want to provide a custom http.Client with a timeout to avoid hanging indefinitely on slow or unresponsive servers.

 func GenerateTerraformCode(appCode string, generationEndpoint string, apiToken string) (string, error) {
   payload := map[string]string{
     "code": appCode,
   }
+  client := &http.Client{
+    Timeout: 30 * time.Second,
+  }

49-55: Differentiate between 4xx/5xx errors or handle them gracefully

Currently, a 400 response is singled out, and all other non-200 responses return a generic error message. Distinguishing between client (4xx) and server (5xx) errors could improve observability and debugging.


57-67: Leverage a single GeneratorResponse type across both functions

Consider placing GeneratorResponse in a shared scope if you’re using it in both methods. This reduces duplication and eases maintenance.


72-99: Reuse client between functions

Similar to the first function, consider reusing the same http.Client instance or introduce a shared helper method to avoid duplicating request handling logic.


109-112: Include response details when logging 400 errors

In addition to logging the raw body, consider adding more contextual information about the error to help diagnose user input issues or server validation failures.

backend/controllers/projects.go (1)

655-717: Graceful handling of missing AI summary parameters

Consider adding more structured logging for each error scenario. In particular, if ai summary endpoint or apiToken is missing, you might store a structured error in the database or post a more user-friendly comment in the PR.

libs/digger_config/terragrunt/atlantis/generate.go (6)

142-142: Populate error detail proactively

You’re setting a cached result with err before fully parsing dependencies. If subsequent logic modifies this error, consider storing more detailed data.


259-259: Cascading dependencies edge case

When cascadeDependencies is true, failures to retrieve child dependencies are silently ignored (continue). Consider adding logs or fallback logic to improve traceability.


366-382: Guard the return early for short-circuited logic

When triggerProjectsFromDirOnly is true and relativeSourceDir == ".", it simply returns nil. This is valid, but consider logging the scenario for debugging consistency.


480-480: Add test coverage for createHclProject logic

Unit tests for various local overrides or skipping with atlantis_skip might help prevent regressions.


553-553: Skipping project logic clarity

Because getDependencies(...) can return nil to indicate a project skip, consider documenting that behavior clearly to avoid confusion in future.


706-706: Clarify concurrency settings

Parse(...) uses a semaphore with a weight of 10. Is this a chosen concurrency limit for all calls, or should it be configurable based on environment constraints?

backend/tasks/runs_test.go (1)

146-146: Added parameters for AI summary in the test

Be cautious about always passing empty string and false. You may want to add a test scenario that sets them to a non-default value to verify that logic in downstream methods works as intended.

libs/digger_config/digger_config.go (2)

33-45: Robust file loading approach.

Using separate cases for digger.yml vs. digger.yaml is good. Consider adding more granular logs or telemetry (if relevant) to assist in debugging file-not-found scenarios.


579-579: Potential edge cases in path normalization.

When normalizing patterns for Autoplan.WhenModified, be mindful of unusual directory structures or symbolic links. The logic here is acceptable, but additional checks might be warranted if you support more complex setups.

backend/migrations/20241229112312.sql (1)

1-2: Add down migration for rollback support.

While the migration adds the necessary columns correctly, consider adding a down migration to support rollbacks:

-- Up migration
ALTER TABLE "public"."digger_batches" 
  ADD COLUMN "ai_summary_comment_id" text NULL,
  ADD COLUMN "report_terraform_outputs" boolean NULL;

-- Down migration
ALTER TABLE "public"."digger_batches"
  DROP COLUMN "ai_summary_comment_id",
  DROP COLUMN "report_terraform_outputs";
backend/scripts/curl_bootstrap.sh (1)

22-26: Pin the web service image version.

Using latest tag can lead to unexpected behavior. Consider pinning to a specific version:

-    image: "registry.digger.dev/diggerhq/digger_backend:latest"
+    image: "registry.digger.dev/diggerhq/digger_backend:v1.x.x"
libs/digger_config/config.go (3)

18-20: Add documentation for new configuration fields.

Please add documentation comments for the new fields to explain their purpose and expected values:

+    // Reporting contains configuration for various reporting features
     Reporting                  ReporterConfig
+    // ReportTerraformOutputs when true, includes Terraform outputs in the report
     ReportTerraformOutputs     bool

22-24: Document ReporterConfig struct and add validation.

Add documentation and consider adding validation for the AiSummary field:

+// ReporterConfig contains settings for different types of reporting features
 type ReporterConfig struct {
+    // AiSummary when true, enables AI-powered summary generation
     AiSummary bool
 }

+// Validate ensures the reporter configuration is valid
+func (r *ReporterConfig) Validate() error {
+    // Add validation logic if needed
+    return nil
+}

37-52: Consider adding validation for Project configuration.

The Project struct contains critical configuration. Consider adding validation to ensure required fields are properly set:

func (p *Project) Validate() error {
    if p.Name == "" {
        return fmt.Errorf("project name is required")
    }
    if p.Dir == "" {
        return fmt.Errorf("project directory is required")
    }
    // Add more validation as needed
    return nil
}
backend/utils/graphs.go (1)

17-17: Consider breaking down the long parameter list.

The function signature has grown quite long with 19 parameters, which could make it harder to maintain and use correctly.

Consider introducing a configuration struct to group related parameters:

type JobConversionConfig struct {
    JobType               scheduler.DiggerCommand
    VCSType              models.DiggerVCSType
    OrganisationId       uint
    GithubInstallationId int64
    GitlabProjectId      int
    Branch               string
    PRNumber            int
    RepoInfo            struct {
        Owner     string
        Name      string
        FullName  string
        CommitSha string
    }
    Comments struct {
        CommentId                   int64
        PlaceholderCommentIdForReport *string
        AiSummaryCommentId         string
    }
    Config struct {
        DiggerConfigStr       string
        ReportTerraformOutput bool
    }
}

func ConvertJobsToDiggerJobs(config JobConversionConfig, jobsMap map[string]scheduler.Job, projectMap map[string]configuration.Project, projectsGraph graph.Graph[string, configuration.Project]) (*uuid.UUID, map[string]*models.DiggerJob, error)
action.yml (1)

431-433: Remove trailing spaces

The cache configuration has trailing spaces on line 433 that should be removed.

 path: ${{ github.workspace }}/cache
 key: digger-cache-${{ hashFiles('**/cache') }}
-        
+
🧰 Tools
🪛 yamllint (1.35.1)

[error] 433-433: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a48f98 and 60dbc32.

⛔ Files ignored due to path filters (6)
  • backend/migrations/atlas.sum is excluded by !**/*.sum
  • cli/go.sum is excluded by !**/*.sum
  • ee/backend/go.sum is excluded by !**/*.sum
  • ee/cli/go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
  • libs/go.sum is excluded by !**/*.sum
📒 Files selected for processing (25)
  • action.yml (2 hunks)
  • backend/controllers/github.go (10 hunks)
  • backend/controllers/github_test.go (4 hunks)
  • backend/controllers/projects.go (3 hunks)
  • backend/migrations/20240403155357_drop_dup_idx.sql (1 hunks)
  • backend/migrations/20241229112312.sql (1 hunks)
  • backend/models/scheduler.go (1 hunks)
  • backend/models/storage.go (1 hunks)
  • backend/models/storage_test.go (1 hunks)
  • backend/scripts/curl_bootstrap.sh (1 hunks)
  • backend/tasks/runs_test.go (1 hunks)
  • backend/utils/ai.go (1 hunks)
  • backend/utils/graphs.go (2 hunks)
  • cli/go.mod (2 hunks)
  • docs/mint.json (0 hunks)
  • ee/backend/controllers/gitlab.go (2 hunks)
  • ee/backend/go.mod (8 hunks)
  • ee/backend/hooks/github.go (1 hunks)
  • ee/cli/go.mod (1 hunks)
  • libs/digger_config/config.go (2 hunks)
  • libs/digger_config/converters.go (3 hunks)
  • libs/digger_config/digger_config.go (3 hunks)
  • libs/digger_config/terragrunt/atlantis/generate.go (11 hunks)
  • libs/digger_config/yaml.go (2 hunks)
  • libs/go.mod (0 hunks)
💤 Files with no reviewable changes (2)
  • docs/mint.json
  • libs/go.mod
✅ Files skipped from review due to trivial changes (1)
  • backend/migrations/20240403155357_drop_dup_idx.sql
🚧 Files skipped from review as they are similar to previous changes (4)
  • backend/models/scheduler.go
  • backend/models/storage.go
  • ee/backend/hooks/github.go
  • ee/backend/controllers/gitlab.go
🧰 Additional context used
🪛 Shellcheck (0.10.0)
backend/scripts/curl_bootstrap.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

🪛 golangci-lint (1.62.2)
backend/controllers/github.go

538-538: no new variables on left side of :=

(typecheck)


958-958: no new variables on left side of :=

(typecheck)

🪛 yamllint (1.35.1)
action.yml

[error] 433-433: trailing spaces

(trailing-spaces)

🔇 Additional comments (23)
backend/controllers/projects.go (2)

20-20: OS import usage

The addition of the "os" import is valid for retrieving environment variables (e.g., os.Getenv). Ensure safe handling of sensitive data from environment variables when logging or storing.


485-489: Verify post-job summary creation

You're calling CreateTerraformOutputsSummary immediately after updating the batch status. Ensure that any partial failures within CreateTerraformOutputsSummary do not block other routine tasks, like final notifications or auto-merge.

libs/digger_config/terragrunt/atlantis/generate.go (4)

Line range hint 121-125: Caching dependencies locally

The use of requestGroup.Do and getDependenciesCache is an effective approach. Ensure threading is safe around all shared state (which it appears to be).


315-339: Design promotes clean reuse with createBaseProject

Good approach extracting common Atlantis project initialization here. Ensures consistency across different creation workflows.


352-356: Add top-level directory checks

The lines create relativeDependencies always. Be mindful that “.tf” may inadvertently pick up hidden or extraneous files.


444-453: Project naming collisions

You rely on projectNameFromDir to name both the project and workspace. In large monorepos, different subdirectories might somehow yield the same sanitized name. Consider checking for collisions or retaining additional path segments.

backend/models/storage_test.go (1)

154-154: New parameters in CreateDiggerBatch

You’ve introduced aiSummaryCommentId as an empty string and reportTerraformOutputs as false. If future tests require verifying these fields, consider adding more explicit coverage that sets different values.

libs/digger_config/converters.go (3)

50-58: Prefer consistent initialization style.

The conditional pointer assignment and subsequent block for awsCognitoOidc look consistent. Ensure you consistently initialize and assign default regions or fallback values for all supported providers if the code evolves to support new fields.


172-183: Concise utility addition looks good.

The copyReporterConfig function provides a neat way to handle default vs. provided values. If additional fields are introduced in the future, ensure you expand this copier function to safely handle them without overshadowing defaults.


198-205: New reporting logic is coherent.

Good approach in setting ReportTerraformOutputs defaults and calling copyReporterConfig. This maintains backward compatibility and ensures new reporting fields are exposed effectively.

libs/digger_config/yaml.go (3)

22-24: Clarity in naming the new reporting fields.

Adding ReportTerraformOutputs and Reporting with a pointer to ReportingConfigYaml is clear and semantically consistent. Ensure that usage downstream handles nil pointers correctly.


25-27: Good introduction of the ReportingConfigYaml struct.

This is straightforward. In future expansions, consider the possibility that more reporting-related fields might be added to reduce repeated structures.


146-163: Ensure Terragrunt parsing logic consistently uses the new TriggerProjectsFromDirOnly field.

The newly added fields in TerragruntParsingConfig appear coherent with the file’s structure. Verify that the rest of the codebase properly interprets this option to avoid partial application of filters.

libs/digger_config/digger_config.go (1)

553-553: Adding TriggerProjectsFromDirOnly parameter recognized.

Looks consistent with the logic in hydrateDiggerConfigYamlWithTerragrunt. Verify that tests capture scenarios when TriggerProjectsFromDirOnly is enabled to confirm correct project detection.

backend/controllers/github_test.go (4)

734-734: Extra parameter integration is seamless.

This test demonstrates how the new boolean parameter is passed to ConvertJobsToDiggerJobs. The usage looks straightforward.


763-763: Parameter expansion maintained consistency.

Ensuring that all necessary parameters (aiSummaryCommentId, reportTerraformOutput, etc.) are captured in test scenarios is crucial. This looks correct.


796-796: Multiple projects scenario well-covered.

Again, passing the new parameter in a multi-project test ensures consistency across job creation logic. Good job.


841-841: Three-level dependency scenario remains valid.

Including the new parameter also for this deeper dependency test ensures no breakage. The coverage is thorough.

cli/go.mod (1)

Line range hint 58-84: Verify AWS SDK version compatibility across packages.

The AWS SDK packages have been updated to newer versions. While the updates appear consistent, it's important to verify compatibility with the rest of the codebase.

Run the following script to check for any version mismatches:

✅ Verification successful

AWS SDK dependencies are correctly updated and used consistently

The verification shows that AWS SDK packages are used consistently across the codebase in various components:

  • Storage components (S3)
  • Authentication (Cognito, STS)
  • DynamoDB operations
  • Configuration and credentials management

All imports are using the same AWS SDK v2 family (aws-sdk-go-v2), and the updated versions in go.mod maintain compatibility across these packages. The dependencies marked as "indirect" are correctly labeled as they are transitive dependencies required by the directly used AWS packages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for AWS SDK version consistency across the codebase

# Search for AWS SDK imports and their versions
rg --type go "github.com/aws/aws-sdk-go-v2" -A 1

Length of output: 4040

ee/cli/go.mod (1)

61-84: LGTM! Dependencies are consistent with cli/go.mod.

The AWS SDK version updates and go-gitlab addition mirror the changes in cli/go.mod, maintaining consistency across modules.

action.yml (1)

278-278: LGTM: OpenTofu setup action version update

The update from v1.0.3 to v1.0.5 is a minor version bump that likely includes bug fixes and improvements.

ee/backend/go.mod (1)

14-14: LGTM: Dependency updates

The changes include:

  1. Version updates for existing dependencies (e.g., github.com/samber/lo to v1.46.0)
  2. New dependencies added (e.g., github.com/diggerhq/digger/ee/drift)
  3. Database-related dependencies added (e.g., gorm.io/datatypes, gorm.io/driver/mysql)

These changes align with the PR's objectives for supporting AI summary functionality.

Also applies to: 20-20, 36-36, 96-96, 118-119, 129-130, 186-188, 303-309

backend/controllers/github.go (1)

312-320: LGTM: Added panic recovery to event handlers

Good addition of panic recovery in both handlePullRequestEvent and handleIssueCommentEvent handlers. This improves the robustness of the application by preventing crashes and logging panic details for debugging.

Also applies to: 727-735

Comment on lines 27 to 34
echo "GITHUB_ORG=${GITHUB_ORG:-your_github_org}
HOSTNAME=http://DIGGER_HOSTNAME
BEARER_AUTH_TOKEN=$(openssl rand -base64 12)
DATABASE_URL=postgres://postgres:root@postgres:5432/postgres?sslmode=disable
HTTP_BASIC_AUTH=1
HTTP_BASIC_AUTH_USERNAME=myorg
HTTP_BASIC_AUTH_PASSWORD=$(openssl rand -base64 12)
ALLOW_DIRTY=false" > .env && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance environment variable security.

  1. Consider using a .env.example file for template values
  2. Add input validation for critical environment variables
  3. Document the required environment variables

Would you like me to generate a more secure environment variable handling implementation?

@@ -0,0 +1,36 @@
echo 'version: '"'"'3.7'"'"'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add shebang line for shell script.

Add a shebang line at the beginning of the script to specify the shell:

+#!/bin/bash
 echo 'version: '"'"'3.7'"'"'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo 'version: '"'"'3.7'"'"'
#!/bin/bash
echo 'version: '"'"'3.7'"'"'
🧰 Tools
🪛 Shellcheck (0.10.0)

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

Comment on lines 5 to 9
image: postgres:alpine
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=root
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance PostgreSQL container security and persistence.

Consider these improvements for the PostgreSQL service:

  1. Use a specific version tag instead of alpine for better reproducibility
  2. Use environment variables for sensitive data
  3. Add volume persistence for data
   postgres:
-    image: postgres:alpine
+    image: postgres:15-alpine
     ports:
       - "5432:5432"
     environment:
-      - POSTGRES_PASSWORD=root
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-root}
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+
+volumes:
+  postgres_data:

Committable suggestion skipped: line range outside the PR's diff.

@@ -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)
batch, err := models.DB.CreateDiggerBatch(vcsType, githubInstallationId, repoOwner, repoName, repoFullName, prNumber, diggerConfigStr, branch, jobType, &commentId,placeholderCommentIdForReport, gitlabProjectId, aiSummaryCommentId, reportTerraformOutput)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix parameter ordering in CreateDiggerBatch call.

The parameter ordering in the CreateDiggerBatch call appears incorrect. The placeholderCommentIdForReport parameter is placed between commentId and gitlabProjectId, breaking the logical grouping of related parameters.

Apply this diff to fix the parameter ordering:

-	batch, err := models.DB.CreateDiggerBatch(vcsType, githubInstallationId, repoOwner, repoName, repoFullName, prNumber, diggerConfigStr, branch, jobType, &commentId,placeholderCommentIdForReport,  gitlabProjectId, aiSummaryCommentId, reportTerraformOutput)
+	batch, err := models.DB.CreateDiggerBatch(vcsType, githubInstallationId, repoOwner, repoName, repoFullName, prNumber, diggerConfigStr, branch, jobType, &commentId, gitlabProjectId, aiSummaryCommentId, placeholderCommentIdForReport, reportTerraformOutput)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 517 to 522
var aiSummaryCommentId = ""
if config.Reporting.AiSummary {
aiSummaryComment, err := ghService.PublishComment(prNumber, "AI Summary will be posted here after completion")
if err != nil {
log.Printf("could not post ai summary comment: %v", err)
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not post ai comment summary comment id: %v", err))
return fmt.Errorf("could not post ai summary comment: %v", err)
}
aiSummaryCommentId = aiSummaryComment.Id
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix duplicate AI summary implementation

The AI summary comment creation logic is duplicated between handlePullRequestEvent and handleIssueCommentEvent. Consider extracting this into a shared helper function.

func createAiSummaryComment(ghService *dg_github.GithubService, issueNumber int, commentReporterManager utils.CommentReporterManager) (string, error) {
    if !config.Reporting.AiSummary {
        return "", nil
    }
    aiSummaryComment, err := ghService.PublishComment(issueNumber, "AI Summary will be posted here after completion")
    if err != nil {
        log.Printf("could not post ai summary comment: %v", err)
        commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not post ai comment summary comment id: %v", err))
        return "", fmt.Errorf("could not post ai summary comment: %v", err)
    }
    return aiSummaryComment.Id, nil
}

Also applies to: 938-947

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix redeclaration of batchId variable

The batchId variable is redeclared using := operator when it's already defined. This will cause a compilation error.

-batchId, _, err := utils.ConvertJobsToDiggerJobs(
+batchId, _, err = utils.ConvertJobsToDiggerJobs(

Also applies to: 958-958

🧰 Tools
🪛 golangci-lint (1.62.2)

538-538: no new variables on left side of :=

(typecheck)

Comment on lines 1031 to 1171
for _, line := range lines {
if strings.HasPrefix(line, "+") && !strings.HasPrefix(line, "+++") {
appCode.WriteString(strings.TrimPrefix(line, "+"))
appCode.WriteString("\n")
}
}
}
appCode.WriteString("\n")
}

if appCode.Len() == 0 {
return "", fmt.Errorf("no code changes found in commit %s. Please ensure the PR contains added or modified code", *commitSha)
}

return appCode.String(), nil
}

appCode, err := getCodeFromCommit(ghService, repoOwner, repoName, commitSha, project.Dir)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: Failed to get code content: %v", err))
log.Printf("Error getting code content: %v", err)
return fmt.Errorf("error getting code content: %v", err)
}

commentReporterManager.UpdateComment(fmt.Sprintf(":white_check_mark: Successfully loaded code from commit"))

log.Printf("the app code is: %v", appCode)

commentReporterManager.UpdateComment(fmt.Sprintf("Generating terraform..."))
terraformCode, err := utils.GenerateTerraformCode(appCode, generationEndpoint, apiToken)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not generate terraform code: %v", err))
log.Printf("could not generate terraform code: %v", err)
return fmt.Errorf("could not generate terraform code: %v", err)
}

commentReporterManager.UpdateComment(fmt.Sprintf(":white_check_mark: Generated terraform"))

// comment terraform code to project dir
//project.Dir
log.Printf("terraform code is %v", terraformCode)

baseTree, _, err := ghService.Client.Git.GetTree(context.Background(), repoOwner, repoName, *commitSha, false)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: Failed to get base tree: %v", err))
log.Printf("Error getting base tree: %v", err)
return fmt.Errorf("error getting base tree: %v", err)
}

// Create a new tree with the new file
treeEntries := []*github.TreeEntry{
{
Path: github.String(filepath.Join(project.Dir, fmt.Sprintf("generated_%v.tf", issueNumber))),
Mode: github.String("100644"),
Type: github.String("blob"),
Content: github.String(terraformCode),
},
}

newTree, _, err := ghService.Client.Git.CreateTree(context.Background(), repoOwner, repoName, *baseTree.SHA, treeEntries)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: Failed to create new tree: %v", err))
log.Printf("Error creating new tree: %v", err)
return fmt.Errorf("error creating new tree: %v", err)
}

// Create the commit
commitMsg := fmt.Sprintf("Add generated Terraform code for %v", projectName)
commit := &github.Commit{
Message: &commitMsg,
Tree: newTree,
Parents: []*github.Commit{{SHA: commitSha}},
}

newCommit, _, err := ghService.Client.Git.CreateCommit(context.Background(), repoOwner, repoName, commit, nil)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: Failed to commit Terraform file: %v", err))
log.Printf("Error committing Terraform file: %v", err)
return fmt.Errorf("error committing Terraform file: %v", err)
}

// Update the reference to point to the new commit
ref := &github.Reference{
Ref: github.String(fmt.Sprintf("refs/heads/%s", *branch)),
Object: &github.GitObject{
SHA: newCommit.SHA,
},
}
_, _, err = ghService.Client.Git.UpdateRef(context.Background(), repoOwner, repoName, ref, false)
if err != nil {
commentReporterManager.UpdateComment(fmt.Sprintf(":x: Failed to update branch reference: %v", err))
log.Printf("Error updating branch reference: %v", err)
return fmt.Errorf("error updating branch reference: %v", err)
}

commentReporterManager.UpdateComment(":white_check_mark: Successfully generated and committed Terraform code")
return nil
}
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Security review of code generation implementation

The code generation implementation has several security concerns that need to be addressed:

  1. The API token is used without any validation of its presence or format
  2. The HTTP client lacks important security configurations:
    • No timeout settings, which could lead to hung connections
    • No TLS configuration validation
  3. The response handling could be improved:
    • Large response bodies are not limited, risking memory issues
    • Generic error message for non-200 responses could mask security issues
  4. The generated code is committed without any validation of its content or size

Recommendations:

  • Add validation for the API token's presence and format
  • Configure HTTP client with appropriate timeouts and TLS settings
  • Implement response size limits
  • Add proper validation of the generated Terraform code before committing
  • Consider adding rate limiting for the generation endpoint
  • Improve error handling with more specific error messages for different failure scenarios
🔗 Analysis chain

Review the GenerateTerraformFromCode implementation

The implementation has several areas that could be improved:

  1. Consider adding rate limiting for API calls
  2. The 1MB patch size limit should be configurable
  3. Consider adding retry logic for GitHub API calls
  4. Add validation for the generated Terraform code before committing

Also, verify the security implications:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential security issues in the code generation endpoint configuration
rg -A 5 "DIGGER_GENERATION_ENDPOINT|DIGGER_GENERATION_API_TOKEN" --type yaml

Length of output: 78


Script:

#!/bin/bash
# Let's check for environment variables and configuration in other file types
rg -A 5 "DIGGER_GENERATION_ENDPOINT|DIGGER_GENERATION_API_TOKEN"

# Check how these variables are used in the codebase
ast-grep --pattern 'os.Getenv("DIGGER_GENERATION_ENDPOINT")'
ast-grep --pattern 'os.Getenv("DIGGER_GENERATION_API_TOKEN")'

# Check the GenerateTerraformCode function implementation for security measures
ast-grep --pattern 'func GenerateTerraformCode($$$)'

Length of output: 4192

# Conflicts:
#	backend/controllers/github.go
#	backend/migrations/atlas.sum
#	backend/models/scheduler.go
#	backend/models/storage.go
#	backend/services/spec.go
#	backend/utils/graphs.go
#	ee/backend/controllers/gitlab.go
#	ee/backend/hooks/github.go
@motatoes motatoes force-pushed the feat/support-single-comment-summary branch from 60dbc32 to 1d3bc41 Compare January 3, 2025 05:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (2)
backend/controllers/github.go (2)

Line range hint 315-315: Add missing import for 'runtime/debug' package

The code uses debug.Stack() but the runtime/debug package is not imported. This causes compilation failures.

Add this import to fix the compilation error:

+"runtime/debug"

Security improvements needed in code generation implementation

Based on the verification results, several security concerns in the code generation implementation are confirmed:

  1. HTTP client configuration in GenerateTerraformCode:

    • No timeout settings for HTTP requests
    • No TLS configuration validation
    • No response size limits (only patch size is limited to 1MB in a different context)
  2. API token handling:

    • While the token's presence is checked, there's no validation of its format or strength
    • Token is used directly in the Authorization header without any sanitization
  3. Response handling:

    • io.ReadAll is used without size limits, potentially leading to memory exhaustion
    • Error messages at lines 49-55 are too generic and could mask security issues
    • No validation of generated code content before using it

Recommendations:

  • Add request timeout settings
  • Implement response size limits
  • Add proper TLS configuration
  • Validate and sanitize API token
  • Implement content validation for generated code
  • Improve error handling with more specific messages
🔗 Analysis chain

Line range hint 1066-1067: Improve security in code generation implementation

The code generation implementation has several security concerns that need to be addressed:

  1. The API token is used without any validation of its presence or format
  2. The HTTP client lacks important security configurations:
    • No timeout settings
    • No TLS configuration validation
  3. The response handling could be improved:
    • Large response bodies are not limited
    • Generic error message for non-200 responses could mask security issues
  4. The generated code is committed without any validation of its content or size

Run this script to check for potential security issues:

Also applies to: 1073-1074


Let me gather more information about the security measures in place.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential security issues in the code generation endpoint configuration
rg -A 5 "DIGGER_GENERATION_ENDPOINT|DIGGER_GENERATION_API_TOKEN" --type yaml

# Let's check for environment variables and configuration in other file types
rg -A 5 "DIGGER_GENERATION_ENDPOINT|DIGGER_GENERATION_API_TOKEN"

# Check the GenerateTerraformCode function implementation for security measures
ast-grep --pattern 'func GenerateTerraformCode($$$)'

Length of output: 4144


Script:

#!/bin/bash
# Check for any size limits or validation in the code
rg -A 5 "MaxPatchSize|MaxFileSize|MaxBodySize" backend/

# Check for timeout configurations
rg -A 5 "Timeout|timeout" backend/utils/

# Check for TLS configuration
rg -A 5 "TLSConfig|tls" backend/utils/

# Check for any validation of generated code
rg -A 5 "validate|sanitize|check" backend/utils/ai.go

Length of output: 1466

♻️ Duplicate comments (2)
backend/controllers/github.go (2)

528-529: ⚠️ Potential issue

Fix incorrect error message in placeholder comment creation

The error message incorrectly references "strconv.ParseInt" when the error is from publishing a comment.

Apply this fix:

-log.Printf("strconv.ParseInt error: %v", err)
+log.Printf("failed to publish placeholder comment: %v", err)

Also applies to: 944-945


533-533: ⚠️ Potential issue

Fix redeclaration of batchId variable

The batchId variable is redeclared using := operator when it's already defined. This will cause a compilation error.

Apply this fix:

-batchId, _, err := utils.ConvertJobsToDiggerJobs(
+batchId, _, err = utils.ConvertJobsToDiggerJobs(

Also applies to: 949-949

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60dbc32 and 1d3bc41.

📒 Files selected for processing (6)
  • backend/controllers/github.go (3 hunks)
  • backend/models/scheduler.go (1 hunks)
  • backend/models/storage.go (2 hunks)
  • backend/utils/graphs.go (2 hunks)
  • ee/backend/controllers/gitlab.go (2 hunks)
  • ee/backend/hooks/github.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • ee/backend/hooks/github.go
  • backend/models/storage.go
  • backend/models/scheduler.go
  • ee/backend/controllers/gitlab.go
  • backend/utils/graphs.go
🧰 Additional context used
🪛 GitHub Actions: EE Backend Go Tests
backend/controllers/github.go

[error] 315-315: Undefined variable 'debug' referenced in code

🪛 GitHub Actions: Backend Go Tests
backend/controllers/github.go

[error] 315-315: Undefined variable 'debug' used in code

Comment on lines +525 to +533

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Extract duplicate placeholder comment logic into a helper function

The placeholder comment creation logic is duplicated between handlePullRequestEvent and handleIssueCommentEvent. This violates the DRY principle.

Extract the logic into a helper function:

func createPlaceholderComment(ghService *dg_github.GithubService, number int, commentReporterManager utils.CommentReporterManager) (*github.IssueComment, error) {
    placeholderComment, err := ghService.PublishComment(number, "digger report placeholder")
    if err != nil {
        log.Printf("failed to publish placeholder comment: %v", err)
        commentReporterManager.UpdateComment(fmt.Sprintf(":x: could not create placeholder commentId for report: %v", err))
        return nil, fmt.Errorf("comment reporter error: %v", err)
    }
    return placeholderComment, nil
}

Also applies to: 942-949

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
backend/migrations/20250102194016.sql (1)

1-2: Consider adding these maintainability improvements.

  1. Add a column description to document its purpose:
COMMENT ON COLUMN "public"."digger_batches"."placeholder_comment_id_for_report" IS 'Stores the ID of the placeholder comment created for AI summary during job processing';
  1. Add a DOWN migration for rollback capability:
-- Add to a separate rollback file or section:
ALTER TABLE "public"."digger_batches" DROP COLUMN "placeholder_comment_id_for_report";
  1. Consider adding an index if this column will be frequently queried:
CREATE INDEX idx_digger_batches_placeholder_comment ON "public"."digger_batches" ("placeholder_comment_id_for_report");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d3bc41 and 4c9fb5c.

⛔ Files ignored due to path filters (1)
  • backend/migrations/atlas.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • backend/migrations/20250102194016.sql (1 hunks)
🔇 Additional comments (1)
backend/migrations/20250102194016.sql (1)

1-2: LGTM! Safe schema change.

The migration adds a nullable text column which is a non-blocking operation. The column type and nullability are appropriate for storing comment IDs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant