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

Improving DX, adding a --ci option and listing generated reports in output #68

Merged
merged 2 commits into from
Nov 16, 2024

Conversation

Halleck45
Copy link
Owner

@Halleck45 Halleck45 commented Nov 10, 2024

Screenshot 2024-11-10 19 51 45

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new command-line flag --ci for continuous integration mode, enhancing report generation options.
    • Added a new GeneratedReport struct to manage report metadata.
    • Enhanced report generation methods to return structured report data alongside errors.
    • Implemented a new end screen in the CLI to display generated reports and provide user tips.
  • Improvements

    • Improved console output styling for better user experience.
    • Enhanced error handling and user feedback during report generation.
    • Updated the CLI to support dynamic report generation based on configuration settings.
  • Bug Fixes

    • Resolved issues with report generation logic and ensured proper state management during resets.

@codecov-commenter
Copy link

codecov-commenter commented Nov 10, 2024

Codecov Report

Attention: Patch coverage is 45.69536% with 82 lines in your changes missing coverage. Please review.

Project coverage is 53.69%. Comparing base (27bb643) to head (3312bab).

Files with missing lines Patch % Lines
src/Cli/ScreenEnd.go 0.00% 37 Missing ⚠️
src/Configuration/ConfigurationLoader.go 60.41% 15 Missing and 4 partials ⚠️
src/Command/AnalyzeCommand.go 69.56% 5 Missing and 2 partials ⚠️
src/Report/Markdown/MarkdownReportGenerator.go 63.15% 7 Missing ⚠️
src/Report/Json/JsonReportGenerator.go 66.66% 5 Missing ⚠️
src/Cli/ScreenHome.go 20.00% 4 Missing ⚠️
src/Configuration/Configuration.go 33.33% 2 Missing ⚠️
src/Cli/ScreenHtmlReport.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #68      +/-   ##
==========================================
+ Coverage   53.49%   53.69%   +0.20%     
==========================================
  Files          42       43       +1     
  Lines        4827     4894      +67     
==========================================
+ Hits         2582     2628      +46     
- Misses       2016     2038      +22     
+ Partials      229      228       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

coderabbitai bot commented Nov 10, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces significant enhancements to the command-line interface (CLI) of the application, including a new --ci flag for continuous integration mode. This flag modifies report generation behavior and enhances output styling using the lipgloss package. Additionally, the report generation process has been restructured to return detailed report metadata. The changes also include updates to various structs and methods across multiple files to improve error handling and output clarity while maintaining existing functionality.

Changes

File Change Summary
main.go Added --ci flag to cli.App, updated output styling using lipgloss, modified report handling.
src/Cli/ScreenHome.go Updated Render, Reset, and Update methods to enhance console output styling and model refresh logic.
src/Cli/ScreenHtmlReport.go Modified View method to adjust error handling from capturing the result to focusing on error only.
src/Command/AnalyzeCommand.go Restructured Execute method for dynamic report generation and improved user feedback.
src/Configuration/Configuration.go Added IsComingFromConfigFile field and HasReports method to structs for configuration handling.
src/Configuration/ConfigurationLoader.go Updated Loads method to set IsComingFromConfigFile when loading a configuration file.
src/Report/GeneratedReport.go Introduced GeneratedReport struct for managing report metadata.
src/Report/Html/HtmlReportGenerator.go Updated method signatures to return structured report data and adjusted error handling.
src/Report/Json/JsonReportGenerator.go Updated method signatures to return structured report data and adjusted error handling.
src/Report/Json/JsonReportGenerator_test.go Modified tests to reflect changes in Generate method return values.
src/Report/Markdown/MarkdownReportGenerator.go Updated method signatures to return structured report data and adjusted error handling.
src/Report/Markdown/MardownReportGenerator_test.go Modified tests to reflect changes in Generate method return values.
src/Report/Reporter.go Introduced Reporter interface defining the Generate method for report generation.
src/Cli/ScreenEnd.go Added ScreenEnd struct and associated methods for managing the end screen of the CLI application.
src/Cli/ScreenHome_test.go Reformatted tests for consistency, added assertions, and improved clarity without altering logic.

Possibly related PRs

  • HTML report for banch comparaison #61: The changes in this PR involve modifications to the report generation logic, which may relate to the enhancements made in the main PR regarding the command-line interface and report outputs when using the --ci flag.
  • CI for accessibility of HTML report #66: This PR focuses on improving accessibility features in the HTML report, which aligns with the main PR's updates to the command-line interface and report generation, particularly in enhancing user experience.

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:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • 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 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: 6

🧹 Outside diff range and nitpick comments (25)
src/Report/GeneratedReport.go (3)

3-15: Consider using constants for the Type field and adding validation.

While the struct is well-organized, consider these improvements:

  1. Define constants for possible report types to prevent typos and ensure consistency
  2. Add validation methods to ensure required fields are not empty
  3. Consider using custom types for fields that need validation

Here's a suggested implementation:

+// ReportType represents the type of generated report
+type ReportType string
+
+const (
+    HTMLReport    ReportType = "html"
+    JSONReport    ReportType = "json"
+    MarkdownReport ReportType = "markdown"
)

 type GeneratedReport struct {
     // The path to the generated report
     Path string

     // The type of the report
-    Type string
+    Type ReportType

     // Description of the report
     Description string

     // Icon of the report
     Icon string
 }

+// Validate ensures all required fields are properly set
+func (r *GeneratedReport) Validate() error {
+    if r.Path == "" {
+        return fmt.Errorf("path is required")
+    }
+    return nil
+}

4-14: Enhance field documentation with examples and more details.

While the current comments are good, they could be more helpful with examples and additional context.

Here's a suggested improvement for the comments:

 type GeneratedReport struct {
-    // The path to the generated report
+    // Path is the filesystem location where the report is saved
+    // Example: "/tmp/reports/analysis-2024-01-20.html"
     Path string

-    // The type of the report
+    // Type indicates the report format (e.g., "html", "json", "markdown")
     Type string

-    // Description of the report
+    // Description provides a human-readable summary of the report's contents
+    // Example: "Code quality analysis report for main branch"
     Description string

-    // Icon of the report
+    // Icon represents a visual identifier for the report type
+    // Example: "📊" for charts, "📝" for documents
     Icon string
 }

3-15: Consider implementing common Go interfaces.

To improve usability and integration with Go's standard library, consider implementing common interfaces like fmt.Stringer.

Here's a suggested addition:

// String implements fmt.Stringer interface
func (r GeneratedReport) String() string {
    return fmt.Sprintf("%s report at %s: %s", r.Type, r.Path, r.Description)
}
src/Report/Reporter.go (1)

8-10: Add GoDoc comments for better documentation.

Consider adding documentation comments to describe the interface and its method.

+// Reporter defines the contract for generating reports from analyzed files
 type Reporter interface {
+    // Generate produces reports from the analyzed files and project aggregation
+    // Returns a slice of GeneratedReport and any error encountered during generation
     Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error)
 }
src/Report/Json/JsonReportGenerator_test.go (1)

Line range hint 1-108: Consider improving test structure and coverage.

While the test is thorough in validating the JSON structure, there are some potential improvements:

  1. Consider using test fixtures instead of hardcoded values
  2. The error checks are repetitive and could be consolidated
  3. Missing test cases for error scenarios (e.g., invalid file paths, permission issues)

Here's a suggested refactor to improve the test structure:

 func TestGenerateJson(t *testing.T) {
+    // Setup test cases
+    tests := []struct {
+        name        string
+        files       []*pb.File
+        aggregated  Analyzer.ProjectAggregated
+        shouldError bool
+    }{
+        {
+            name: "valid case",
+            files: []*pb.File{{Path: "file1.php"}},
+            aggregated: Analyzer.ProjectAggregated{
+                Combined: Analyzer.Aggregated{
+                    ConcernedFiles: []*pb.File{{Path: "file1.php"}},
+                },
+            },
+            shouldError: false,
+        },
+        // Add more test cases here
+    }
+
+    for _, tt := range tests {
+        t.Run(tt.name, func(t *testing.T) {
+            reportPath := "/tmp/report.json"
+            generator := &JsonReportGenerator{ReportPath: reportPath}
+            defer os.Remove(reportPath)
+
+            _, err := generator.Generate(tt.files, tt.aggregated)
+            if tt.shouldError {
+                assert.Error(t, err)
+                return
+            }
+
+            assert.NoError(t, err)
+            assertValidJsonReport(t, reportPath)
+        })
+    }
 }
+
+func assertValidJsonReport(t *testing.T, path string) {
+    t.Helper()
+    
+    assert.FileExists(t, path)
+    
+    bytes, err := ioutil.ReadFile(path)
+    assert.NoError(t, err)
+    
+    var report map[string]interface{}
+    err = json.Unmarshal(bytes, &report)
+    assert.NoError(t, err)
+    
+    concernedFiles, ok := report["concernedFiles"].([]interface{})
+    assert.True(t, ok)
+    assert.Len(t, concernedFiles, 1)
+    
+    concernedFile, ok := concernedFiles[0].(map[string]interface{})
+    assert.True(t, ok)
+    assert.Contains(t, concernedFile, "path")
+}
src/Configuration/ConfigurationLoader.go (3)

24-47: Consider enhancing error messages for better debugging.

While the error handling is present, the errors could be more descriptive to aid in debugging, especially in CI environments.

Consider wrapping errors with context:

 if err != nil {
-    return cfg, err
+    return cfg, fmt.Errorf("failed to open configuration file %s: %w", filename, err)
 }
 if err != nil {
-    return cfg, err
+    return cfg, fmt.Errorf("failed to decode configuration file %s: %w", filename, err)
 }

51-58: Enhance error message for YAML parsing failures.

Similar to the previous suggestion, the error message could be more descriptive.

Consider wrapping the error:

 if err != nil {
-    return cfg, err
+    return cfg, fmt.Errorf("failed to parse YAML configuration: %w", err)
 }

62-71: Improve validation error message.

The error message for empty FilenameToChecks could be more informative.

Consider a more descriptive error message:

 if len(c.FilenameToChecks) == 0 {
-    return errors.New("No filename to check")
+    return errors.New("configuration loader requires at least one filename to check")
 }
src/Configuration/Configuration.go (3)

30-31: Add documentation for the new field.

The IsComingFromConfigFile field would benefit from a documentation comment explaining its purpose and usage, especially since it's an exported field.

 	Storage *Storage.Workdir

+	// IsComingFromConfigFile indicates whether the configuration was loaded from a config file
+	// rather than being created programmatically or via command line arguments
 	IsComingFromConfigFile bool

40-43: Fix the malformed comment and add proper Go documentation.

The current comment is malformed. It should follow Go's documentation conventions.

-// function HasReports() bool {
+// HasReports returns true if any report type (HTML, Markdown, or JSON) is configured
 func (c *ConfigurationReport) HasReports() bool {

74-74: Fix indentation to match other fields.

The indentation of the new field initialization doesn't align with other fields in the struct initialization.

-		IsComingFromConfigFile:   false,
+		IsComingFromConfigFile: false,
src/Report/Markdown/MarkdownReportGenerator.go (1)

28-32: LGTM: Good use of interface return type.

Returning the interface type instead of the concrete implementation improves modularity and follows the interface segregation principle. This change makes the code more flexible and easier to test.

src/Cli/ScreenHome.go (3)

78-79: Consider moving the style definition to package-level vars.

While the styling enhances the output, the style definition could be moved to the package-level vars section for better reusability and consistency across the package.

 // General stuff for styling the view
 var (
 	term = termenv.EnvColorProfile()
+	nonInteractiveStyle = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("#666666")).
+		Italic(true)
 )

 // ... later in the Render method ...
-		var style = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Italic(true)
-		fmt.Println(style.Render("No interactive mode detected."))
+		fmt.Println(nonInteractiveStyle.Render("No interactive mode detected."))

Line range hint 208-208: Improve the comment's clarity.

The comment "issue when navigating back to the main screen" is vague. Consider documenting the specific issue this fixes and why fillInScreens needs to be called here.

-	// issue when navigating back to the main screen
+	// Ensure screens are properly repopulated when navigating back to the main screen
+	// This fixes potential screen list inconsistencies after model updates

Line range hint 19-31: Consider adding documentation about screen lifecycle.

The ScreenHome struct manages multiple screens and their lifecycle (creation, navigation, updates). Consider adding documentation that explains:

  • How screens are managed and their lifecycle
  • When screens are created/destroyed
  • How navigation between screens works
  • How screen state is maintained

This would improve maintainability and help future contributors understand the screen management system better.

main.go (3)

87-88: Consider enhancing the JSON report flag documentation

While the implementation is correct, consider adding more details to the usage description, such as the expected format or purpose of the JSON report.

-						Usage:    "Generate a report in JSON format",
+						Usage:    "Generate a report in JSON format for programmatic analysis",

97-102: Enhance CI mode flag documentation

The --ci flag's usage description should explicitly state what it enables (non-interactive mode and default report generation).

-						Usage:    "Enable CI mode",
+						Usage:    "Enable CI mode (enables non-interactive mode and generates default HTML/Markdown reports)",

132-134: Consider enhancing the welcome message

While the styling is good, consider adding version information to the welcome message for better user experience.

-					fmt.Println(style.Render("\n🦫 AST Metrics is a language-agnostic static code analyzer."))
+					fmt.Println(style.Render(fmt.Sprintf("\n🦫 AST Metrics v%s - A language-agnostic static code analyzer", version)))
src/Report/Json/JsonReportGenerator.go (1)

37-37: Refine error messages for clarity

The error messages could be more informative and consistent. Consider using error wrapping and providing clearer context to aid in debugging.

Apply the following diff to improve the error messages:

-	return nil, fmt.Errorf("can not clean report err: %s", err.Error())
+	return nil, fmt.Errorf("failed to clean report: %w", err)

-	return nil, fmt.Errorf("can not serialize report to JSON err: %s", err.Error())
+	return nil, fmt.Errorf("failed to serialize report to JSON: %w", err)

-	return nil, fmt.Errorf("can not save report to path %s err: %s", j.ReportPath, err.Error())
+	return nil, fmt.Errorf("failed to save report to path %s: %w", j.ReportPath, err)

Also applies to: 43-43, 49-49

src/Command/AnalyzeCommand.go (4)

6-6: Consider using 'pterm' for consistent output styling

The fmt package is imported at line 6~ and used for printing output messages later in the code. To maintain consistent styling and take advantage of pterm's capabilities, consider using pterm methods for all user-facing messages instead of fmt.Println. This will ensure uniformity in the application's output and enhance the user experience.


179-200: Handle report generation errors without halting subsequent processes

In the report generation loop (lines 193~ to 200~), if an error occurs while generating a report, the function logs the error and returns immediately (line 197~). This stops the generation of any remaining reports, which might not be desirable. To ensure that all possible reports are generated, consider logging the error and continuing with the next reporter.

Apply this diff to continue generating reports even if one fails:

if err != nil {
    pterm.Error.Println("Cannot generate report: " + err.Error())
-   return err
+   continue
}

254-266: Use 'pterm' for consistent and styled output messages

Lines 257~ to 265~ use fmt.Println to display messages to the user. For consistency and to enhance output styling, consider replacing fmt.Println with pterm methods such as pterm.Println, pterm.Info.Println, or pterm.Success.Println. This will provide a more uniform and visually appealing interface.

Apply this diff to update the output methods:

- fmt.Println("")
- fmt.Println("📁 These reports have been generated:")
+ pterm.Println("")
+ pterm.Println("📁 These reports have been generated:")

for _, report := range generatedReports {
-   fmt.Println("\n  ✔ " + report.Path + " (" + report.Type + ")")
-   fmt.Println("\n        " + report.Description)
+   pterm.Success.Println("\n  ✔ " + report.Path + " (" + report.Type + ")")
+   pterm.Info.Println("\n        " + report.Description)
}

- fmt.Println("")
+ pterm.Println("")

276-284: Enhance user messages with 'pterm' for better styling

Lines 278~ and 282~ use fmt.Println to display informational messages to the user. Using pterm methods can improve the presentation and maintain consistency with other parts of the application where pterm is used.

Apply this diff to replace fmt.Println with pterm methods:

- fmt.Println("\n💡 We noticed that you haven't yet created a configuration file. You can create a .ast-metrics.yaml configuration file by running: ast-metrics init")
- fmt.Println("")
+ pterm.Info.Println("\n💡 We noticed that you haven't yet created a configuration file. You can create a .ast-metrics.yaml configuration file by running: ast-metrics init")
+ pterm.Println("")

- fmt.Println("\n🌟 If you like AST Metrics, please consider starring the project on GitHub: https://github.com/Halleck45/ast-metrics/. Thanks!")
- fmt.Println("")
+ pterm.Info.Println("\n🌟 If you like AST Metrics, please consider starring the project on GitHub: https://github.com/Halleck45/ast-metrics/. Thanks!")
+ pterm.Println("")
src/Report/Html/HtmlReportGenerator.go (2)

Line range hint 37-131: Handle errors returned by GenerateLanguagePage calls

In the Generate method, the errors returned by v.GenerateLanguagePage are not being checked. Ignoring these errors could lead to silent failures and incomplete report generation.

Consider capturing and handling the errors appropriately:

// Example for one call
if err := v.GenerateLanguagePage("index.html", "All", projectAggregated.Combined, files, projectAggregated); err != nil {
    return nil, err
}

// Repeat for other calls to GenerateLanguagePage
for language, currentView := range projectAggregated.ByProgrammingLanguage {
    if err := v.GenerateLanguagePage("index.html", language, currentView, files, projectAggregated); err != nil {
        return nil, err
    }
}

41-41: Return empty slice instead of nil when no report is generated

When v.ReportPath is empty, the Generate method returns nil, nil. In Go, it's idiomatic to return an empty slice rather than nil to avoid potential nil pointer dereferences in the calling code.

Update the return statement:

-		return nil, nil
+		return []Report.GeneratedReport{}, nil
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 27bb643 and 8f179f6.

📒 Files selected for processing (13)
  • main.go (5 hunks)
  • src/Cli/ScreenHome.go (2 hunks)
  • src/Cli/ScreenHtmlReport.go (1 hunks)
  • src/Command/AnalyzeCommand.go (5 hunks)
  • src/Configuration/Configuration.go (3 hunks)
  • src/Configuration/ConfigurationLoader.go (2 hunks)
  • src/Report/GeneratedReport.go (1 hunks)
  • src/Report/Html/HtmlReportGenerator.go (4 hunks)
  • src/Report/Json/JsonReportGenerator.go (1 hunks)
  • src/Report/Json/JsonReportGenerator_test.go (1 hunks)
  • src/Report/Markdown/MardownReportGenerator_test.go (2 hunks)
  • src/Report/Markdown/MarkdownReportGenerator.go (4 hunks)
  • src/Report/Reporter.go (1 hunks)
🔇 Additional comments (20)
src/Report/GeneratedReport.go (1)

1-1: LGTM! Package name follows Go conventions.

src/Report/Reporter.go (1)

8-10: LGTM! Well-designed interface following SOLID principles.

The Reporter interface is well-designed with:

  • Single responsibility (SRP)
  • Clear method signature
  • Proper error handling
  • Clean parameter types
src/Report/Json/JsonReportGenerator_test.go (1)

30-30: Verify the Generate method's API change across the codebase.

The change to ignore the first return value suggests an API modification in the Generate method. Let's verify this change is consistent across the codebase.

✅ Verification successful

Let me gather more information about the Generate method's implementation to verify the API change.


Based on the gathered information, I can now provide the final response:

The API change to ignore the first return value is consistent across the codebase

The verification shows that:

  • The Reporter interface in src/Report/Reporter.go defines Generate to return ([]GeneratedReport, error)
  • All implementations consistently ignore the first return value in test files (JsonReportGenerator_test.go, MardownReportGenerator_test.go)
  • The actual usage in production code (AnalyzeCommand.go, ScreenHtmlReport.go) shows proper handling of both return values
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Generate method signature and usage across different report generators

# Check the Generate method signature in report generators
echo "Checking Generate method signatures:"
ast-grep --pattern 'func $_($$) ($_, error)' | rg 'Generate'

# Check usage patterns of Generate method
echo "\nChecking Generate method usage patterns:"
rg -A 1 '\.Generate\(' 

Length of output: 1032


Script:

#!/bin/bash
# Check the actual implementation of Generate methods
echo "Checking Generate method implementations:"
ast-grep --pattern 'func ($$) Generate($_, $_) ($_, error) {
  $$$
}'

# Check interface definitions that might define Generate
echo "\nChecking interface definitions:"
ast-grep --pattern 'type $_ interface {
  $$$
  Generate($_, $_) ($_, error)
  $$$
}'

Length of output: 534

src/Configuration/ConfigurationLoader.go (3)

4-20: LGTM! Well-structured initialization code.

The import organization, struct definition, and constructor follow Go best practices. The default configuration filenames follow common patterns for configuration files.


Line range hint 73-123: LGTM! Well-documented default configuration.

The default configuration template is comprehensive and includes helpful comments that improve the developer experience, aligning well with the PR's DX improvement objectives.


24-47: Verify configuration behavior in CI environments.

Since this PR adds CI-specific features, we should ensure the configuration loading behavior is appropriate for CI environments.

✅ Verification successful

Based on the analysis of the shell script results, I can now provide a final response:

Configuration handling is appropriate for CI environments

The codebase properly handles CI-specific configuration through:

  • A dedicated --ci flag that enables CI mode (non-interactive, with HTML and Markdown reports)
  • The IsComingFromConfigFile flag is used appropriately to detect and handle configuration file presence
  • Configuration loading behavior is consistent across both CI and non-CI environments
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any CI-specific configuration handling in the codebase
rg -l "IsComingFromConfigFile|--ci" --type go

# Look for any CI-related test cases
rg -l "TestCI|ci.*test" --type go

Length of output: 189


Script:

#!/bin/bash
# Check how IsComingFromConfigFile is used in the codebase
rg "IsComingFromConfigFile" -B 2 -A 2

# Check for CI-related command line flags or environment handling
rg "ci|CI" -B 2 -A 2 --type go

# Look at the configuration struct definition
ast-grep --pattern 'type Configuration struct {
  $$$
}'

Length of output: 59966

src/Configuration/Configuration.go (1)

Line range hint 30-74: Changes look good and align well with PR objectives!

The additions improve the developer experience by:

  1. Providing clarity about configuration sources
  2. Adding a convenient way to check for configured reports
  3. Supporting the new --ci option through better configuration tracking

These changes lay good groundwork for the report listing feature mentioned in the PR objectives.

src/Report/Markdown/MarkdownReportGenerator.go (4)

15-15: LGTM: Import addition is necessary for the interface return type.


34-38: LGTM: Consistent error handling pattern.

The error handling follows a consistent pattern of returning (nil, err) throughout the method, which makes the code more predictable and maintainable.

Also applies to: 44-45, 50-51, 56-57, 71-72, 77-78, 84-85, 93-94


97-104: LGTM: Well-structured report metadata.

The report metadata structure provides clear and useful information about the generated report, which aligns well with the PR's objective of improving developer experience.


Line range hint 34-105: Verify CI mode handling.

The PR objectives mention adding a --ci option, but there's no visible handling of CI-specific behavior in the Generate method. Let's verify if this is handled elsewhere or needs to be added here.

✅ Verification successful

Let me gather more context about the CI mode implementation.


Let me check how the CI flag is used to affect the application behavior.


Let me try one final search to understand how the CI mode is handled in the application configuration.


CI mode is correctly handled - no changes needed in MarkdownReportGenerator

The CI mode is properly implemented at the application level where it acts as an alias for multiple flags including --non-interactive and --report-markdown. The MarkdownReportGenerator doesn't need specific CI mode handling because:

  1. The CI mode automatically enables markdown report generation through the flag aliasing
  2. The non-interactive behavior is handled at the application level (main.go)
  3. The generator already works correctly when the report is requested, regardless of the mode
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for CI mode handling in the codebase
echo "Searching for CI mode flag handling..."
rg --type go -i "ci.*flag|flag.*ci"

echo "Searching for CI mode specific behavior..."
rg --type go -i "ci.*mode|mode.*ci"

Length of output: 413


Script:

#!/bin/bash
# Check the CI mode implementation in main.go
echo "Checking CI mode implementation in main.go..."
rg -A 10 -B 2 'Enable CI mode' main.go

# Check if there are any other CI-related configurations
echo -e "\nChecking for other CI-related configurations..."
ast-grep --pattern 'var $_ = cli.BoolFlag {
  $$$
  Usage: "Enable CI mode",
  $$$
}'

Length of output: 720


Script:

#!/bin/bash
# Check how CI flag affects the application
echo "Checking CI flag usage..."
rg -A 5 -B 5 'ctx\.Bool\("ci"\)' main.go

# Check for any CI-related configuration in the app initialization
echo -e "\nChecking app initialization for CI mode..."
ast-grep --pattern 'func $_(ctx *cli.Context) error {
  $$$
  if ctx.Bool("ci") {
    $$$
  }
  $$$
}'

Length of output: 309


Script:

#!/bin/bash
# Search for configuration handling related to CI mode
echo "Searching for configuration handling..."
rg -A 10 "func.*initConfig" main.go

# Check for any non-interactive mode settings which might be related to CI
echo -e "\nChecking non-interactive mode settings..."
rg -A 5 "non-interactive" main.go

Length of output: 837

src/Cli/ScreenHome.go (1)

8-8: LGTM: Good choice for terminal styling!

The addition of lipgloss is a good choice for enhancing the CLI output styling, which aligns well with the DX improvement objectives.

main.go (2)

5-5: LGTM: Import additions are properly utilized

The new imports for fmt and lipgloss are appropriately used for enhanced output formatting.

Also applies to: 8-8


125-125: LGTM: Proper handling of interactive mode flags

The condition correctly handles both --non-interactive and --ci flags.

src/Report/Json/JsonReportGenerator.go (4)

20-23: Change factory function to return interface for better abstraction

Modifying NewJsonReportGenerator to return the Report.Reporter interface instead of a concrete *JsonReportGenerator enhances flexibility. This change promotes abstraction and allows for swapping different implementations without altering the calling code.


27-27: Update 'Generate' method signature to return reports

Changing the Generate method to return ([]Report.GeneratedReport, error) aligns it with the updated reporting framework. This allows the method to return detailed report metadata alongside any errors, improving the integration with the rest of the reporting system.


30-30: Handle empty 'ReportPath' explicitly

When ReportPath is empty, the method returns (nil, nil). Ensure that the calling code correctly handles this scenario to prevent unexpected behavior due to missing reports.

Please confirm that the callers of this method check for a nil report slice when ReportPath is empty.


52-60: Construct and return report metadata

Building the reports slice with detailed metadata and returning it ensures that the generated report information is available to the caller. This practice enhances the reporting capabilities by providing structured data about the generated reports.

src/Report/Html/HtmlReportGenerator.go (2)

31-34: Update constructor to return the Report.Reporter interface

The NewHtmlReportGenerator function now returns the Report.Reporter interface instead of a concrete *HtmlReportGenerator type. This promotes abstraction and allows for more flexible implementations of report generators.


122-131: Properly construct and return the generated report metadata

The addition of the reports slice provides structured metadata about the generated reports, which is beneficial for consumers of the Generate method.

@@ -71,7 +71,7 @@ func TestGenerateWithTemplateFiles(t *testing.T) {
// Create a temporary template file
ioutil.WriteFile("/tmp/templates/index.md", []byte("Test template"), 0644)

err := generator.Generate(files, projectAggregated)
_, err := generator.Generate(files, projectAggregated)
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 validation for template-based report generation results.

Similar to the previous test, this test should verify the content and structure of the returned GeneratedReport, especially in the context of template-based generation.

 func TestGenerateWithTemplateFiles(t *testing.T) {
 	generator := &MarkdownReportGenerator{ReportPath: "/tmp/report.md"}
 	files := []*pb.File{}
 	projectAggregated := Analyzer.ProjectAggregated{}

 	// Create a temporary template file
 	ioutil.WriteFile("/tmp/templates/index.md", []byte("Test template"), 0644)

-	_, err := generator.Generate(files, projectAggregated)
+	result, err := generator.Generate(files, projectAggregated)
+
+	// Verify the returned report metadata
+	expectedPath := "/tmp/report.md"
+	if result.Path != expectedPath {
+		t.Errorf("Expected report path %v, got %v", expectedPath, result.Path)
+	}
+	// Add other assertions for template-specific metadata
📝 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
_, err := generator.Generate(files, projectAggregated)
result, err := generator.Generate(files, projectAggregated)
// Verify the returned report metadata
expectedPath := "/tmp/report.md"
if result.Path != expectedPath {
t.Errorf("Expected report path %v, got %v", expectedPath, result.Path)
}
// Add other assertions for template-specific metadata

src/Cli/ScreenHtmlReport.go Show resolved Hide resolved
main.go Outdated Show resolved Hide resolved
@Halleck45 Halleck45 added the enhancement New feature or request label Nov 10, 2024
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 (8)
src/Cli/ScreenEnd.go (3)

13-38: Add documentation for the 'tea' field.

While most fields are documented, the tea field lacks proper documentation. Consider adding a descriptive comment to explain its purpose.

 	// program
-	tea *tea.Program
+	// tea is the Bubble Tea program instance used for terminal UI rendering
+	tea *tea.Program

84-85: Add implementation or documentation for Reset method.

The Reset method is empty without any explanation. Either implement the required functionality or add a comment explaining why it's intentionally empty.

 func (r *ScreenEnd) Reset(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) {
+	// TODO: Implement reset functionality or document why this method is intentionally empty
 }

58-81: Refactor Render method for better maintainability.

Several improvements could enhance the maintainability of the Render method:

  1. Extract string literals as constants
  2. Simplify multiple consecutive newlines
  3. Add nil check for reports slice
+const (
+    reportsHeader = "📁 These reports have been generated:"
+    configTip     = "💡 We noticed that you haven't yet created a configuration file. You can create a .ast-metrics.yaml configuration file by running: ast-metrics init"
+    starMessage   = "🌟 If you like AST Metrics, please consider starring the project on GitHub: https://github.com/Halleck45/ast-metrics/. Thanks!"
+)

 func (r *ScreenEnd) Render() {
+    if r.reports == nil {
+        return
+    }
+
     // List reports
     if r.configuration.Reports.HasReports() {
-        fmt.Println("\n")
-        fmt.Println("📁 These reports have been generated:")
+        fmt.Println("\n" + reportsHeader)

         for _, report := range r.reports {
             fmt.Println("\n  ✔ " + report.Path + " (" + report.Type + ")")
             fmt.Println("\n        " + report.Description)
         }
-
-        fmt.Println("")
     }

     // Tips if configuration file does not exist
     if !r.configuration.IsComingFromConfigFile {
-        fmt.Println("\n💡 We noticed that you haven't yet created a configuration file. You can create a .ast-metrics.yaml configuration file by running: ast-metrics init")
-        fmt.Println("")
+        fmt.Println("\n" + configTip)
     }

-    fmt.Println("\n🌟 If you like AST Metrics, please consider starring the project on GitHub: https://github.com/Halleck45/ast-metrics/. Thanks!")
-    fmt.Println("")
+    fmt.Println("\n" + starMessage + "\n")
 }
src/Report/Json/JsonReportGenerator.go (2)

20-24: LGTM: Good use of interface-based design.

Returning the Report.Reporter interface instead of the concrete type improves abstraction and flexibility. This change follows the Go idiom of "accept interfaces, return structs" and allows for better testing and maintainability.


52-60: LGTM: Comprehensive report metadata structure.

The GeneratedReport structure provides useful metadata about the generated JSON file. Consider adding file size or generation timestamp to the metadata for better tracking and debugging capabilities.

 reports := []Report.GeneratedReport{
   {
     Path:        j.ReportPath,
     Type:        "file",
     Description: "The JSON report allows scripts to parse the results programmatically.",
     Icon:        "📄",
+    Size:        len(jsonReport),
+    GeneratedAt: time.Now().UTC(),
   },
 }
src/Cli/ScreenHome.go (2)

78-79: Consider extracting the style definition.

While the styling improves visual feedback, consider moving the style definition to a constants section at the top of the file alongside other style-related variables. This would maintain consistency with the existing pattern (see the term variable) and improve reusability.

// General stuff for styling the view
var (
	term = termenv.EnvColorProfile()
+	nonInteractiveStyle = lipgloss.NewStyle().
+		Foreground(lipgloss.Color("#666666")).
+		Italic(true)
)

// ... rest of the file ...

// In the Render method:
-		var style = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Italic(true)
-		fmt.Println(style.Render("No interactive mode detected."))
+		fmt.Println(nonInteractiveStyle.Render("No interactive mode detected."))

Line range hint 1-246: Add tests to improve coverage.

According to the PR objectives, this file has 0% test coverage. Consider adding tests for:

  • Screen navigation logic
  • Model updates
  • Non-interactive mode rendering
  • Screen initialization

This will help prevent regressions and ensure the UI behaves correctly.

Would you like me to help create a test suite for this file? I can provide examples of test cases that cover the critical paths.

src/Command/AnalyzeCommand.go (1)

178-199: Excellent refactoring of report generation logic.

The new implementation using a slice of reporters improves modularity, maintainability, and extensibility.

Consider these improvements:

  1. Error handling could be more granular to allow partial success:
 for _, reporter := range reporters {
     reports, err := reporter.Generate(allResults, projectAggregated)
     if err != nil {
-        pterm.Error.Println("Cannot generate report: " + err.Error())
-        return err
+        log.Errorf("Failed to generate report using %T: %v", reporter, err)
+        continue
     }
     generatedReports = append(generatedReports, reports...)
 }
  1. Consider extracting reporter initialization to improve readability:
func (v *AnalyzeCommand) initializeReporters() []Report.Reporter {
    var reporters []Report.Reporter
    if v.configuration.Reports.Html != "" {
        reporters = append(reporters, Html.NewHtmlReportGenerator(v.configuration.Reports.Html))
    }
    // ... similar for other reporters
    return reporters
}

Add unit tests for the new report generation logic.

The Codecov report indicates missing coverage. Please add tests for:

  • Reporter initialization with different configuration combinations
  • Error handling scenarios
  • Integration of multiple reporters
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8f179f6 and a9d0226.

📒 Files selected for processing (7)
  • main.go (5 hunks)
  • src/Cli/ScreenEnd.go (1 hunks)
  • src/Cli/ScreenHome.go (4 hunks)
  • src/Command/AnalyzeCommand.go (3 hunks)
  • src/Report/Json/JsonReportGenerator.go (1 hunks)
  • src/Report/Json/JsonReportGenerator_test.go (1 hunks)
  • src/Report/Markdown/MardownReportGenerator_test.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • main.go
  • src/Report/Json/JsonReportGenerator_test.go
  • src/Report/Markdown/MardownReportGenerator_test.go
🔇 Additional comments (9)
src/Cli/ScreenEnd.go (1)

1-11: LGTM!

The package structure and imports are well-organized and appropriate for the functionality.

src/Report/Json/JsonReportGenerator.go (3)

12-12: LGTM: Import addition is necessary for the new return types.

The new import is required to support the updated method signatures returning Report.Reporter and Report.GeneratedReport types.


27-30: LGTM: Improved error handling with early return.

The updated method signature properly returns both the report slice and error. The early return case for empty ReportPath is now more explicit and consistent with the new return type.


37-37: LGTM: Consistent error handling pattern.

Error handling follows a consistent pattern across all error cases:

  1. Returns nil for the report slice
  2. Uses fmt.Errorf with descriptive messages
  3. Includes the original error in the message

Also applies to: 43-43, 49-49

src/Cli/ScreenHome.go (3)

8-8: LGTM: Import addition aligns with styling improvements.

The addition of the lipgloss package is appropriate for enhancing the CLI's visual feedback.


112-112: Verify the impact of GetModel signature change.

The return type change from modelChoices to tea.Model is a breaking change that could affect callers. Let's verify all usages to ensure they've been updated.

✅ Verification successful

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

GetModel signature change is properly implemented across the codebase

All implementations and usages of GetModel() consistently use tea.Model as the return type. The change has been properly propagated across all screen components:

  • ScreenHome
  • ScreenHtmlReport
  • ScreenEnd
  • ScreenTableClass
  • ScreenSummary
  • ScreenRisks
  • ScreenByProgrammingLanguage

The interface definition in Screen.go also matches this signature, confirming a consistent implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for GetModel() calls to verify they handle the new return type
rg -A 2 "GetModel\(\)"

Length of output: 3531


242-245: Verify Screen interface implementation.

The addition of GetScreenName suggests implementing a common Screen interface. Let's verify that all screen types implement this method consistently.

✅ Verification successful

Let me gather more information about screen implementations to verify this thoroughly.


GetScreenName implementation is consistent across all screen types

The verification shows that GetScreenName is properly implemented by all screen structs that implement the Screen interface. Each implementation returns a unique, descriptive screen name:

  • ScreenHtmlReport: "Generate HTML report"
  • ScreenHome: "Home"
  • ScreenEnd: "End"
  • ScreenTableClass: "Classes and object oriented statistics"
  • ScreenSummary: "Overview"
  • ScreenRisks: "Top candidates for refactoring"
  • ScreenByProgrammingLanguage: (with emoji)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for GetScreenName implementations across all screen types
ast-grep --pattern 'func ($x $y) GetScreenName() string {
  $$$
}'

# Search for the Screen interface definition
rg -A 5 "type Screen interface"

Length of output: 339


Script:

#!/bin/bash
# Find all screen type files
fd -e go . src/Cli | grep -i "screen"

# Search for all struct definitions that might be screens
rg "type Screen\w+ struct"

# Look for GetScreenName method implementations with context
rg -A 2 "func .* GetScreenName\(\) string"

Length of output: 2017

src/Command/AnalyzeCommand.go (2)

15-16: LGTM: Import changes align with the report generation refactoring.

The reorganization of imports reflects the improved modular structure for report generation.


261-264: LGTM: Enhanced end screen with report listing.

The addition of generated reports to the end screen improves developer experience by providing clear feedback about the generated artifacts.

Comment on lines +40 to +56
type modelEnd struct {
}

func (m modelEnd) Init() tea.Cmd {
return nil
}

func (m *modelEnd) Reset(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) {
}

func (m modelEnd) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}

func (m modelEnd) View() string {
return ""
}
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

Now I have enough context to provide the final response:

The modelEnd implementation is necessary but can be improved

The modelEnd struct is a required implementation as it's used by ScreenEnd to satisfy the Screen interface contract, which requires a GetModel() method returning a tea.Model. However, its current empty implementation could be enhanced:

  • The empty View() method should return a meaningful message or confirmation of completion
  • The Reset() method should handle cleanup if needed
  • Consider adding documentation explaining its purpose as the final screen
🔗 Analysis chain

Verify the necessity of the modelEnd implementation.

The modelEnd struct appears to be a placeholder implementation of the tea.Model interface. If it's not actively used, consider removing it to reduce unnecessary complexity. If it's intended for future use, please add TODO comments explaining the planned functionality.


Let me search for more context about where this model is being used.


Let me check if there's any interface or contract that requires this tea.Model implementation.


Let me check one of the other screen implementations to understand the pattern better.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if modelEnd is used anywhere in the codebase
rg "modelEnd" --type go

Length of output: 412


Script:

#!/bin/bash
# Check the entire ScreenEnd.go file to understand the context
cat src/Cli/ScreenEnd.go

# Search for any references to ScreenEnd
rg "ScreenEnd" --type go -A 5

Length of output: 4312


Script:

#!/bin/bash
# Look for any interface definitions that ScreenEnd might be implementing
rg "type.*interface" --type go -A 5

# Check for any other screens to understand the pattern
fd -e go -x grep -l "GetModel()" {}

Length of output: 2133


Script:

#!/bin/bash
# Check implementation of another screen for comparison
cat src/Cli/ScreenHome.go

# Also check the Screen interface definition
cat src/Cli/Screen.go

Length of output: 7224

Comment on lines +27 to +60
func (j *JsonReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]Report.GeneratedReport, error) {

if j.ReportPath == "" {
return nil
return nil, nil
}

report := j.buildReport(projectAggregated)

err := Cleaner.CleanVal(report)
if err != nil {
return fmt.Errorf("can not clean report err: %s", err.Error())
return nil, fmt.Errorf("can not clean report err: %s", err.Error())
}

// This code serializes the results to JSON
jsonReport, err := json.Marshal(report)
if err != nil {
return fmt.Errorf("can not serialize report to JSON err: %s", err.Error())
return nil, fmt.Errorf("can not serialize report to JSON err: %s", err.Error())
}

// This code writes the JSON report to a file
err = ioutil.WriteFile(j.ReportPath, jsonReport, os.ModePerm)
if err != nil {
return fmt.Errorf("can not save report to path %s err: %s", j.ReportPath, err.Error())
return nil, fmt.Errorf("can not save report to path %s err: %s", j.ReportPath, err.Error())
}

return nil
reports := []Report.GeneratedReport{
{
Path: j.ReportPath,
Type: "file",
Description: "The JSON report allows scripts to parse the results programmatically.",
Icon: "📄",
},
}
return reports, 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

Add test coverage for error scenarios in the Generate method

The current test file only covers the happy path. Missing test cases for:

  • Empty report path scenario (ReportPath: "")
  • File permission errors (non-writable directory)
  • JSON marshaling errors (by injecting data that causes marshaling to fail)
  • CleanVal errors

Example test cases to add:

func TestGenerateWithEmptyPath(t *testing.T) {
    generator := &JsonReportGenerator{ReportPath: ""}
    reports, err := generator.Generate(nil, Analyzer.ProjectAggregated{})
    assert.Nil(t, err)
    assert.Nil(t, reports)
}

func TestGenerateWithNonWritablePath(t *testing.T) {
    generator := &JsonReportGenerator{ReportPath: "/root/forbidden/report.json"}
    reports, err := generator.Generate([]*pb.File{}, Analyzer.ProjectAggregated{})
    assert.NotNil(t, err)
    assert.Contains(t, err.Error(), "can not save report")
}
🔗 Analysis chain

Verify: Improve test coverage for the Generate method.

According to Codecov, this file has 66.66% coverage with 5 missing lines. Consider adding test cases for:

  1. Error cases in JSON marshaling
  2. File writing failures
  3. Different report metadata configurations

Let me analyze the test coverage further by checking the implementation of error cases.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check existing test coverage for the Generate method

# Test: Look for existing test cases
rg -A 5 "func Test.*Generate" "src/Report/Json/JsonReportGenerator_test.go"

# Test: Check for error case testing
rg -A 5 "Error|err :=" "src/Report/Json/JsonReportGenerator_test.go"

Length of output: 2037


Script:

#!/bin/bash
# Check for error handling test cases specifically around report path and marshaling

# Look for test cases with empty report path
rg -B 2 -A 2 'ReportPath: ""' "src/Report/Json/JsonReportGenerator_test.go"

# Look for test cases with invalid permissions or paths
rg -B 2 -A 2 'ReportPath: "/invalid/' "src/Report/Json/JsonReportGenerator_test.go"

# Check if there are any negative test cases
rg "func Test.*Generate.*Error" "src/Report/Json/JsonReportGenerator_test.go"

# Look at the full test file to understand the test coverage
cat "src/Report/Json/JsonReportGenerator_test.go"

Length of output: 2794

@Halleck45 Halleck45 merged commit 94cfcdb into main Nov 16, 2024
3 checks passed
@Halleck45 Halleck45 deleted the dx_ci branch November 16, 2024 15:51
This was referenced Nov 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants