Skip to content

Commit

Permalink
Merge pull request #73 from Halleck45/doc_contributing
Browse files Browse the repository at this point in the history
make contribution easier
  • Loading branch information
Halleck45 authored Nov 19, 2024
2 parents 65052ba + b2d5ca0 commit 633dc56
Show file tree
Hide file tree
Showing 31 changed files with 176 additions and 51 deletions.
118 changes: 108 additions & 10 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,115 @@
# Contributing

## Requirements
Thank you for your interest, it's amazing 🥰. Here is some information to help you contribute to this project.

+ Go 1.21+
+ Make
I hope these informations will help you:

## Setup
> - [I have never programmed in Go, but I would love to learn](#-i-have-never-programmed-in-go-but-i-would-love-to-learn)
> - [How to run automated tests?](#-how-to-run-automated-tests)
> - [How is the source code organized?](#-how-is-the-source-code-organized)
> - [I want to add or modify a report](#-i-want-to-add-or-modify-a-report)
> - [My contribution is about supporting a new programming language](#-my-contribution-is-about-supporting-a-new-programming-language)
> - [My contribution involves updating the data structure (protobuf)](#-my-contribution-involves-updating-the-data-structure-protobuf)
> - [How to release new version?](#-how-to-release-new-version)
> - [How to improve the website?](#-how-to-improve-the-website)
Install dependencies:

## 🤓 I have never programmed in Go, but I would love to learn

No problem! Golang is accessible and easy to learn. Here are some resources to get you started:

+ [A Tour of Go](https://tour.golang.org/welcome/1): an interactive tour that will help you get started with Go.
+ [Go by Example](https://gobyexample.com/): a hands-on introduction to Go using annotated example programs.
+ [Go in 5 minutes](https://www.youtube.com/c/go-in-5-minutes): a series of short videos that will help you get started with Go.

You will need `Go 1.21+` to contribute to the source code. Please follow the [official installation guide](https://go.dev/doc/install) to install Go on your machine.

## 🤖 How to run automated tests?

To run automated tests, use the following command:

```bash
make install
go test ./...
```

## 📂 How is the source code organized?

The main directories of the application are as follows:

+ `src/Analyzer`: contains everything related to AST analysis (complexity, volume, etc.)
+ `src/Configuration`: manages configuration (loading files, validation, etc.)
+ `src/Engine`: contains various engines that convert source code (Python, Golang, PHP...) into an AST
+ `src/Report`: generates reports (HTML, markdown, etc.)

## 📃 I want to add or modify a report

Reports can be generated in formats like HTML, markdown, etc.

To add a new report, you need to create a structure that implements the `Reporter` interface defined in `src/Report/Reporter.go`.

```go
type Reporter interface {
// generates a report based on the files and the project aggregated data
Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error)
}
```

Then, register this new `Reporter` in the list of available reporters in the file `src/Report/ReportersFactory.go`.

Finally, add a CLI option (e.g., `--report-myreport=file1.foo`) to activate this report by modifying the `main.go` file.

## 🔥 My contribution is about supporting a new programming language

Language agnosticism in the analysis is achieved by using protobuf files that act as intermediaries between the parsed file (an AST) and the analysis engine.

To add support for a new programming language, you need to declare a new Engine that implements the `Engine` interface defined in `src/Engine/Engine.go`.

```go
type Engine interface {
// Returns true when analyzed files are concerned by the programming language
IsRequired() bool

// Prepare the engine for the analysis. For example, in order to prepare caches
Ensure() error

// First step of analysis. Parse all files, and generate protobuff compatible AST files
DumpAST()

// Cleanups the engine. For example, to remove caches
Finish() error

// Give a UI progress bar to the engine
SetProgressbar(progressbar *pterm.SpinnerPrinter)

// Give the configuration to the engine
SetConfiguration(configuration *Configuration.Configuration)

// Parse a file and return a protobuff compatible AST object
Parse(filepath string) (*pb.File, error)
}
```

## Sources
The protobuf file is defined in the proto directory, and a corresponding Go file is generated with the `make build-protobuff` command. This file is versioned as src/NodeType.NodeType.go.

## 🚩 My contribution involves updating the data structure (protobuf)

The data structure is defined in protobuf files located in the proto directory.

You need to install protobuf by running:

Statement descriptors are centralized in protobuf files, in the `proto` directory.
```bash
make install-protobuf
```

When ready to generate the Go code, run:
Once you've finished editing the protobuf files, you can generate the corresponding Go code with the command:

```bash
make build-protobuff
```

## Releasing
This will also verify that the protobuf files are well-formed.

## 📦 How to release new version?

First ensure tests pass:

Expand All @@ -35,4 +121,16 @@ Then release new version:

```bash
make build
```

## 🌐 How to improve the website?

The [website](https://halleck45.github.io/ast-metrics/) is hosted on Github, using the `documentation` branch.

When the `documentation` branch is updated, the website is automatically updated. You just need to create a pull request on the `documentation` branch.

You'll need [mkdocs](https://www.mkdocs.org/) to build the website locally. Once installed, you can run the following command to build the website:

```bash
mkdocs serve
```
2 changes: 1 addition & 1 deletion src/Cli/ScreenHtmlReport.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/charmbracelet/lipgloss"
"github.com/halleck45/ast-metrics/src/Analyzer"
pb "github.com/halleck45/ast-metrics/src/NodeType"
Report "github.com/halleck45/ast-metrics/src/Report/Html"
"github.com/halleck45/ast-metrics/src/Report"
)

type ScreenHtmlReport struct {
Expand Down
24 changes: 6 additions & 18 deletions src/Command/AnalyzeCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ import (
"github.com/halleck45/ast-metrics/src/Engine"
pb "github.com/halleck45/ast-metrics/src/NodeType"
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"
OpenMetrics "github.com/halleck45/ast-metrics/src/Report/OpenMetrics"
"github.com/halleck45/ast-metrics/src/Storage"
"github.com/inancgumus/screen"
"github.com/pterm/pterm"
Expand Down Expand Up @@ -176,22 +172,14 @@ func (v *AnalyzeCommand) Execute() error {
v.spinner.Increment()
}

reporters := []Report.Reporter{}
// Factory reporters
reportersFactory := Report.ReportersFactory{
Configuration: v.configuration,
}
reporters := reportersFactory.Factory(v.configuration)

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))
}
if v.configuration.Reports.OpenMetrics != "" {
reporters = append(reporters, OpenMetrics.NewOpenMetricsReportGenerator(v.configuration.Reports.OpenMetrics))
}

// Generate reports
for _, reporter := range reporters {
reports, err := reporter.Generate(allResults, projectAggregated)
Expand Down
13 changes: 13 additions & 0 deletions src/Engine/Engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,24 @@ import (
)

type Engine interface {
// Returns true when analyzed files are concerned by the programming language
IsRequired() bool

// Prepare the engine for the analysis. For example, in order to prepare caches
Ensure() error

// First step of analysis. Parse all files, and generate protobuff compatible AST files
DumpAST()

// Cleanups the engine. For example, to remove caches
Finish() error

// Give a UI progress bar to the engine
SetProgressbar(progressbar *pterm.SpinnerPrinter)

// Give the configuration to the engine
SetConfiguration(configuration *Configuration.Configuration)

// Parse a file and return a protobuff compatible AST object
Parse(filepath string) (*pb.File, error)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,26 @@ import (
"github.com/halleck45/ast-metrics/src/Analyzer"
"github.com/halleck45/ast-metrics/src/Engine"
pb "github.com/halleck45/ast-metrics/src/NodeType"
"github.com/halleck45/ast-metrics/src/Report"
"github.com/halleck45/ast-metrics/src/Ui"
)

var (
//go:embed templates/*
content embed.FS
htmlContent embed.FS
)

type HtmlReportGenerator struct {
// The path where the report will be generated
ReportPath string
}

func NewHtmlReportGenerator(reportPath string) Report.Reporter {
func NewHtmlReportGenerator(reportPath string) Reporter {
return &HtmlReportGenerator{
ReportPath: reportPath,
}
}

func (v *HtmlReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]Report.GeneratedReport, error) {
func (v *HtmlReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error) {

// Ensure report is required
if v.ReportPath == "" {
Expand Down Expand Up @@ -73,13 +72,13 @@ func (v *HtmlReportGenerator) Generate(files []*pb.File, projectAggregated Analy
"componentComparaisonOperator.html",
} {
// read the file
content, err := content.ReadFile(fmt.Sprintf("templates/%s", file))
htmlContent, err := htmlContent.ReadFile(fmt.Sprintf("templates/html/%s", file))
if err != nil {
return nil, err
}

// write the file to temporary folder (/tmp)
err = os.WriteFile(fmt.Sprintf("%s/%s", templateDir, file), content, 0644)
err = os.WriteFile(fmt.Sprintf("%s/%s", templateDir, file), htmlContent, 0644)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -119,7 +118,7 @@ func (v *HtmlReportGenerator) Generate(files []*pb.File, projectAggregated Analy
return nil, err
}

reports := []Report.GeneratedReport{
reports := []GeneratedReport{
{
Path: v.ReportPath,
Type: "directory",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@ import (
"github.com/halleck45/ast-metrics/src/Analyzer"
pb "github.com/halleck45/ast-metrics/src/NodeType"
"github.com/halleck45/ast-metrics/src/Pkg/Cleaner"
"github.com/halleck45/ast-metrics/src/Report"
)

type JsonReportGenerator struct {
ReportPath string
}

// This factory creates a new JsonReportGenerator
func NewJsonReportGenerator(ReportPath string) Report.Reporter {
func NewJsonReportGenerator(ReportPath string) Reporter {
return &JsonReportGenerator{
ReportPath: ReportPath,
}
}

// Generate generates a JSON report
func (j *JsonReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]Report.GeneratedReport, error) {
func (j *JsonReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error) {

if j.ReportPath == "" {
return nil, nil
Expand All @@ -49,7 +48,7 @@ func (j *JsonReportGenerator) Generate(files []*pb.File, projectAggregated Analy
return nil, fmt.Errorf("can not save report to path %s err: %s", j.ReportPath, err.Error())
}

reports := []Report.GeneratedReport{
reports := []GeneratedReport{
{
Path: j.ReportPath,
Type: "file",
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,25 @@ import (

"github.com/halleck45/ast-metrics/src/Analyzer"
pb "github.com/halleck45/ast-metrics/src/NodeType"
"github.com/halleck45/ast-metrics/src/Report"
)

var (
//go:embed templates/*
content embed.FS
mdContent embed.FS
)

type MarkdownReportGenerator struct {
// The path where the report will be generated
ReportPath string
}

func NewMarkdownReportGenerator(reportPath string) Report.Reporter {
func NewMarkdownReportGenerator(reportPath string) Reporter {
return &MarkdownReportGenerator{
ReportPath: reportPath,
}
}

func (v *MarkdownReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]Report.GeneratedReport, error) {
func (v *MarkdownReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error) {

// Ensure report is required
if v.ReportPath == "" {
Expand All @@ -46,13 +45,13 @@ func (v *MarkdownReportGenerator) Generate(files []*pb.File, projectAggregated A
}
for _, file := range []string{"index.md"} {
// read the file
content, err := content.ReadFile(fmt.Sprintf("templates/%s", file))
mdContent, err := mdContent.ReadFile(fmt.Sprintf("templates/markdown/%s", file))
if err != nil {
return nil, err
}

// write the file to temporary folder (/tmp)
err = os.WriteFile(fmt.Sprintf("%s/%s", templateDir, file), content, 0644)
err = os.WriteFile(fmt.Sprintf("%s/%s", templateDir, file), mdContent, 0644)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -94,7 +93,7 @@ func (v *MarkdownReportGenerator) Generate(files []*pb.File, projectAggregated A
return nil, err
}

reports := []Report.GeneratedReport{
reports := []GeneratedReport{
{
Path: v.ReportPath,
Type: "file",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,20 @@ import (
"github.com/halleck45/ast-metrics/src/Analyzer"
"github.com/halleck45/ast-metrics/src/Engine"
pb "github.com/halleck45/ast-metrics/src/NodeType"
"github.com/halleck45/ast-metrics/src/Report"
)

type OpenMetricsReportGenerator struct {
// The path where the report will be generated
ReportPath string
}

func NewOpenMetricsReportGenerator(reportPath string) Report.Reporter {
func NewOpenMetricsReportGenerator(reportPath string) Reporter {
return &OpenMetricsReportGenerator{
ReportPath: reportPath,
}
}

func (v *OpenMetricsReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]Report.GeneratedReport, error) {
func (v *OpenMetricsReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) ([]GeneratedReport, error) {

if v.ReportPath == "" {
return nil, nil
Expand Down Expand Up @@ -135,7 +134,7 @@ func (v *OpenMetricsReportGenerator) Generate(files []*pb.File, projectAggregate
}

// Return the created report, in order to inform the user
reports := []Report.GeneratedReport{
reports := []GeneratedReport{
{
Path: v.ReportPath,
Type: "file",
Expand Down
File renamed without changes.
Loading

0 comments on commit 633dc56

Please sign in to comment.