diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index ddd9f35..7fb21e7 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - go: ["1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20", "1.21"] + go: ["1.19", "1.20", "1.21"] steps: # Set fetch-depth: 0 to fetch commit history and tags for use in version calculation - name: Check out code diff --git a/README.md b/README.md index 104779b..2629b8b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ If you just want to list the files that porto would change vanity import, run: ```bash porto -l path/to/library ``` + ## Inclusion/exclusion rules `porto` skips autogenerated, internal, third party and vendored files by default. You can customize what files get included using some flags: @@ -38,13 +39,22 @@ porto -l path/to/library ```bash porto --skip-files ".*\\.pb\\.go$" path/to/library ``` + - If you want to ignore directories (e.g. tooling directories), pass the `--skip-dirs` flag: ```bash porto --skip-dirs "^tools$" path/to/library ``` + - If you want to include `internal` folders and directories skipped by default: ```bash porto --include-internal --skip-dirs-use-default=false path/to/library ``` + +- If you want to restrict to certain files e.g. `doc.go` you +can use: + +```bash +porto --restrict-to-files "doc.go$" path/to/library +``` diff --git a/cmd/porto/main.go b/cmd/porto/main.go index 0d5e829..25a961e 100644 --- a/cmd/porto/main.go +++ b/cmd/porto/main.go @@ -12,27 +12,20 @@ import ( ) func main() { - flagWriteOutputToFile := flag.Bool("w", false, "write result to (source) file instead of stdout") - flagListDiff := flag.Bool("l", false, "list files whose vanity import differs from porto's") + flagWriteOutputToFile := flag.Bool("w", false, "Write result to (source) file instead of stdout") + flagListDiff := flag.Bool("l", false, "List files whose vanity import differs from porto's") flagSkipFiles := flag.String("skip-files", "", "Regexps of files to skip") flagSkipDirs := flag.String("skip-dirs", "", "Regexps of directories to skip") - flagSkipDefaultDirs := flag.Bool("skip-dirs-use-default", true, "use default skip directory list") - flagIncludeInternal := flag.Bool("include-internal", false, "include internal folders") + flagSkipDefaultDirs := flag.Bool("skip-dirs-use-default", true, "Use default skip directory list") + flagIncludeInternal := flag.Bool("include-internal", false, "Include internal folders") + flagRestrictToFiles := flag.String("restrict-to-files", "", "Regexps of files to restrict the inspection on. It takes precedence over -skip-files and -skip-dirs") flag.Parse() baseDir := flag.Arg(0) + if len(flag.Args()) == 0 { + flag.Usage() fmt.Println(` -usage: porto [options] - -Options: --w Write result to (source) file instead of stdout (default: false) --l List files whose vanity import differs from porto's (default: false) ---skip-files Regexps of files to skip ---skip-dirs Regexps of directories to skip ---skip-dirs-use-default Use default skip directory list (default: true) ---include-internal Include internal folders - Examples: Add import path to a folder @@ -53,7 +46,12 @@ Add import path to a folder skipFilesRegex, err := porto.GetRegexpList(*flagSkipFiles) if err != nil { - log.Fatalf("failed to build files regexes: %v", err) + log.Fatalf("failed to build files regexes to exclude: %v", err) + } + + restrictToFilesRegex, err := porto.GetRegexpList(*flagRestrictToFiles) + if err != nil { + log.Fatalf("failed to build files regexes to include: %v", err) } var skipDirsRegex []*regexp.Regexp @@ -66,13 +64,20 @@ Add import path to a folder } skipDirsRegex = append(skipDirsRegex, userSkipDirsRegex...) - diffCount, err := porto.FindAndAddVanityImportForDir(workingDir, baseAbsDir, porto.Options{ + opts := porto.Options{ WriteResultToFile: *flagWriteOutputToFile, ListDiffFiles: *flagListDiff, - SkipFilesRegexes: skipFilesRegex, - SkipDirsRegexes: skipDirsRegex, IncludeInternal: *flagIncludeInternal, - }) + } + + if len(restrictToFilesRegex) > 0 { + opts.RestrictToFilesRegexes = restrictToFilesRegex + } else { + opts.SkipFilesRegexes = skipFilesRegex + opts.SkipDirsRegexes = skipDirsRegex + } + + diffCount, err := porto.FindAndAddVanityImportForDir(workingDir, baseAbsDir, opts) if err != nil { log.Fatal(err) } diff --git a/import.go b/import.go index 7cae7c5..385dc8c 100644 --- a/import.go +++ b/import.go @@ -7,7 +7,7 @@ import ( "go/ast" "go/parser" "go/token" - "io/ioutil" + "os" "path/filepath" "regexp" "strings" @@ -56,7 +56,7 @@ func addImportPath(absFilepath string, module string) (bool, []byte, error) { return false, nil, errGenerated } - content, err := ioutil.ReadFile(absFilepath) + content, err := os.ReadFile(absFilepath) if err != nil { return false, nil, fmt.Errorf("failed to parse the file %q: %v", absFilepath, err) } @@ -99,7 +99,7 @@ func findAndAddVanityImportForModuleDir(workingDir, absDir string, moduleName st return 0, nil } - files, err := ioutil.ReadDir(absDir) + files, err := os.ReadDir(absDir) if err != nil { return 0, fmt.Errorf("failed to read the content of %q: %v", absDir, err) } @@ -127,7 +127,18 @@ func findAndAddVanityImportForModuleDir(workingDir, absDir string, moduleName st } gc += c - } else if fileName := f.Name(); isGoFile(fileName) && !isGoTestFile(fileName) && !matchesAny(opts.SkipFilesRegexes, fileName) { + } else if fileName := f.Name(); isGoFile(fileName) && !isGoTestFile(fileName) { + shouldEvaluate := true + if len(opts.RestrictToFilesRegexes) > 0 { + shouldEvaluate = matchesAny(opts.RestrictToFilesRegexes, fileName) + } else if len(opts.SkipFilesRegexes) > 0 { + shouldEvaluate = !matchesAny(opts.SkipFilesRegexes, fileName) + } + + if !shouldEvaluate { + continue + } + absFilepath := absDir + pathSeparator + fileName hasChanged, newContent, err := addImportPath(absDir+pathSeparator+fileName, moduleName) @@ -181,7 +192,7 @@ func matchesAny(regexes []*regexp.Regexp, str string) bool { } func findAndAddVanityImportForNonModuleDir(workingDir, absDir string, opts Options) (int, error) { - files, err := ioutil.ReadDir(absDir) + files, err := os.ReadDir(absDir) if err != nil { return 0, fmt.Errorf("failed to read %q: %v", absDir, err) } @@ -231,6 +242,8 @@ type Options struct { SkipDirsRegexes []*regexp.Regexp // Include internal packages IncludeInternal bool + // Set of regex for matching files to be included + RestrictToFilesRegexes []*regexp.Regexp } // FindAndAddVanityImportForDir scans all files in a folder and based on go.mod files @@ -240,7 +253,7 @@ func FindAndAddVanityImportForDir(workingDir, absDir string, opts Options) (int, return findAndAddVanityImportForModuleDir(workingDir, absDir, moduleName, opts) } - files, err := ioutil.ReadDir(absDir) + files, err := os.ReadDir(absDir) if err != nil { return 0, fmt.Errorf("failed to read the content of %q: %v", absDir, err) } diff --git a/import_test.go b/import_test.go index 4d99c3b..d41f2aa 100644 --- a/import_test.go +++ b/import_test.go @@ -72,6 +72,52 @@ func TestFindFilesWithVanityImport(t *testing.T) { assert.Equal(t, 0, c) }) + t.Run("skip file", func(t *testing.T) { + c, err := findAndAddVanityImportForModuleDir( + cwd, + cwd+"/testdata/leftpad", + "github.com/jcchavezs/porto-integration-leftpad", + Options{ + ListDiffFiles: true, + SkipFilesRegexes: []*regexp.Regexp{regexp.MustCompile(`leftpad\.go`)}, + }, + ) + + require.NoError(t, err) + assert.Equal(t, 1, c) + }) + + t.Run("include file", func(t *testing.T) { + c, err := findAndAddVanityImportForModuleDir( + cwd, + cwd+"/testdata/leftpad", + "github.com/jcchavezs/porto-integration-leftpad", + Options{ + ListDiffFiles: true, + RestrictToFilesRegexes: []*regexp.Regexp{regexp.MustCompile(`other\.go`)}, + }, + ) + + require.NoError(t, err) + assert.Equal(t, 1, c) + }) + + t.Run("skip and include file", func(t *testing.T) { + c, err := findAndAddVanityImportForModuleDir( + cwd, + cwd+"/testdata/leftpad", + "github.com/jcchavezs/porto-integration-leftpad", + Options{ + ListDiffFiles: true, + RestrictToFilesRegexes: []*regexp.Regexp{regexp.MustCompile(`other\.go`)}, + SkipFilesRegexes: []*regexp.Regexp{regexp.MustCompile(`leftpad\.go`)}, + }, + ) + + require.NoError(t, err) + assert.Equal(t, 1, c) + }) + } func TestMatchesAny(t *testing.T) {