Skip to content

Commit

Permalink
Improving DX, adding a --ci option and listing generated reports in o…
Browse files Browse the repository at this point in the history
…utput
  • Loading branch information
Halleck45 committed Nov 10, 2024
1 parent 27bb643 commit 8f179f6
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 112 deletions.
28 changes: 24 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package main

import (
"bufio"
"fmt"
"os"

"github.com/charmbracelet/lipgloss"
"github.com/halleck45/ast-metrics/src/Cli"
"github.com/halleck45/ast-metrics/src/Command"
"github.com/halleck45/ast-metrics/src/Configuration"
Expand Down Expand Up @@ -82,8 +84,8 @@ func main() {
},
// JSON report
&cli.StringFlag{
Name: "report-json",
Usage: "Generate a report in JSON format",
Name: "report-json",
Usage: "Generate a report in JSON format",
Category: "Report",
},
// Watch mode
Expand All @@ -92,6 +94,12 @@ func main() {
Usage: "Re-run the analysis when files change",
Category: "Global options",
},
// CI mode (alias of --non-interactive, --report-html and --report-markdown)
&cli.BoolFlag{
Name: "ci",
Usage: "Enable CI mode",
Category: "Global options",
},
// Configuration
&cli.StringFlag{
Name: "config",
Expand All @@ -114,14 +122,16 @@ func main() {

// get option --non-interactive
isInteractive := true
if cCtx.Bool("non-interactive") {
if cCtx.Bool("non-interactive") || cCtx.Bool("ci") {
pterm.DisableColor()
isInteractive = false
}

// Stdout
outWriter := bufio.NewWriter(os.Stdout)
pterm.DefaultBasicText.Println(pterm.LightMagenta(" AST Metrics ") + "is a language-agnostic static code analyzer.")
var style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Bold(true)
fmt.Println(style.Render("\n🦫 AST Metrics is a language-agnostic static code analyzer."))
fmt.Println("")

// Prepare configuration object
configuration := Configuration.NewConfiguration()
Expand Down Expand Up @@ -183,6 +193,16 @@ func main() {
configuration.Reports.Json = cCtx.String("report-json")
}

// CI mode
if cCtx.Bool("ci") {
if configuration.Reports.Html == "" {
configuration.Reports.Html = "ast-metrics-html-report"
}
if configuration.Reports.Markdown == "" {
configuration.Reports.Markdown = "ast-metrics-markdown-report.md"
}
}

// Compare with
if cCtx.String("compare-with") != "" {
configuration.CompareWith = cCtx.String("compare-with")
Expand Down
4 changes: 3 additions & 1 deletion src/Cli/ScreenHome.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/fsnotify/fsnotify"
"github.com/halleck45/ast-metrics/src/Analyzer"
pb "github.com/halleck45/ast-metrics/src/NodeType"
Expand Down Expand Up @@ -74,7 +75,8 @@ func (r *ScreenHome) Render() {

if !r.isInteractive {
// If not interactive
fmt.Println("No interactive mode detected.")
var style = lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Italic(true)
fmt.Println(style.Render("No interactive mode detected."))
return
}

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/ScreenHtmlReport.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (m modelScreenHtmlReport) View() string {
// Generate report
// report: html
htmlReportGenerator := Report.NewHtmlReportGenerator(directory)
err := htmlReportGenerator.Generate(m.files, m.projectAggregated)
_, err := htmlReportGenerator.Generate(m.files, m.projectAggregated)
if err != nil {
return fmt.Sprintf("Error generating report: %s", err)
}
Expand Down
67 changes: 48 additions & 19 deletions src/Command/AnalyzeCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package Command
import (
"bufio"
"errors"
"fmt"
"os"

"github.com/fsnotify/fsnotify"
Expand All @@ -12,7 +13,8 @@ import (
"github.com/halleck45/ast-metrics/src/Configuration"
"github.com/halleck45/ast-metrics/src/Engine"
pb "github.com/halleck45/ast-metrics/src/NodeType"
Report "github.com/halleck45/ast-metrics/src/Report/Html"
Report "github.com/halleck45/ast-metrics/src/Report"
Html "github.com/halleck45/ast-metrics/src/Report/Html"
Json "github.com/halleck45/ast-metrics/src/Report/Json"
Markdown "github.com/halleck45/ast-metrics/src/Report/Markdown"
"github.com/halleck45/ast-metrics/src/Storage"
Expand Down Expand Up @@ -174,24 +176,28 @@ func (v *AnalyzeCommand) Execute() error {
v.spinner.Increment()
}

// report: html
htmlReportGenerator := Report.NewHtmlReportGenerator(v.configuration.Reports.Html)
err = htmlReportGenerator.Generate(allResults, projectAggregated)
if err != nil {
pterm.Error.Println("Cannot generate html report: " + err.Error())
return err
}
// report: markdown
markdownReportGenerator := Markdown.NewMarkdownReportGenerator(v.configuration.Reports.Markdown)
err = markdownReportGenerator.Generate(allResults, projectAggregated)
if err != nil {
pterm.Error.Println("Cannot generate markdown report: " + err.Error())
}
// report: json
jsonReportGenerator := Json.NewJsonReportGenerator(v.configuration.Reports.Json)
err = jsonReportGenerator.Generate(allResults, projectAggregated)
if err != nil {
pterm.Error.Println("Cannot generate json report: " + err.Error())
reporters := []Report.Reporter{}
generatedReports := []Report.GeneratedReport{}
if v.configuration.Reports.HasReports() {
if v.configuration.Reports.Html != "" {
reporters = append(reporters, Html.NewHtmlReportGenerator(v.configuration.Reports.Html))
}
if v.configuration.Reports.Markdown != "" {
reporters = append(reporters, Markdown.NewMarkdownReportGenerator(v.configuration.Reports.Markdown))
}
if v.configuration.Reports.Json != "" {
reporters = append(reporters, Json.NewJsonReportGenerator(v.configuration.Reports.Json))
}

// Generate reports
for _, reporter := range reporters {
reports, err := reporter.Generate(allResults, projectAggregated)
if err != nil {
pterm.Error.Println("Cannot generate report: " + err.Error())
return err
}
generatedReports = append(generatedReports, reports...)
}
}

// Evaluate requirements
Expand Down Expand Up @@ -245,6 +251,20 @@ func (v *AnalyzeCommand) Execute() error {
}
}

// List reports
if v.configuration.Reports.HasReports() {

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

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

fmt.Println("")
}

// Link to file wartcher (in order to close it when app is closed)
if v.FileWatcher != nil {
v.currentPage.FileWatcher = v.FileWatcher
Expand All @@ -253,6 +273,15 @@ func (v *AnalyzeCommand) Execute() error {
// Store state of the command
v.alreadyExecuted = true

// Tips if configuration file does not exist
if !v.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🌟 If you like AST Metrics, please consider starring the project on GitHub: https://github.com/Halleck45/ast-metrics/. Thanks!")
fmt.Println("")

if shouldFail {
os.Exit(1)
}
Expand Down
8 changes: 8 additions & 0 deletions src/Configuration/Configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type Configuration struct {

// Location of cache files
Storage *Storage.Workdir

IsComingFromConfigFile bool
}

type ConfigurationReport struct {
Expand All @@ -35,6 +37,11 @@ type ConfigurationReport struct {
Json string `yaml:"json"`
}

// function HasReports() bool {
func (c *ConfigurationReport) HasReports() bool {
return c.Html != "" || c.Markdown != "" || c.Json != ""
}

type ConfigurationRequirements struct {
Rules *struct {
CyclomaticComplexity *ConfigurationDefaultRule `yaml:"cyclomatic_complexity"`
Expand Down Expand Up @@ -64,6 +71,7 @@ func NewConfiguration() *Configuration {
Watching: false,
CompareWith: "",
Storage: Storage.Default(),
IsComingFromConfigFile: false,
}
}

Expand Down
115 changes: 58 additions & 57 deletions src/Configuration/ConfigurationLoader.go
Original file line number Diff line number Diff line change
@@ -1,75 +1,76 @@
package Configuration

import (
"errors"
"os"
"errors"
"os"

"gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
)

type ConfigurationLoader struct {
FilenameToChecks []string
FilenameToChecks []string
}

func NewConfigurationLoader() *ConfigurationLoader {
return &ConfigurationLoader{
FilenameToChecks: []string{
".ast-metrics.yaml",
".ast-metrics.dist.yaml",
},
}
return &ConfigurationLoader{
FilenameToChecks: []string{
".ast-metrics.yaml",
".ast-metrics.dist.yaml",
},
}
}

func (c *ConfigurationLoader) Loads(cfg *Configuration) (*Configuration, error) {
// Load configuration file
for _, filename := range c.FilenameToChecks {

if _, err := os.Stat(filename); err == nil {

// Load configuration
f, err := os.Open(filename)
if err != nil {
return cfg, err
}
defer f.Close()

decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
return cfg, err
}

return cfg, nil
}
}

return cfg, nil
// Load configuration file
for _, filename := range c.FilenameToChecks {

if _, err := os.Stat(filename); err == nil {

// Load configuration
f, err := os.Open(filename)
if err != nil {
return cfg, err
}
defer f.Close()

decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
return cfg, err
}

cfg.IsComingFromConfigFile = true
return cfg, nil
}
}

return cfg, nil
}

func (c *ConfigurationLoader) Import(yamlString string) (*Configuration, error) {
// Load YAML string into configuration
cfg := &Configuration{}
err := yaml.Unmarshal([]byte(yamlString), cfg)
if err != nil {
return cfg, err
}

return cfg, nil
// Load YAML string into configuration
cfg := &Configuration{}
err := yaml.Unmarshal([]byte(yamlString), cfg)
if err != nil {
return cfg, err
}

return cfg, nil
}

func (c *ConfigurationLoader) CreateDefaultFile() error {
if len(c.FilenameToChecks) == 0 {
return errors.New("No filename to check")
}
filename := c.FilenameToChecks[0]

// Create default configuration file
f, err := os.Create(filename)
if err != nil {
return err
}

_, err = f.WriteString(`# AST Metrics configuration file
if len(c.FilenameToChecks) == 0 {
return errors.New("No filename to check")
}
filename := c.FilenameToChecks[0]

// Create default configuration file
f, err := os.Create(filename)
if err != nil {
return err
}

_, err = f.WriteString(`# AST Metrics configuration file
# This file is used to configure AST Metrics
# You can find more information at https://github.com/Halleck45/ast-metrics/
Expand Down Expand Up @@ -115,9 +116,9 @@ requirements:
to: "Controller"
`)

if err != nil {
return err
}
if err != nil {
return err
}

return nil
return nil
}
15 changes: 15 additions & 0 deletions src/Report/GeneratedReport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package Report

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

// The type of the report
Type string

// Description of the report
Description string

// Icon of the report
Icon string
}
Loading

0 comments on commit 8f179f6

Please sign in to comment.