Skip to content

Commit

Permalink
Separating lib from cli (#655)
Browse files Browse the repository at this point in the history
* Separating lib from cli

* Renamed NewRevive to New

* Added GetLintFailures helper function

* Moved formatter to call to format since that's when it's needed

* makes fields of Revive struct non-public

* minor modifs in tests: remove unnamed constats

* Added lint package management to lint command

* README message for using revive as a library

* README formatting

* Removed unused method

* Slightly improved wording in README

* Handling format errors

* Renaming file to better reflect intent

* Refactoring pattern usage

* README heads

* renames excludePaths into excludePatterns

Co-authored-by: Bernardo Heynemann <bernardo.heynemann@coinbase.com>
Co-authored-by: chavacava <salvadorcavadini+github@gmail.com>
  • Loading branch information
3 people committed Mar 29, 2022
1 parent fa939ad commit 318db94
Show file tree
Hide file tree
Showing 7 changed files with 510 additions and 130 deletions.
58 changes: 57 additions & 1 deletion README.md
Expand Up @@ -588,7 +588,57 @@ import (
)

func main() {
cli.RunRevive(cli.NewExtraRule(&myRule{}, lint.RuleConfig{}))
cli.RunRevive(revivelib.NewExtraRule(&myRule{}, lint.RuleConfig{}))
}

type myRule struct{}

func (f myRule) Name() string {
return "myRule"
}

func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure { ... }
```

You can still go further and use `revive` without its cli, as part of your library, or your own cli:

```go
package mylib

import (
"github.com/mgechev/revive/cli"
"github.com/mgechev/revive/revivelib"
"github.com/mgechev/revive/lint"
)

// Error checking removed for clarity
func LintMyFile(file string) {
conf, _:= config.GetConfig("../defaults.toml")

revive, _ := revivelib.New(
conf, // Configuration file
true, // Set exit status
2048, // Max open files

// Then add as many extra rules as you need
revivelib.NewExtraRule(&myRule{}, lint.RuleConfig{}),
)

failuresChan, err := revive.Lint(
revivelib.Include(file),
revivelib.Exclude("./fixtures"),
// You can use as many revivelib.Include or revivelib.Exclude as required
)
if err != nil {
panic("Shouldn't have failed: " + err.Error)
}

// Now let's return the formatted errors
failures, exitCode, _ := revive.Format("stylish", failuresChan)

// failures is the string with all formatted lint error messages
// exit code is 0 if no errors, 1 if errors (unless config options change it)
// ... do something with them
}

type myRule struct{}
Expand Down Expand Up @@ -691,6 +741,12 @@ REVIVE_FORCE_COLOR=1 revive -formatter friendly ./... | tee revive.log
:---: |:---: |:---: |:---: |:---: |:---: |
[ridvansumset](https://github.com/ridvansumset) |[Jarema](https://github.com/Jarema) |[vkrol](https://github.com/vkrol) |[haya14busa](https://github.com/haya14busa) |[sina-devel](https://github.com/sina-devel) |[techknowlogick](https://github.com/techknowlogick) |


[<img alt="heynemann" src="https://avatars.githubusercontent.com/u/60965?v=4&s=117" width="117">](https://github.com/heynemann) | | | | | |
|:---: |:---: |:---: |:---: |:---: |:---: |
[heynemann](https://github.com/heynemann) | | | | | |


## License

MIT
153 changes: 24 additions & 129 deletions cli/main.go
Expand Up @@ -3,17 +3,14 @@ package cli
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime/debug"
"strings"

"github.com/fatih/color"
"github.com/mgechev/dots"
"github.com/mgechev/revive/config"
"github.com/mgechev/revive/lint"
"github.com/mgechev/revive/logging"
"github.com/mgechev/revive/revivelib"
"github.com/mitchellh/go-homedir"
)

Expand All @@ -29,160 +26,58 @@ func fail(err string) {
os.Exit(1)
}

// ExtraRule configures a new rule to be used with revive.
type ExtraRule struct {
Rule lint.Rule
DefaultConfig lint.RuleConfig
}

// NewExtraRule returns a configured extra rule
func NewExtraRule(rule lint.Rule, defaultConfig lint.RuleConfig) ExtraRule {
return ExtraRule{
Rule: rule,
DefaultConfig: defaultConfig,
}
}

// RunRevive runs the CLI for revive.
func RunRevive(extraRules ...ExtraRule) {
log, err := logging.GetLogger()
func RunRevive(extraRules ...revivelib.ExtraRule) {
conf, err := config.GetConfig(configPath)
if err != nil {
fail(err.Error())
}

formatter, err := config.GetFormatter(formatterName)
revive, err := revivelib.New(
conf,
setExitStatus,
maxOpenFiles,
extraRules...,
)
if err != nil {
fail(err.Error())
}

conf, err := config.GetConfig(configPath)
if err != nil {
fail(err.Error())
}
files := flag.Args()
packages := []*revivelib.LintPattern{}

if setExitStatus {
conf.ErrorCode = 1
conf.WarningCode = 1
for _, file := range files {
packages = append(packages, revivelib.Include(file))
}

extraRuleInstances := make([]lint.Rule, len(extraRules))
for i, extraRule := range extraRules {
extraRuleInstances[i] = extraRule.Rule

ruleName := extraRule.Rule.Name()
_, isRuleAlreadyConfigured := conf.Rules[ruleName]
if !isRuleAlreadyConfigured {
conf.Rules[ruleName] = extraRule.DefaultConfig
}
for _, file := range excludePatterns {
packages = append(packages, revivelib.Exclude(file))
}

lintingRules, err := config.GetLintingRules(conf, extraRuleInstances)
failures, err := revive.Lint(packages...)
if err != nil {
fail(err.Error())
}

log.Println("Config loaded")

if len(excludePaths) == 0 { // if no excludes were set in the command line
excludePaths = conf.Exclude // use those from the configuration
}

packages, err := getPackages(excludePaths)
output, exitCode, err := revive.Format(formatterName, failures)
if err != nil {
fail(err.Error())
}
revive := lint.New(func(file string) ([]byte, error) {
return ioutil.ReadFile(file)
}, maxOpenFiles)

failures, err := revive.Lint(packages, lintingRules, *conf)
if err != nil {
fail(err.Error())
}

formatChan := make(chan lint.Failure)
exitChan := make(chan bool)

var output string
go (func() {
output, err = formatter.Format(formatChan, *conf)
if err != nil {
fail(err.Error())
}
exitChan <- true
})()

exitCode := 0
for f := range failures {
if f.Confidence < conf.Confidence {
continue
}
if exitCode == 0 {
exitCode = conf.WarningCode
}
if c, ok := conf.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError {
exitCode = conf.ErrorCode
}
if c, ok := conf.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError {
exitCode = conf.ErrorCode
}

formatChan <- f
}

close(formatChan)
<-exitChan
if output != "" {
fmt.Println(output)
}

os.Exit(exitCode)
}

func normalizeSplit(strs []string) []string {
res := []string{}
for _, s := range strs {
t := strings.Trim(s, " \t")
if len(t) > 0 {
res = append(res, t)
}
}
return res
}

func getPackages(excludePaths arrayFlags) ([][]string, error) {
globs := normalizeSplit(flag.Args())
if len(globs) == 0 {
globs = append(globs, ".")
}

packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePaths))
if err != nil {
return nil, err
}

return packages, nil
}

type arrayFlags []string

func (i *arrayFlags) String() string {
return strings.Join([]string(*i), " ")
}

func (i *arrayFlags) Set(value string) error {
*i = append(*i, value)
return nil
}

var (
configPath string
excludePaths arrayFlags
formatterName string
help bool
versionFlag bool
setExitStatus bool
maxOpenFiles int
configPath string
excludePatterns revivelib.ArrayFlags
formatterName string
versionFlag bool
setExitStatus bool
maxOpenFiles int
)

var originalUsage = flag.Usage
Expand Down Expand Up @@ -243,7 +138,7 @@ func init() {
defaultConfigPath := buildDefaultConfigPath()

flag.StringVar(&configPath, "config", defaultConfigPath, configUsage)
flag.Var(&excludePaths, "exclude", excludeUsage)
flag.Var(&excludePatterns, "exclude", excludeUsage)
flag.StringVar(&formatterName, "formatter", "", formatterUsage)
flag.BoolVar(&versionFlag, "version", false, versionUsage)
flag.BoolVar(&setExitStatus, "set_exit_status", false, exitStatusUsage)
Expand Down

0 comments on commit 318db94

Please sign in to comment.