From 1964a6b36d059793b63a270e1ada53641f31286e Mon Sep 17 00:00:00 2001 From: Mario Valderrama <15158349+avorima@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:27:31 +0200 Subject: [PATCH] Allow skipping generated files (#67) * Implement skipping generated files Add a new command-line flag that is disabled by default which will skip processing generated files. Signed-off-by: Mario Valderrama * Add test for skipping generated files Signed-off-by: Mario Valderrama * Fix gofumpt issues Signed-off-by: Mario Valderrama --- README.md | 3 +++ cmd/gci/gcicommand.go | 7 ++++-- cmd/gci/root.go | 4 ++- pkg/analyzer/analyzer.go | 2 +- pkg/gci/configuration.go | 8 +++--- pkg/gci/gci.go | 26 +++++++++++++++++++ pkg/gci/gci_test.go | 28 +++++++++++++++++++++ pkg/gci/internal/skipTest/generated_code.go | 14 +++++++++++ 8 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 pkg/gci/internal/skipTest/generated_code.go diff --git a/README.md b/README.md index 085a843..9ef8c3e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Flags: Std | Standard - Captures all standard packages if they do not match another section (default [Standard,Default]) -x, --SectionSeparator strings SectionSeparators are inserted between Sections (default [NewLine]) + --SkipGeneratedFiles Don't process generated files -h, --help help for print ``` @@ -62,6 +63,7 @@ Flags: Std | Standard - Captures all standard packages if they do not match another section (default [Standard,Default]) -x, --SectionSeparator strings SectionSeparators are inserted between Sections (default [NewLine]) + --SkipGeneratedFiles Don't process generated files -h, --help help for write ``` @@ -83,6 +85,7 @@ Flags: Std | Standard - Captures all standard packages if they do not match another section (default [Standard,Default]) -x, --SectionSeparator strings SectionSeparators are inserted between Sections (default [NewLine]) + --SkipGeneratedFiles Don't process generated files -d, --debug Enables debug output from the formatter -h, --help help for diff ``` diff --git a/cmd/gci/gcicommand.go b/cmd/gci/gcicommand.go index 9c4fdd5..bd2fc48 100644 --- a/cmd/gci/gcicommand.go +++ b/cmd/gci/gcicommand.go @@ -16,7 +16,7 @@ import ( type processingFunc = func(args []string, gciCfg gci.GciConfiguration) error func (e *Executor) newGciCommand(use, short, long string, aliases []string, stdInSupport bool, processingFunc processingFunc) *cobra.Command { - var noInlineComments, noPrefixComments, debug *bool + var noInlineComments, noPrefixComments, debug, skipGeneratedFiles *bool var sectionStrings, sectionSeparatorStrings *[]string cmd := cobra.Command{ Use: use, @@ -26,7 +26,9 @@ func (e *Executor) newGciCommand(use, short, long string, aliases []string, stdI ValidArgsFunction: goFileCompletion, RunE: func(cmd *cobra.Command, args []string) error { fmtCfg := configuration.FormatterConfiguration{*noInlineComments, *noPrefixComments, *debug} - gciCfg, err := gci.GciStringConfiguration{fmtCfg, *sectionStrings, *sectionSeparatorStrings}.Parse() + gciCfg, err := gci.GciStringConfiguration{ + fmtCfg, *sectionStrings, *sectionSeparatorStrings, *skipGeneratedFiles, + }.Parse() if err != nil { return err } @@ -55,5 +57,6 @@ func (e *Executor) newGciCommand(use, short, long string, aliases []string, stdI noPrefixComments = cmd.Flags().Bool("NoPrefixComments", false, "Drops comment lines above an import statement while formatting") sectionStrings = cmd.Flags().StringSliceP("Section", "s", gci.DefaultSections().String(), sectionHelp) sectionSeparatorStrings = cmd.Flags().StringSliceP("SectionSeparator", "x", gci.DefaultSectionSeparators().String(), "SectionSeparators are inserted between Sections") + skipGeneratedFiles = cmd.Flags().Bool("SkipGeneratedFiles", false, "Don't process generated files") return &cmd } diff --git a/cmd/gci/root.go b/cmd/gci/root.go index b61ec7e..ff0e16d 100644 --- a/cmd/gci/root.go +++ b/cmd/gci/root.go @@ -60,7 +60,9 @@ func (e *Executor) runInCompatibilityMode(cmd *cobra.Command, args []string) err // generate section specification from old localFlags format sections := gci.LocalFlagsToSections(*e.localFlags) sectionSeparators := gci.DefaultSectionSeparators() - cfg := gci.GciConfiguration{configuration.FormatterConfiguration{false, false, false}, sections, sectionSeparators} + cfg := gci.GciConfiguration{ + configuration.FormatterConfiguration{false, false, false}, sections, sectionSeparators, false, + } if *e.writeMode { return gci.WriteFormattedFiles(args, cfg) } diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index 6ca93e1..04bdcea 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -120,7 +120,7 @@ func parseGciConfiguration() (*gci.GciConfiguration, error) { sectionSeparatorStrings = strings.Split(sectionSeparatorsStr, SectionDelimiter) fmt.Println(sectionSeparatorsStr) } - return gci.GciStringConfiguration{fmtCfg, sectionStrings, sectionSeparatorStrings}.Parse() + return gci.GciStringConfiguration{fmtCfg, sectionStrings, sectionSeparatorStrings, false}.Parse() } func generateCmdLine(cfg gci.GciConfiguration) string { diff --git a/pkg/gci/configuration.go b/pkg/gci/configuration.go index 5825238..4563ddb 100644 --- a/pkg/gci/configuration.go +++ b/pkg/gci/configuration.go @@ -11,14 +11,16 @@ import ( type GciConfiguration struct { configuration.FormatterConfiguration - Sections SectionList - SectionSeparators SectionList + Sections SectionList + SectionSeparators SectionList + SkipGeneratedFiles bool } type GciStringConfiguration struct { Cfg configuration.FormatterConfiguration `yaml:",inline"` SectionStrings []string `yaml:"sections"` SectionSeparatorStrings []string `yaml:"sectionseparators"` + SkipGeneratedFiles bool `yaml:"skipGeneratedFiles"` } func (g GciStringConfiguration) Parse() (*GciConfiguration, error) { @@ -37,7 +39,7 @@ func (g GciStringConfiguration) Parse() (*GciConfiguration, error) { return nil, err } } - return &GciConfiguration{g.Cfg, sections, sectionSeparators}, nil + return &GciConfiguration{g.Cfg, sections, sectionSeparators, g.SkipGeneratedFiles}, nil } func initializeGciConfigFromYAML(filePath string) (*GciConfiguration, error) { diff --git a/pkg/gci/gci.go b/pkg/gci/gci.go index eb30c11..ab41590 100644 --- a/pkg/gci/gci.go +++ b/pkg/gci/gci.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "strings" "sync" "github.com/hexops/gotextdiff" @@ -129,6 +130,9 @@ func LoadFormatGoFile(file io.FileObj, cfg GciConfiguration) (unmodifiedFile, fo if err != nil { return nil, nil, err } + if cfg.SkipGeneratedFiles && isGeneratedFileByComment(string(unmodifiedFile)) { + return unmodifiedFile, unmodifiedFile, nil + } formattedFile, err = formatGoFile(unmodifiedFile, cfg) if err != nil { @@ -141,3 +145,25 @@ func LoadFormatGoFile(file io.FileObj, cfg GciConfiguration) (unmodifiedFile, fo } return unmodifiedFile, formattedFile, nil } + +// isGenerated reports whether the source file is generated code. +// Using a bit laxer rules than https://golang.org/s/generatedcode to +// match more generated code. +// Taken from https://github.com/golangci/golangci-lint. +func isGeneratedFileByComment(doc string) bool { + const ( + genCodeGenerated = "code generated" + genDoNotEdit = "do not edit" + genAutoFile = "autogenerated file" // easyjson + ) + + markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile} + doc = strings.ToLower(doc) + for _, marker := range markers { + if strings.Contains(doc, marker) { + return true + } + } + + return false +} diff --git a/pkg/gci/gci_test.go b/pkg/gci/gci_test.go index 832a60a..2137cda 100644 --- a/pkg/gci/gci_test.go +++ b/pkg/gci/gci_test.go @@ -110,6 +110,34 @@ func TestSkippingOverIncorrectlyFormattedFiles(t *testing.T) { assert.True(t, <-validFileProcessedChan) } +func TestSkippingGeneratedFiles(t *testing.T) { + cfg, err := GciStringConfiguration{SkipGeneratedFiles: true}.Parse() + assert.NoError(t, err) + + var generatedCodeCtr int + var files []io.FileObj + files = append(files, TestFile{io.File{"internal/skipTest/generated_code.go"}, &generatedCodeCtr}) + + validFileProcessedChan := make(chan bool, len(files)) + + generatorFunc := func() ([]io.FileObj, error) { + return files, nil + } + fileAccessTestFunc := func(_ string, unmodifiedFile, formattedFile []byte) error { + validFileProcessedChan <- true + assert.NotEmpty(t, unmodifiedFile) + assert.Equal(t, string(unmodifiedFile), string(formattedFile)) + return nil + } + err = processFiles(generatorFunc, *cfg, fileAccessTestFunc) + + assert.NoError(t, err) + // check all files have been accessed + assert.Equal(t, generatedCodeCtr, 1) + // check that processing for the valid file was called + assert.True(t, <-validFileProcessedChan) +} + type TestFile struct { wrappedFile io.File accessCounter *int diff --git a/pkg/gci/internal/skipTest/generated_code.go b/pkg/gci/internal/skipTest/generated_code.go new file mode 100644 index 0000000..8a8f980 --- /dev/null +++ b/pkg/gci/internal/skipTest/generated_code.go @@ -0,0 +1,14 @@ +package testdata + +// DO NOT EDIT + +import ( + "testing" + + "fmt" +) + +// nolint +func Test(t *testing.T) { + fmt.Println("test") +}