diff --git a/.github/workflows/gci.yml b/.github/workflows/gci.yml index 7b05d40..4c96103 100644 --- a/.github/workflows/gci.yml +++ b/.github/workflows/gci.yml @@ -8,7 +8,7 @@ jobs: name: Build ${{ matrix.target_os }}_${{ matrix.target_arch }} binaries runs-on: ${{ matrix.os }} env: - GOVER: 1.18 + GOVER: 1.21 GOOS: ${{ matrix.target_os }} GOARCH: ${{ matrix.target_arch }} GOPROXY: https://proxy.golang.org @@ -34,11 +34,11 @@ jobs: target_arch: arm steps: - name: Set up Go ${{ env.GOVER }} - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: ${{ env.GOVER }} - name: Check out code into the Go module directory - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Go modules (Linux) if: matrix.target_os == 'linux' uses: actions/cache@v3 @@ -71,7 +71,7 @@ jobs: ${{ matrix.target_os }}-${{ matrix.target_arch }}-go-${{ env.GOVER }}-build- - name: golangci-lint if: matrix.target_arch == 'amd64' && matrix.target_os == 'linux' - uses: golangci/golangci-lint-action@v3.2.0 + uses: golangci/golangci-lint-action@v4 with: version: ${{ env.GOLANGCILINT_VER }} - name: Run make test diff --git a/README.md b/README.md index b68248d..851d37d 100644 --- a/README.md +++ b/README.md @@ -101,13 +101,14 @@ Flags: --custom-order Enable custom order of sections -d, --debug Enables debug output from the formatter -h, --help help for print - -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias. The default value is [standard,default]. + -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias > localmodule. The default value is [standard,default]. standard - standard section that Go provides officially, like "fmt" Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix. Multiple custom prefixes may be provided, they will be rendered as distinct sections separated by newline. You can regroup multiple prefixes by separating them with comma: Prefix(github.com/daixiang0,gitlab.com/daixiang0,daixiang0) default - default section, contains all rest imports blank - blank section, contains all blank imports. dot - dot section, contains all dot imports. (default [standard,default]) alias - alias section, contains all alias imports. + localmodule: localmodule section, contains all imports from local packages --skip-generated Skip generated files --skip-vendor Skip files inside vendor directory ``` @@ -126,13 +127,14 @@ Flags: --custom-order Enable custom order of sections -d, --debug Enables debug output from the formatter -h, --help help for write - -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias. The default value is [standard,default]. + -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias > localmodule. The default value is [standard,default]. standard - standard section that Go provides officially, like "fmt" Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix. Multiple custom prefixes may be provided, they will be rendered as distinct sections separated by newline. You can regroup multiple prefixes by separating them with comma: Prefix(github.com/daixiang0,gitlab.com/daixiang0,daixiang0) default - default section, contains all rest imports blank - blank section, contains all blank imports. dot - dot section, contains all dot imports. (default [standard,default]) alias - alias section, contains all alias imports. + localmodule: localmodule section, contains all imports from local packages --skip-generated Skip generated files --skip-vendor Skip files inside vendor directory ``` @@ -148,13 +150,14 @@ Flags: --custom-order Enable custom order of sections -d, --debug Enables debug output from the formatter -h, --help help for list - -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias. The default value is [standard,default]. + -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias > localmodule. The default value is [standard,default]. standard - standard section that Go provides officially, like "fmt" Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix. Multiple custom prefixes may be provided, they will be rendered as distinct sections separated by newline. You can regroup multiple prefixes by separating them with comma: Prefix(github.com/daixiang0,gitlab.com/daixiang0,daixiang0) default - default section, contains all rest imports blank - blank section, contains all blank imports. - alias - alias section, contains all alias imports. dot - dot section, contains all dot imports. (default [standard,default]) + alias - alias section, contains all alias imports. + localmodule: localmodule section, contains all imports from local packages --skip-generated Skip generated files --skip-vendor Skip files inside vendor directory ``` @@ -170,13 +173,14 @@ Flags: --custom-order Enable custom order of sections -d, --debug Enables debug output from the formatter -h, --help help for diff - -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias. The default value is [standard,default]. + -s, --section stringArray Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias > localmodule. The default value is [standard,default]. standard - standard section that Go provides officially, like "fmt" Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix. Multiple custom prefixes may be provided, they will be rendered as distinct sections separated by newline. You can regroup multiple prefixes by separating them with comma: Prefix(github.com/daixiang0,gitlab.com/daixiang0,daixiang0) default - default section, contains all rest imports blank - blank section, contains all blank imports. - alias - alias section, contains all alias imports. dot - dot section, contains all dot imports. (default [standard,default]) + alias - alias section, contains all alias imports. + localmodule: localmodule section, contains all imports from local packages --skip-generated Skip generated files --skip-vendor Skip files inside vendor directory ``` @@ -367,3 +371,4 @@ import ( - Add more testcases - Support imports completion (please use `goimports` first then use GCI) - Optimize comments +- Remove Analyzer layer and fully use analyzer syntax diff --git a/cmd/gci/gcicommand.go b/cmd/gci/gcicommand.go index a1a2236..7279ccf 100644 --- a/cmd/gci/gcicommand.go +++ b/cmd/gci/gcicommand.go @@ -48,13 +48,14 @@ func (e *Executor) newGciCommand(use, short, long string, aliases []string, stdI debug = cmd.Flags().BoolP("debug", "d", false, "Enables debug output from the formatter") - sectionHelp := `Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias. The default value is [standard,default]. + sectionHelp := `Sections define how inputs will be processed. Section names are case-insensitive and may contain parameters in (). The section order is standard > default > custom > blank > dot > alias > localmodule. The default value is [standard,default]. standard - standard section that Go provides officially, like "fmt" Prefix(github.com/daixiang0) - custom section, groups all imports with the specified Prefix. Imports will be matched to the longest Prefix. Multiple custom prefixes may be provided, they will be rendered as distinct sections separated by newline. You can regroup multiple prefixes by separating them with comma: Prefix(github.com/daixiang0,gitlab.com/daixiang0,daixiang0) default - default section, contains all rest imports blank - blank section, contains all blank imports. dot - dot section, contains all dot imports. -alias - alias section, contains all alias imports.` +alias - alias section, contains all alias imports. +localmodule: localmodule section, contains all imports from local packages` skipGenerated = cmd.Flags().Bool("skip-generated", false, "Skip generated files") skipVendor = cmd.Flags().Bool("skip-vendor", false, "Skip files inside vendor directory") diff --git a/go.mod b/go.mod index ea8cc8f..2c29f71 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,19 @@ module github.com/daixiang0/gci -go 1.18 +go 1.21 + +toolchain go1.22.1 require ( + github.com/golangci/modinfo v0.3.3 github.com/hexops/gotextdiff v1.0.3 github.com/pmezard/go-difflib v1.0.0 github.com/spf13/cobra v1.6.1 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.24.0 - golang.org/x/mod v0.8.0 - golang.org/x/sync v0.1.0 - golang.org/x/tools v0.6.0 + golang.org/x/mod v0.16.0 + golang.org/x/sync v0.6.0 + golang.org/x/tools v0.19.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -20,5 +23,4 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/sys v0.5.0 // indirect ) diff --git a/go.sum b/go.sum index 53d4463..a365553 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,17 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golangci/modinfo v0.3.3 h1:YBQDZpDMJpe5mtd0klUFYL8tSVkmF3cmm0fZ48sc7+s= +github.com/golangci/modinfo v0.3.3/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -16,30 +20,24 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go index d782201..85a89e8 100644 --- a/pkg/analyzer/analyzer.go +++ b/pkg/analyzer/analyzer.go @@ -5,7 +5,9 @@ import ( "go/token" "strings" + "github.com/golangci/modinfo" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" "github.com/daixiang0/gci/pkg/config" "github.com/daixiang0/gci/pkg/gci" @@ -45,6 +47,10 @@ var Analyzer = &analysis.Analyzer{ Name: "gci", Doc: "A tool that control Go package import order and make it always deterministic.", Run: runAnalysis, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + modinfo.Analyzer, + }, } func runAnalysis(pass *analysis.Pass) (interface{}, error) { @@ -62,8 +68,12 @@ func runAnalysis(pass *analysis.Pass) (interface{}, error) { return nil, InvalidNumberOfFilesInAnalysis{expectedNumFiles, foundNumFiles} } - // read configuration options - gciCfg, err := parseGciConfiguration() + file, err := modinfo.FindModuleFromPass(pass) + if err != nil { + return nil, err + } + + gciCfg, err := generateGciConfiguration(file.Path).Parse() if err != nil { return nil, err } @@ -91,7 +101,7 @@ func runAnalysis(pass *analysis.Pass) (interface{}, error) { return nil, nil } -func parseGciConfiguration() (*config.Config, error) { +func generateGciConfiguration(modPath string) *config.YamlConfig { fmtCfg := config.BoolConfig{ NoInlineComments: noInlineComments, NoPrefixComments: noPrefixComments, @@ -109,7 +119,8 @@ func parseGciConfiguration() (*config.Config, error) { sectionSeparatorStrings = strings.Split(sectionSeparatorsStr, SectionDelimiter) fmt.Println(sectionSeparatorsStr) } - return config.YamlConfig{Cfg: fmtCfg, SectionStrings: sectionStrings, SectionSeparatorStrings: sectionSeparatorStrings}.Parse() + + return &config.YamlConfig{Cfg: fmtCfg, SectionStrings: sectionStrings, SectionSeparatorStrings: sectionSeparatorStrings, ModPath: modPath} } func generateCmdLine(cfg config.Config) string { diff --git a/pkg/config/config.go b/pkg/config/config.go index 18b9c74..33ea8d2 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,6 +38,10 @@ type YamlConfig struct { Cfg BoolConfig `yaml:",inline"` SectionStrings []string `yaml:"sections"` SectionSeparatorStrings []string `yaml:"sectionseparators"` + + // Since history issue, Golangci-lint needs Analyzer to run and GCI add an Anzlyzer layer to integrate. + // The ModPath param is only from analyer.go, no need to set it in all other places. + ModPath string `yaml:"-"` } func (g YamlConfig) Parse() (*Config, error) { @@ -50,7 +54,7 @@ func (g YamlConfig) Parse() (*Config, error) { if sections == nil { sections = section.DefaultSections() } - if err := configureSections(sections); err != nil { + if err := configureSections(sections, g.ModPath); err != nil { return nil, err } @@ -93,11 +97,14 @@ func ParseConfig(in string) (*Config, error) { return gciCfg, nil } -func configureSections(sections section.SectionList) error { +// configureSections now only do golang module path finding. +// Since history issue, Golangci-lint needs Analyzer to run and GCI add an Anzlyzer layer to integrate. +// The path param is from analyer.go, in all other places should pass empty string. +func configureSections(sections section.SectionList, path string) error { for _, sec := range sections { switch s := sec.(type) { case *section.LocalModule: - if err := s.Configure(); err != nil { + if err := s.Configure(path); err != nil { return err } } diff --git a/pkg/section/local_module.go b/pkg/section/local_module.go index 07821fe..5af74b3 100644 --- a/pkg/section/local_module.go +++ b/pkg/section/local_module.go @@ -35,14 +35,17 @@ func (m *LocalModule) Type() string { // Configure configures the module section by finding the module // for the current path -func (m *LocalModule) Configure() error { - modPath, err := findLocalModule() - if err != nil { - return fmt.Errorf("finding local modules for `localModule` configuration: %w", err) +func (m *LocalModule) Configure(path string) error { + if path != "" { + m.Path = path + } else { + path, err := findLocalModule() + if err != nil { + return fmt.Errorf("finding local modules for `localModule` configuration: %w", err) + } + m.Path = path } - m.Path = modPath - return nil }