Skip to content

Commit d3a8549

Browse files
committed
Add PR comment to flakeguard report
1 parent d9043b4 commit d3a8549

File tree

3 files changed

+186
-38
lines changed

3 files changed

+186
-38
lines changed

tools/flakeguard/cmd/report.go

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,10 @@ var ReportCmd = &cobra.Command{
2020
// Get flag values
2121
reportResultsPath, _ := cmd.Flags().GetString("results-path")
2222
reportOutputPath, _ := cmd.Flags().GetString("output-path")
23-
reportFormats, _ := cmd.Flags().GetString("format")
2423
reportMaxPassRatio, _ := cmd.Flags().GetFloat64("max-pass-ratio")
2524
reportCodeOwnersPath, _ := cmd.Flags().GetString("codeowners-path")
2625
reportRepoPath, _ := cmd.Flags().GetString("repo-path")
27-
28-
// Split the formats into a slice
29-
formats := strings.Split(reportFormats, ",")
26+
generatePRComment, _ := cmd.Flags().GetBool("generate-pr-comment")
3027

3128
// Start spinner for loading test reports
3229
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
@@ -104,19 +101,57 @@ var ReportCmd = &cobra.Command{
104101
}
105102
fmt.Printf("All tests report saved to %s\n", allTestsReportPath)
106103

107-
// Generate and save the summary reports (all tests) in specified formats
108-
for _, format := range formats {
109-
format = strings.ToLower(strings.TrimSpace(format))
104+
// Generate GitHub summary markdown
105+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
106+
s.Suffix = " Generating GitHub summary markdown..."
107+
s.Start()
108+
109+
err = generateGitHubSummaryMarkdown(aggregatedReport, filepath.Join(outputDir, "all-tests"))
110+
if err != nil {
111+
s.Stop()
112+
return fmt.Errorf("error generating GitHub summary markdown: %w", err)
113+
}
114+
s.Stop()
115+
fmt.Println("GitHub summary markdown generated successfully.")
116+
117+
if generatePRComment {
118+
// Retrieve required flags
119+
currentBranch, _ := cmd.Flags().GetString("current-branch")
120+
currentCommitSHA, _ := cmd.Flags().GetString("current-commit-sha")
121+
baseBranch, _ := cmd.Flags().GetString("base-branch")
122+
repoURL, _ := cmd.Flags().GetString("repo-url")
123+
actionRunID, _ := cmd.Flags().GetString("action-run-id")
124+
125+
// Validate that required flags are provided
126+
missingFlags := []string{}
127+
if currentBranch == "" {
128+
missingFlags = append(missingFlags, "--current-branch")
129+
}
130+
if currentCommitSHA == "" {
131+
missingFlags = append(missingFlags, "--current-commit-sha")
132+
}
133+
if repoURL == "" {
134+
missingFlags = append(missingFlags, "--repo-url")
135+
}
136+
if actionRunID == "" {
137+
missingFlags = append(missingFlags, "--action-run-id")
138+
}
139+
if len(missingFlags) > 0 {
140+
return fmt.Errorf("the following flags are required when --generate-pr-comment is set: %s", strings.Join(missingFlags, ", "))
141+
}
142+
143+
// Generate PR comment markdown
110144
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
111-
s.Suffix = fmt.Sprintf(" Generating all tests summary in format %s...", format)
145+
s.Suffix = " Generating PR comment markdown..."
112146
s.Start()
113147

114-
if err := generateReport(aggregatedReport, format, filepath.Join(outputDir, "all-tests")); err != nil {
148+
err = generatePRCommentMarkdown(aggregatedReport, filepath.Join(outputDir, "all-tests"), baseBranch, currentBranch, currentCommitSHA, repoURL, actionRunID)
149+
if err != nil {
115150
s.Stop()
116-
return fmt.Errorf("error generating all tests summary in format %s: %w", format, err)
151+
return fmt.Errorf("error generating PR comment markdown: %w", err)
117152
}
118153
s.Stop()
119-
fmt.Printf("All tests summary in format %s generated successfully.\n", format)
154+
fmt.Println("PR comment markdown generated successfully.")
120155
}
121156

122157
// Filter failed tests (PassRatio < maxPassRatio and not skipped)
@@ -163,38 +198,40 @@ var ReportCmd = &cobra.Command{
163198
func init() {
164199
ReportCmd.Flags().StringP("results-path", "p", "", "Path to the folder containing JSON test result files (required)")
165200
ReportCmd.Flags().StringP("output-path", "o", "./report", "Path to output the generated report files")
166-
ReportCmd.Flags().StringP("format", "f", "markdown,json", "Comma-separated list of summary report formats (markdown,json)")
167201
ReportCmd.Flags().Float64P("max-pass-ratio", "", 1.0, "The maximum pass ratio threshold for a test to be considered flaky")
168202
ReportCmd.Flags().StringP("codeowners-path", "", "", "Path to the CODEOWNERS file")
169203
ReportCmd.Flags().StringP("repo-path", "", ".", "The path to the root of the repository/project")
204+
ReportCmd.Flags().Bool("generate-pr-comment", false, "Set to true to generate PR comment markdown")
205+
ReportCmd.Flags().String("base-branch", "develop", "The base branch to compare against (used in PR comment)")
206+
ReportCmd.Flags().String("current-branch", "", "The current branch name (required if generate-pr-comment is set)")
207+
ReportCmd.Flags().String("current-commit-sha", "", "The current commit SHA (required if generate-pr-comment is set)")
208+
ReportCmd.Flags().String("repo-url", "", "The repository URL (required if generate-pr-comment is set)")
209+
ReportCmd.Flags().String("action-run-id", "", "The GitHub Actions run ID (required if generate-pr-comment is set)")
210+
170211
ReportCmd.MarkFlagRequired("results-path")
171212
}
172213

173-
func generateReport(report *reports.TestReport, format, outputPath string) error {
214+
func generateGitHubSummaryMarkdown(report *reports.TestReport, outputPath string) error {
174215
fs := reports.OSFileSystem{}
175-
format = strings.ToLower(strings.TrimSpace(format))
176-
177-
switch format {
178-
case "markdown":
179-
// Adjust the markdown filename to include "-summary"
180-
mdFileName := outputPath + "-summary.md"
181-
mdFile, err := fs.Create(mdFileName)
182-
if err != nil {
183-
return fmt.Errorf("error creating markdown file: %w", err)
184-
}
185-
defer mdFile.Close()
186-
reports.GenerateMarkdownSummary(mdFile, report, 1.0)
187-
case "json":
188-
// Generate summary JSON
189-
summaryData := reports.GenerateSummaryData(report.Results, 1.0)
190-
summaryFileName := outputPath + "-summary.json"
191-
if err := reports.SaveSummaryAsJSON(fs, summaryFileName, summaryData); err != nil {
192-
return fmt.Errorf("error saving summary JSON: %w", err)
193-
}
194-
default:
195-
return fmt.Errorf("unsupported summary report format: %s", format)
216+
mdFileName := outputPath + "-summary.md"
217+
mdFile, err := fs.Create(mdFileName)
218+
if err != nil {
219+
return fmt.Errorf("error creating GitHub summary markdown file: %w", err)
196220
}
221+
defer mdFile.Close()
222+
reports.GenerateGitHubSummaryMarkdown(mdFile, report, 1.0)
223+
return nil
224+
}
197225

226+
func generatePRCommentMarkdown(report *reports.TestReport, outputPath, baseBranch, currentBranch, currentCommitSHA, repoURL, actionRunID string) error {
227+
fs := reports.OSFileSystem{}
228+
mdFileName := outputPath + "-pr-comment.md"
229+
mdFile, err := fs.Create(mdFileName)
230+
if err != nil {
231+
return fmt.Errorf("error creating PR comment markdown file: %w", err)
232+
}
233+
defer mdFile.Close()
234+
reports.GeneratePRCommentMarkdown(mdFile, report, 1.0, baseBranch, currentBranch, currentCommitSHA, repoURL, actionRunID)
198235
return nil
199236
}
200237

tools/flakeguard/reports/presentation.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func formatPassRatio(passRatio float64) string {
8383
return fmt.Sprintf("%.2f%%", passRatio*100)
8484
}
8585

86-
func GenerateMarkdownSummary(w io.Writer, testReport *TestReport, maxPassRatio float64) {
86+
func GenerateGitHubSummaryMarkdown(w io.Writer, testReport *TestReport, maxPassRatio float64) {
8787
settingsTable := buildSettingsTable(testReport, maxPassRatio)
8888
fmt.Fprint(w, "# Flakeguard Summary\n\n")
8989
printTable(w, settingsTable)
@@ -104,6 +104,45 @@ func GenerateMarkdownSummary(w io.Writer, testReport *TestReport, maxPassRatio f
104104
RenderResults(w, testReport.Results, maxPassRatio, true)
105105
}
106106

107+
func GeneratePRCommentMarkdown(w io.Writer, testReport *TestReport, maxPassRatio float64, baseBranch, currentBranch, currentCommitSHA, repoURL, actionRunID string) {
108+
fmt.Fprint(w, "# Flakeguard Summary\n\n")
109+
110+
// Construct additionalInfo inside the function
111+
additionalInfo := fmt.Sprintf(
112+
"Ran new or updated tests between `%s` and %s (`%s`).",
113+
baseBranch,
114+
currentCommitSHA,
115+
currentBranch,
116+
)
117+
118+
// Construct the links
119+
viewDetailsLink := fmt.Sprintf("[View Flaky Detector Details](%s/actions/runs/%s)", repoURL, actionRunID)
120+
compareChangesLink := fmt.Sprintf("[Compare Changes](%s/compare/%s...%s#files_bucket)", repoURL, baseBranch, currentCommitSHA)
121+
linksLine := fmt.Sprintf("%s | %s", viewDetailsLink, compareChangesLink)
122+
123+
// Include additional information
124+
fmt.Fprintln(w, additionalInfo)
125+
fmt.Fprintln(w) // Add an extra newline for formatting
126+
127+
// Include the links
128+
fmt.Fprintln(w, linksLine)
129+
fmt.Fprintln(w) // Add an extra newline for formatting
130+
131+
if len(testReport.Results) == 0 {
132+
fmt.Fprintln(w, "## No tests ran :warning:")
133+
return
134+
}
135+
136+
summary := GenerateSummaryData(testReport.Results, maxPassRatio)
137+
if summary.AveragePassRatio < maxPassRatio {
138+
fmt.Fprintln(w, "## Found Flaky Tests :x:")
139+
} else {
140+
fmt.Fprintln(w, "## No Flakes Found :white_check_mark:")
141+
}
142+
143+
RenderResults(w, testReport.Results, maxPassRatio, true)
144+
}
145+
107146
func buildSettingsTable(testReport *TestReport, maxPassRatio float64) [][]string {
108147
rows := [][]string{
109148
{"**Setting**", "**Value**"},

tools/flakeguard/reports/presentation_test.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package reports
22

33
import (
44
"bytes"
5+
"fmt"
56
"reflect"
67
"strings"
78
"testing"
@@ -71,8 +72,8 @@ func TestGenerateFlakyTestsTable(t *testing.T) {
7172
}
7273
}
7374

74-
// TestGenerateMarkdownSummary tests the GenerateMarkdownSummary function.
75-
func TestGenerateMarkdownSummary(t *testing.T) {
75+
// TestGenerateGitHubSummaryMarkdown tests the GenerateGitHubSummaryMarkdown function.
76+
func TestGenerateGitHubSummaryMarkdown(t *testing.T) {
7677
testReport := &TestReport{
7778
GoProject: "ProjectX",
7879
TestRunCount: 3,
@@ -104,7 +105,7 @@ func TestGenerateMarkdownSummary(t *testing.T) {
104105
var buffer bytes.Buffer
105106
maxPassRatio := 0.9
106107

107-
GenerateMarkdownSummary(&buffer, testReport, maxPassRatio)
108+
GenerateGitHubSummaryMarkdown(&buffer, testReport, maxPassRatio)
108109

109110
output := buffer.String()
110111

@@ -126,6 +127,77 @@ func TestGenerateMarkdownSummary(t *testing.T) {
126127
}
127128
}
128129

130+
// TestGeneratePRCommentMarkdown tests the GeneratePRCommentMarkdown function.
131+
func TestGeneratePRCommentMarkdown(t *testing.T) {
132+
testReport := &TestReport{
133+
GoProject: "ProjectX",
134+
TestRunCount: 3,
135+
RaceDetection: true,
136+
Results: []TestResult{
137+
{
138+
TestName: "TestA",
139+
PassRatio: 0.8,
140+
Runs: 5,
141+
Successes: 4,
142+
Failures: 1,
143+
TestPackage: "pkg1",
144+
CodeOwners: []string{"owner1"},
145+
Durations: []time.Duration{time.Second, time.Second, time.Second, time.Second, time.Second},
146+
},
147+
{
148+
TestName: "TestB",
149+
PassRatio: 1.0,
150+
Runs: 3,
151+
Successes: 3,
152+
Failures: 0,
153+
TestPackage: "pkg2",
154+
CodeOwners: []string{"owner2"},
155+
Durations: []time.Duration{2 * time.Second, 2 * time.Second, 2 * time.Second},
156+
},
157+
},
158+
}
159+
160+
var buffer bytes.Buffer
161+
maxPassRatio := 0.9
162+
baseBranch := "develop"
163+
currentBranch := "feature-branch"
164+
currentCommitSHA := "abcdef1234567890"
165+
repoURL := "https://github.com/example/repo"
166+
actionRunID := "123456789"
167+
168+
GeneratePRCommentMarkdown(&buffer, testReport, maxPassRatio, baseBranch, currentBranch, currentCommitSHA, repoURL, actionRunID)
169+
170+
output := buffer.String()
171+
172+
fmt.Println(output)
173+
174+
// Check that the output includes the expected headings and links
175+
if !strings.Contains(output, "# Flakeguard Summary") {
176+
t.Error("Expected markdown summary to contain '# Flakeguard Summary'")
177+
}
178+
if !strings.Contains(output, fmt.Sprintf("Ran new or updated tests between `%s` and %s (`%s`).", baseBranch, currentCommitSHA, currentBranch)) {
179+
t.Error("Expected markdown to contain the additional info line with branches and commit SHA")
180+
}
181+
if !strings.Contains(output, fmt.Sprintf("[View Flaky Detector Details](%s/actions/runs/%s)", repoURL, actionRunID)) {
182+
t.Error("Expected markdown to contain the 'View Flaky Detector Details' link")
183+
}
184+
if !strings.Contains(output, fmt.Sprintf("[Compare Changes](%s/compare/%s...%s#files_bucket)", repoURL, baseBranch, currentCommitSHA)) {
185+
t.Error("Expected markdown to contain the 'Compare Changes' link")
186+
}
187+
if !strings.Contains(output, "## Found Flaky Tests :x:") {
188+
t.Error("Expected markdown summary to contain '## Found Flaky Tests :x:'")
189+
}
190+
if !strings.Contains(output, "| **Name**") {
191+
t.Error("Expected markdown table headers for test results")
192+
}
193+
if !strings.Contains(output, "| TestA ") {
194+
t.Error("Expected markdown table to include TestA")
195+
}
196+
if strings.Contains(output, "| TestB ") {
197+
t.Error("Did not expect markdown table to include TestB since its pass ratio is above the threshold")
198+
}
199+
}
200+
129201
// TestPrintTable tests the printTable function.
130202
func TestPrintTable(t *testing.T) {
131203
table := [][]string{

0 commit comments

Comments
 (0)