diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..54f94c5 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,28 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v1 + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.30 + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..348e6ca --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,21 @@ +on: + release: + types: [created] + +jobs: + releases-matrix: + name: Release Go Binary + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin] + goarch: [amd64] + steps: + - uses: actions/checkout@v2 + - uses: wangyoucao577/go-release-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: "https://golang.org/dl/go1.15.linux-amd64.tar.gz" + binary_name: "goimportssort" \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..79e7aca --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,18 @@ +on: [push, pull_request] +name: Test +jobs: + test: + strategy: + matrix: + go-version: [1.14.x, 1.15.x] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + - name: Test + run: go test ./... \ No newline at end of file diff --git a/README.md b/README.md index 6cfdc45..cbae06c 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,22 @@ This tool aims to automatically fix the order of golang imports. It will split y ## Installation ``` -$ go get github.com/AanZee/goimportssort +$ go get -u github.com/AanZee/goimportssort ``` ## Usage ``` usage: goimportssort [flags] [path ...] - -l write results to stdout + -l write results to stdout (default false) -local string - put imports beginning with this string after 3rd-party packages; comma-separated list - -v verbose logging - -w write result to (source) file instead of stdout + put imports beginning with this string after 3rd-party packages; comma-separated list +(default tries to get module name of current directory) + -v verbose logging (default false) + -w write result to (source) file (default false) ``` Imports will be sorted according to their categories. ``` -$ goimportssort -v -l --local "github.com/AanZee/goimportssort" example.go +$ goimportssort -v -w ./.. ``` For example: diff --git a/_fixtures/sample2.txt b/_fixtures/sample2.txt deleted file mode 100644 index ccc6c45..0000000 --- a/_fixtures/sample2.txt +++ /dev/null @@ -1,29 +0,0 @@ -package main - -// builtin -// external -// local -import ( - "fmt" - "fmt2" - "log" - - APA "bitbucket.org/example/package/name" - APZ "bitbucket.org/example/package/name" - "bitbucket.org/example/package/name2" - "bitbucket.org/example/package/name3" // foopsie - "bitbucket.org/example/package/name4" - - "github.com/AanZee/goimportssort/package1" - // a - "github.com/AanZee/goimportssort/package2" - - /* - mijn comment - */ -) -// klaslkasdko - -func main() { - fmt.Println("Hello!") -} \ No newline at end of file diff --git a/_fixtures/sample3.txt b/_fixtures/sample3.txt deleted file mode 100644 index 12b14d8..0000000 --- a/_fixtures/sample3.txt +++ /dev/null @@ -1,30 +0,0 @@ -package main - -// builtin -// external -// local -import ( - "fmt" - "log" - - APA "bitbucket.org/example/package/name" - APZ "bitbucket.org/example/package/name" - "bitbucket.org/example/package/name2" - "bitbucket.org/example/package/name3" // foopsie - "bitbucket.org/example/package/name4" - - "github.com/AanZee/goimportssort/package1" - // a - "github.com/AanZee/goimportssort/package2" - - /* - mijn comment - */ - "net/http/httptest" - "database/sql/driver" -) -// klaslkasdko - -func main() { - fmt.Println("Hello!") -} \ No newline at end of file diff --git a/go.mod b/go.mod index 964a914..96193b8 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,6 @@ go 1.14 require ( github.com/dave/dst v0.23.1 github.com/stretchr/testify v1.5.1 + golang.org/x/mod v0.2.0 golang.org/x/tools v0.0.0-20200326210457-5d86d385bf88 ) diff --git a/goimportssort.go b/goimportssort.go index 2869646..1842d7d 100644 --- a/goimportssort.go +++ b/goimportssort.go @@ -1,7 +1,6 @@ /* goimportssort sorts your Go import lines in three categories: inbuilt, external and local. - $ go get github.com/AanZee/goimportssort -Happy hacking! + $ go get -u github.com/AanZee/goimportssort */ package main @@ -12,37 +11,38 @@ import ( "fmt" "go/parser" "go/token" - "golang.org/x/tools/go/packages" "io" "io/ioutil" "log" "os" - "os/exec" "path/filepath" "runtime" "sort" "strings" + "golang.org/x/mod/modfile" + "golang.org/x/tools/go/packages" + "github.com/dave/dst" "github.com/dave/dst/decorator" "github.com/dave/dst/dstutil" ) var ( - list = flag.Bool("l", false, "write results to stdout") - write = flag.Bool("w", false, "write result to (source) file instead of stdout") - localPrefix = flag.String("local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list") - verbose bool // verbose logging + list = flag.Bool("l", false, "write results to stdout") + write = flag.Bool("w", false, "write result to (source) file instead of stdout") + localPrefix = flag.String("local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list") + verbose bool // verbose logging standardPackages = make(map[string]struct{}) ) -// ImpModel is used for storing import information +// impModel is used for storing import information type impModel struct { path string localReference string } -// String is used to get a string representation of an import +// string is used to get a string representation of an import func (m impModel) string() string { if m.localReference == "" { return m.path @@ -79,7 +79,7 @@ func goImportsSortMain() error { if *localPrefix == "" { log.Println("no prefix found, using module name") - moduleName, _ := getModuleName() + moduleName := getModuleName() if moduleName != "" { localPrefix = &moduleName } else { @@ -218,8 +218,8 @@ func process(src []byte) (output []byte, err error) { func replaceImports(newImports []byte, node *dst.File) ([]byte, error) { var ( output []byte - err error - buf bytes.Buffer + err error + buf bytes.Buffer ) // remove + update @@ -239,8 +239,7 @@ func replaceImports(newImports []byte, node *dst.File) ([]byte, error) { packageName := node.Name.Name output = bytes.Replace(buf.Bytes(), []byte("package "+packageName), append([]byte("package "+packageName+"\n\n"), newImports...), 1) } else { - fmt.Println("err not nil:") - fmt.Println(err) + log.Println(err) } return output, err @@ -332,22 +331,31 @@ func isStandardPackage(pkg string) bool { return ok } +func getRootPath() (string, error) { + _, b, _, ok := runtime.Caller(0) + if !ok { + return "", errors.New("could not get root directory") + } + basepath := filepath.Dir(b) + + return basepath, nil +} + // getModuleName parses the GOMOD name -func getModuleName() (string, error) { - gomodCmd := exec.Command("go", "env", "GOMOD") // TODO: Check if there's a better way to get GOMOD - gomod, err := gomodCmd.Output() +func getModuleName() string { + root, err := getRootPath() if err != nil { - log.Println("could not run: go env GOMOD") - return "", err + log.Println("error when getting root path: ", err) + return "" } - gomodStr := strings.TrimSpace(string(gomod)) - moduleCmd := exec.Command("awk", "/module/ {print $2}", gomodStr) // TODO: Check if there's a better way - module, err := moduleCmd.Output() + goModBytes, err := ioutil.ReadFile(filepath.Join(root, "go.mod")) if err != nil { - return "", err + log.Println("error when reading mod file: ", err) + return "" } - moduleStr := strings.TrimSpace(string(module)) - return moduleStr, nil + modName := modfile.ModulePath(goModBytes) + + return modName } diff --git a/goimportssort_test.go b/goimportssort_test.go index 1bea50f..285e430 100644 --- a/goimportssort_test.go +++ b/goimportssort_test.go @@ -10,6 +10,36 @@ import ( func TestProcessFile(t *testing.T) { asserts := assert.New(t) *localPrefix = "github.com/AanZee/goimportssort" + reader := strings.NewReader(`package main + +// builtin +// external +// local +import ( + "fmt" + "log" + + APA "bitbucket.org/example/package/name" + APZ "bitbucket.org/example/package/name" + "bitbucket.org/example/package/name2" + "bitbucket.org/example/package/name3" // foopsie + "bitbucket.org/example/package/name4" + + "github.com/AanZee/goimportssort/package1" + // a + "github.com/AanZee/goimportssort/package2" + + /* + mijn comment + */ + "net/http/httptest" + "database/sql/driver" +) +// klaslkasdko + +func main() { + fmt.Println("Hello!") +}`) want := `package main import ( @@ -33,7 +63,7 @@ func main() { } ` - output, err := processFile("_fixtures/sample3.txt", nil, os.Stdout) + output, err := processFile("", reader, os.Stdout) asserts.NotEqual(nil, output) asserts.Equal(nil, err) asserts.Equal(want, string(output)) @@ -44,7 +74,7 @@ func TestProcessFile_SingleImport(t *testing.T) { *localPrefix = "github.com/AanZee/goimportssort" reader := strings.NewReader( -`package main + `package main import "github.com/AanZee/goimportssort/package1" @@ -57,7 +87,7 @@ func main() { asserts.NotEqual(nil, output) asserts.Equal(nil, err) asserts.Equal( -`package main + `package main import ( "github.com/AanZee/goimportssort/package1" @@ -129,4 +159,48 @@ import ( "github.com/AanZee/goimportssort/package2" ) `, string(output)) -} \ No newline at end of file +} + +func TestProcessFile_WronglyFormattedGo(t *testing.T) { + asserts := assert.New(t) + *localPrefix = "github.com/AanZee/goimportssort" + + reader := strings.NewReader( + `import "github.com/AanZee/goimportssort/package1" + + +func main() { + fmt.Println("Hello!") +}`) + output, err := processFile("", reader, os.Stdout) + asserts.NotEqual(nil, output) + asserts.Equal(nil, err) + asserts.Equal( + `package main + +import ( + "github.com/AanZee/goimportssort/package1" +) + +func main() { + fmt.Println("Hello!") +} +`, string(output)) +} + +func TestGetRootPath(t *testing.T) { + asserts := assert.New(t) + + path, err := getRootPath() + + asserts.Nil(err) + asserts.Contains(path, "/goimportssort") +} + +func TestGetModuleName(t *testing.T) { + asserts := assert.New(t) + + name := getModuleName() + + asserts.Equal("github.com/AanZee/goimportssort", name) +}