Skip to content

Commit ac1a976

Browse files
committed
Merge remote-tracking branch 'origin/feature/initial-improvements'
2 parents e8d7807 + 05f0937 commit ac1a976

File tree

3 files changed

+109
-37
lines changed

3 files changed

+109
-37
lines changed

.golangci.example.yml

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
run:
2-
args:
3-
- ./...
42
verbose: true
53
concurrency: 4
64
deadline: 1m

internal/commands/run.go

+105-32
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package commands
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"go/build"
78
"go/token"
89
"log"
910
"os"
11+
"runtime"
1012
"strings"
1113
"time"
1214

@@ -21,6 +23,7 @@ import (
2123
"github.com/golangci/golangci-lint/pkg/result/processors"
2224
"github.com/sirupsen/logrus"
2325
"github.com/spf13/cobra"
26+
"github.com/spf13/pflag"
2427
"github.com/spf13/viper"
2528
"golang.org/x/tools/go/loader"
2629
)
@@ -51,6 +54,8 @@ func (e *Executor) initRun() {
5154
runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)")
5255
runCmd.Flags().DurationVar(&rc.Deadline, "deadline", time.Minute, "Deadline for total work")
5356
runCmd.Flags().BoolVar(&rc.AnalyzeTests, "tests", false, "Analyze tests (*_test.go)")
57+
runCmd.Flags().BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, "Print avg and max memory usage of golangci-lint and total time")
58+
runCmd.Flags().StringVarP(&rc.Config, "config", "c", "", "Read config from file path `PATH`")
5459

5560
// Linters settings config
5661
lsc := &e.cfg.LintersSettings
@@ -98,8 +103,6 @@ func (e *Executor) initRun() {
98103
runCmd.Flags().StringVar(&ic.DiffFromRevision, "new-from-rev", "", "Show only new issues created after git revision `REV`")
99104
runCmd.Flags().StringVar(&ic.DiffPatchFilePath, "new-from-patch", "", "Show only new issues created in git patch with file path `PATH`")
100105

101-
runCmd.Flags().StringVarP(&e.cfg.Run.Config, "config", "c", "", "Read config from file path `PATH`")
102-
103106
e.parseConfig(runCmd)
104107
}
105108

@@ -240,45 +243,53 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (chan result.
240243
return runner.Run(ctx, linters, lintCtx), nil
241244
}
242245

243-
func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
244-
ctx, cancel := context.WithTimeout(context.Background(), e.cfg.Run.Deadline)
245-
defer cancel()
246+
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
247+
issues, err := e.runAnalysis(ctx, args)
248+
if err != nil {
249+
return err
250+
}
246251

247-
defer func(startedAt time.Time) {
248-
logrus.Infof("Run took %s", time.Since(startedAt))
249-
}(time.Now())
252+
var p printers.Printer
253+
if e.cfg.Output.Format == config.OutFormatJSON {
254+
p = printers.NewJSON()
255+
} else {
256+
p = printers.NewText(e.cfg.Output.PrintIssuedLine,
257+
e.cfg.Output.Format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName)
258+
}
259+
gotAnyIssues, err := p.Print(issues)
260+
if err != nil {
261+
return fmt.Errorf("can't print %d issues: %s", len(issues), err)
262+
}
250263

251-
if e.cfg.Output.PrintWelcomeMessage {
252-
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
264+
if gotAnyIssues {
265+
e.exitCode = e.cfg.Run.ExitCodeIfIssuesFound
266+
return nil
253267
}
254268

255-
f := func() error {
256-
issues, err := e.runAnalysis(ctx, args)
257-
if err != nil {
258-
return err
259-
}
269+
return nil
270+
}
260271

261-
var p printers.Printer
262-
if e.cfg.Output.Format == config.OutFormatJSON {
263-
p = printers.NewJSON()
264-
} else {
265-
p = printers.NewText(e.cfg.Output.PrintIssuedLine,
266-
e.cfg.Output.Format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName)
267-
}
268-
gotAnyIssues, err := p.Print(issues)
269-
if err != nil {
270-
return fmt.Errorf("can't print %d issues: %s", len(issues), err)
272+
func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
273+
needTrackResources := e.cfg.Run.IsVerbose || e.cfg.Run.PrintResourcesUsage
274+
trackResourcesEndCh := make(chan struct{})
275+
defer func() { // XXX: this defer must be before ctx.cancel defer
276+
if needTrackResources { // wait until resource tracking finished to print properly
277+
<-trackResourcesEndCh
271278
}
279+
}()
272280

273-
if gotAnyIssues {
274-
e.exitCode = e.cfg.Run.ExitCodeIfIssuesFound
275-
return nil
276-
}
281+
ctx, cancel := context.WithTimeout(context.Background(), e.cfg.Run.Deadline)
282+
defer cancel()
277283

278-
return nil
284+
if needTrackResources {
285+
go watchResources(ctx, trackResourcesEndCh)
286+
}
287+
288+
if e.cfg.Output.PrintWelcomeMessage {
289+
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
279290
}
280291

281-
if err := f(); err != nil {
292+
if err := e.runAndPrint(ctx, args); err != nil {
282293
log.Print(err)
283294
if e.exitCode == 0 {
284295
e.exitCode = exitCodeIfFailure
@@ -289,7 +300,10 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
289300
func (e *Executor) parseConfig(cmd *cobra.Command) {
290301
// XXX: hack with double parsing to acces "config" option here
291302
if err := cmd.ParseFlags(os.Args); err != nil {
292-
log.Fatalf("Can't parse agrs: %s", err)
303+
if err == pflag.ErrHelp {
304+
return
305+
}
306+
log.Fatalf("Can't parse args: %s", err)
293307
}
294308

295309
if err := viper.BindPFlags(cmd.Flags()); err != nil {
@@ -318,4 +332,63 @@ func (e *Executor) parseConfig(cmd *cobra.Command) {
318332
if err := viper.Unmarshal(&e.cfg); err != nil {
319333
log.Fatalf("Can't unmarshal config by viper: %s", err)
320334
}
335+
336+
if err := e.validateConfig(); err != nil {
337+
log.Fatal(err)
338+
}
339+
}
340+
341+
func (e *Executor) validateConfig() error {
342+
c := e.cfg
343+
if len(c.Run.Args) != 0 {
344+
return errors.New("option run.args in config aren't supported now")
345+
}
346+
347+
if c.Run.CPUProfilePath != "" {
348+
return errors.New("option run.cpuprofilepath in config isn't allowed")
349+
}
350+
351+
return nil
352+
}
353+
354+
func watchResources(ctx context.Context, done chan struct{}) {
355+
startedAt := time.Now()
356+
357+
rssValues := []uint64{}
358+
ticker := time.NewTicker(100 * time.Millisecond)
359+
defer ticker.Stop()
360+
361+
for {
362+
var m runtime.MemStats
363+
runtime.ReadMemStats(&m)
364+
365+
rssValues = append(rssValues, m.Sys)
366+
367+
stop := false
368+
select {
369+
case <-ctx.Done():
370+
stop = true
371+
case <-ticker.C: // track every second
372+
}
373+
374+
if stop {
375+
break
376+
}
377+
}
378+
379+
var avg, max uint64
380+
for _, v := range rssValues {
381+
avg += v
382+
if v > max {
383+
max = v
384+
}
385+
}
386+
avg /= uint64(len(rssValues))
387+
388+
const MB = 1024 * 1024
389+
maxMB := float64(max) / MB
390+
logrus.Infof("Memory: %d samples, avg is %.1fMB, max is %.1fMB",
391+
len(rssValues), float64(avg)/MB, maxMB)
392+
logrus.Infof("Execution took %s", time.Since(startedAt))
393+
close(done)
321394
}

pkg/config/config.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ var DefaultExcludePatterns = []string{
3939
}
4040

4141
type Run struct {
42-
IsVerbose bool `mapstructure:"verbose"`
43-
CPUProfilePath string
44-
Concurrency int
42+
IsVerbose bool `mapstructure:"verbose"`
43+
CPUProfilePath string
44+
Concurrency int
45+
PrintResourcesUsage bool `mapstructure:"print-resources-usage"`
4546

4647
Config string
4748

0 commit comments

Comments
 (0)