validate config and print resources usage

This commit is contained in:
golangci 2018-05-12 10:13:37 +03:00
parent 85ee97f776
commit 05f09371ac
3 changed files with 110 additions and 38 deletions

View File

@ -1,6 +1,4 @@
run: run:
args:
- ./...
verbose: true verbose: true
concurrency: 4 concurrency: 4
deadline: 1m deadline: 1m

View File

@ -2,11 +2,13 @@ package commands
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"go/build" "go/build"
"go/token" "go/token"
"log" "log"
"os" "os"
"runtime"
"strings" "strings"
"time" "time"
@ -21,6 +23,7 @@ import (
"github.com/golangci/golangci-lint/pkg/result/processors" "github.com/golangci/golangci-lint/pkg/result/processors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
@ -51,6 +54,8 @@ func (e *Executor) initRun() {
runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)") runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)")
runCmd.Flags().DurationVar(&rc.Deadline, "deadline", time.Minute, "Deadline for total work") runCmd.Flags().DurationVar(&rc.Deadline, "deadline", time.Minute, "Deadline for total work")
runCmd.Flags().BoolVar(&rc.AnalyzeTests, "tests", false, "Analyze tests (*_test.go)") runCmd.Flags().BoolVar(&rc.AnalyzeTests, "tests", false, "Analyze tests (*_test.go)")
runCmd.Flags().BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, "Print avg and max memory usage of golangci-lint and total time")
runCmd.Flags().StringVarP(&rc.Config, "config", "c", "", "Read config from file path `PATH`")
// Linters settings config // Linters settings config
lsc := &e.cfg.LintersSettings lsc := &e.cfg.LintersSettings
@ -98,8 +103,6 @@ func (e *Executor) initRun() {
runCmd.Flags().StringVar(&ic.DiffFromRevision, "new-from-rev", "", "Show only new issues created after git revision `REV`") runCmd.Flags().StringVar(&ic.DiffFromRevision, "new-from-rev", "", "Show only new issues created after git revision `REV`")
runCmd.Flags().StringVar(&ic.DiffPatchFilePath, "new-from-patch", "", "Show only new issues created in git patch with file path `PATH`") runCmd.Flags().StringVar(&ic.DiffPatchFilePath, "new-from-patch", "", "Show only new issues created in git patch with file path `PATH`")
runCmd.Flags().StringVarP(&e.cfg.Run.Config, "config", "c", "", "Read config from file path `PATH`")
e.parseConfig(runCmd) e.parseConfig(runCmd)
} }
@ -240,19 +243,7 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (chan result.
return runner.Run(ctx, linters, lintCtx), nil return runner.Run(ctx, linters, lintCtx), nil
} }
func (e *Executor) executeRun(cmd *cobra.Command, args []string) { func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), e.cfg.Run.Deadline)
defer cancel()
defer func(startedAt time.Time) {
logrus.Infof("Run took %s", time.Since(startedAt))
}(time.Now())
if e.cfg.Output.PrintWelcomeMessage {
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
}
f := func() error {
issues, err := e.runAnalysis(ctx, args) issues, err := e.runAnalysis(ctx, args)
if err != nil { if err != nil {
return err return err
@ -278,7 +269,27 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
return nil return nil
} }
if err := f(); err != nil { func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
needTrackResources := e.cfg.Run.IsVerbose || e.cfg.Run.PrintResourcesUsage
trackResourcesEndCh := make(chan struct{})
defer func() { // XXX: this defer must be before ctx.cancel defer
if needTrackResources { // wait until resource tracking finished to print properly
<-trackResourcesEndCh
}
}()
ctx, cancel := context.WithTimeout(context.Background(), e.cfg.Run.Deadline)
defer cancel()
if needTrackResources {
go watchResources(ctx, trackResourcesEndCh)
}
if e.cfg.Output.PrintWelcomeMessage {
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
}
if err := e.runAndPrint(ctx, args); err != nil {
log.Print(err) log.Print(err)
if e.exitCode == 0 { if e.exitCode == 0 {
e.exitCode = exitCodeIfFailure e.exitCode = exitCodeIfFailure
@ -289,7 +300,10 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
func (e *Executor) parseConfig(cmd *cobra.Command) { func (e *Executor) parseConfig(cmd *cobra.Command) {
// XXX: hack with double parsing to acces "config" option here // XXX: hack with double parsing to acces "config" option here
if err := cmd.ParseFlags(os.Args); err != nil { if err := cmd.ParseFlags(os.Args); err != nil {
log.Fatalf("Can't parse agrs: %s", err) if err == pflag.ErrHelp {
return
}
log.Fatalf("Can't parse args: %s", err)
} }
if err := viper.BindPFlags(cmd.Flags()); err != nil { if err := viper.BindPFlags(cmd.Flags()); err != nil {
@ -318,4 +332,63 @@ func (e *Executor) parseConfig(cmd *cobra.Command) {
if err := viper.Unmarshal(&e.cfg); err != nil { if err := viper.Unmarshal(&e.cfg); err != nil {
log.Fatalf("Can't unmarshal config by viper: %s", err) log.Fatalf("Can't unmarshal config by viper: %s", err)
} }
if err := e.validateConfig(); err != nil {
log.Fatal(err)
}
}
func (e *Executor) validateConfig() error {
c := e.cfg
if len(c.Run.Args) != 0 {
return errors.New("option run.args in config aren't supported now")
}
if c.Run.CPUProfilePath != "" {
return errors.New("option run.cpuprofilepath in config isn't allowed")
}
return nil
}
func watchResources(ctx context.Context, done chan struct{}) {
startedAt := time.Now()
rssValues := []uint64{}
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
var m runtime.MemStats
runtime.ReadMemStats(&m)
rssValues = append(rssValues, m.Sys)
stop := false
select {
case <-ctx.Done():
stop = true
case <-ticker.C: // track every second
}
if stop {
break
}
}
var avg, max uint64
for _, v := range rssValues {
avg += v
if v > max {
max = v
}
}
avg /= uint64(len(rssValues))
const MB = 1024 * 1024
maxMB := float64(max) / MB
logrus.Infof("Memory: %d samples, avg is %.1fMB, max is %.1fMB",
len(rssValues), float64(avg)/MB, maxMB)
logrus.Infof("Execution took %s", time.Since(startedAt))
close(done)
} }

View File

@ -42,6 +42,7 @@ type Run struct {
IsVerbose bool `mapstructure:"verbose"` IsVerbose bool `mapstructure:"verbose"`
CPUProfilePath string CPUProfilePath string
Concurrency int Concurrency int
PrintResourcesUsage bool `mapstructure:"print-resources-usage"`
Config string Config string