This is an automated email from the ASF dual-hosted git repository. kezhenxu94 pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-eyes.git
commit a62044de52cc10ae8a1c165b780a2e4265423132 Author: kezhenxu94 <[email protected]> AuthorDate: Sun Dec 20 15:58:20 2020 +0800 Reshape the header check command --- .licenserc.json | 50 -------- .licenserc.yaml | 21 +++ cmd/root.go | 177 +++----------------------- commands/header/check.go | 58 +++++++++ commands/header/header.go | 15 +++ go.mod | 16 +-- go.sum | 40 ++---- internal/logger/log.go | 38 ++++++ main.go | 11 +- pkg/header/check.go | 124 ++++++++++++++++++ pkg/header/model.go | 12 ++ {util => pkg/util}/str.go | 0 {util => pkg/util}/str_test.go | 0 test/include_test/without_license/testcase.md | 19 +++ 14 files changed, 326 insertions(+), 255 deletions(-) diff --git a/.licenserc.json b/.licenserc.json deleted file mode 100644 index a15366c..0000000 --- a/.licenserc.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "licenseStrict": [ - "Licensed to the Apache Software Foundation (ASF) under one or more", - "contributor license agreements. See the NOTICE file distributed with", - "this work for additional information regarding copyright ownership.", - "The ASF licenses this file to You under the Apache License, Version 2.0", - "(the \"License\"); you may not use this file except in compliance with", - "the License. You may obtain a copy of the License at", - "http://www.apache.org/licenses/LICENSE-2.0", - "Unless required by applicable law or agreed to in writing, software", - "distributed under the License is distributed on an \"AS IS\" BASIS,", - "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", - "See the License for the specific language governing permissions and", - "limitations under the License." - ], - "licenseLoose": [ - "Apache License, Version 2.0" - ], - "targetFiles": [ - "java", - "go", - "py", - "sh", - "graphql", - "yaml", - "yml" - ], - "exclude": { - "files": [ - ".gitignore", - "NOTICE", - "go.mod", - "go.sum", - ".DS_Store", - "LICENSE" - ], - "extensions": [ - "md", - "xml", - "json" - ], - "directories": [ - "bin", - ".github", - ".git", - ".idea", - "test" - ] - } -} \ No newline at end of file diff --git a/.licenserc.yaml b/.licenserc.yaml new file mode 100644 index 0000000..c23a250 --- /dev/null +++ b/.licenserc.yaml @@ -0,0 +1,21 @@ +mode: strict + +license: > + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +paths: + - 'test/include_test' + +paths-ignore: + - '**/*.md' diff --git a/cmd/root.go b/cmd/root.go index ef8abdf..68cc5eb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,179 +16,38 @@ limitations under the License. package cmd import ( - "bufio" - "encoding/json" - "fmt" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "io/ioutil" - "license-checker/util" - "os" - "path/filepath" - "strings" + headercommand "license-checker/commands/header" + "license-checker/internal/logger" ) var ( - // cfgFile is the path to the config file. - cfgFile string - // checkPath is the path to check license. - checkPath string - // loose is flag to enable loose mode. - loose bool - // verbose is flag to enable verbose mode. - verbose bool + verbosity string ) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "license-checker [flags]", - Long: `license-checker walks the specified path recursively and checks -if the specified files have the license header in the config file.`, - Run: func(cmd *cobra.Command, args []string) { - config, err := LoadConfig() - if err != nil { - fmt.Println(err) - } - - res, err := WalkAndCheck(checkPath, config) - if err != nil { - fmt.Println(err) + Use: "license-checker command [flags]", + Long: "license-checker walks the specified path recursively and checks if the specified files have the license header in the config file.", + SilenceUsage: true, + SilenceErrors: true, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if level, err := logrus.ParseLevel(verbosity); err != nil { + return err + } else { + logger.Log.SetLevel(level) } - printResult(res) + return nil }, } // Execute sets flags to the root command appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", ".licenserc.json", "the config file") - rootCmd.PersistentFlags().StringVarP(&checkPath, "path", "p", ".", "the path to check") - rootCmd.PersistentFlags().BoolVarP(&loose, "loose", "l", false, "loose mode") - rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode") - - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -type excludeConfig struct { - Files []string `json:"files"` - Extensions []string `json:"extensions"` - Directories []string `json:"directories"` -} - -type Config struct { - LicenseStrict []string `json:"licenseStrict"` - LicenseLoose []string `json:"licenseLoose"` - TargetFiles []string `json:"targetFiles"` - Exclude excludeConfig `json:"exclude"` -} - -type Result struct { - Success []string `json:"success"` - Failure []string `json:"failure"` -} - -// LoadConfig reads in config file. -func LoadConfig() (*Config, error) { - var config Config - bytes, err := ioutil.ReadFile(cfgFile) - if err != nil { - return nil, err - } - - err = json.Unmarshal(bytes, &config) - if err != nil { - return nil, err - } - - return &config, nil -} - -// WalkAndCheck traverses the path p and check every target file's license. -func WalkAndCheck(p string, cfg *Config) (*Result, error) { - var license []string - if loose { - license = cfg.LicenseLoose - } else { - license = cfg.LicenseStrict - } - - inExcludeDir := util.InStrSliceMapKeyFunc(cfg.Exclude.Directories) - inExcludeExt := util.InStrSliceMapKeyFunc(cfg.Exclude.Extensions) - inExcludeFiles := util.InStrSliceMapKeyFunc(cfg.Exclude.Files) - - var result Result - err := filepath.Walk(p, func(path string, fi os.FileInfo, err error) error { - if err != nil { - fmt.Println(err) // can't walk here, - return nil // but continue walking elsewhere - } - - if fi.IsDir() { - if inExcludeDir(fi.Name()) || - inExcludeDir(util.CleanPathPrefixes(path, []string{p, string(os.PathSeparator)})) { - return filepath.SkipDir - } - } else { - ext := util.GetFileExtension(fi.Name()) - if inExcludeFiles(fi.Name()) || inExcludeExt(ext) { - return nil - } - - ok, err := CheckLicense(path, license) - if err != nil { - return err - } - - if ok { - result.Success = append(result.Success, fmt.Sprintf("[Pass]: %s", path)) - } else { - result.Failure = append(result.Failure, fmt.Sprintf("[No Specified License]: %s", path)) - } - } - - return nil - }) - - return &result, err -} - -// CheckLicense checks license of single file. -func CheckLicense(filePath string, license []string) (bool, error) { - file, err := os.Open(filePath) - if err != nil { - return false, err - } - defer file.Close() - - index := 0 - scanner := bufio.NewScanner(file) - for scanner.Scan() && index < len(license) { - line := scanner.Text() - if strings.Contains(line, license[index]) { - index++ - } - } - - if index != len(license) { - return false, nil - } - return true, nil -} - -// printResult prints license check result. -func printResult(r *Result) { - if verbose { - for _, s := range r.Success { - fmt.Println(s) - } - } +func Execute() error { + rootCmd.PersistentFlags().StringVarP(&verbosity, "verbosity", "v", logrus.InfoLevel.String(), "Log level (debug, info, warn, error, fatal, panic") - for _, s := range r.Failure { - fmt.Println(s) - } + rootCmd.AddCommand(headercommand.Header) - fmt.Printf("Total check %d files, success: %d, failure: %d\n", - len(r.Success)+len(r.Failure), len(r.Success), len(r.Failure)) + return rootCmd.Execute() } diff --git a/commands/header/check.go b/commands/header/check.go new file mode 100644 index 0000000..24d316b --- /dev/null +++ b/commands/header/check.go @@ -0,0 +1,58 @@ +package header + +import ( + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + "io/ioutil" + "license-checker/internal/logger" + "license-checker/pkg/header" + "strings" +) + +var ( + // cfgFile is the config path to the config file of header command. + cfgFile string +) + +var CheckCommand = &cobra.Command{ + Use: "check", + Long: "`check` command walks the specified paths recursively and checks if the specified files have the license header in the config file.", + Aliases: []string{"c"}, + RunE: func(cmd *cobra.Command, args []string) error { + var config header.Config + if err := loadConfig(&config); err != nil { + return err + } + return header.Check(&config) + }, +} + +func init() { + CheckCommand.Flags().StringVarP(&cfgFile, "config", "c", ".licenserc.yaml", "the config file") +} + +// loadConfig reads and parses the header check configurations in config file. +func loadConfig(config *header.Config) error { + logger.Log.Infoln("Loading configuration from file:", cfgFile) + + bytes, err := ioutil.ReadFile(cfgFile) + if err != nil { + return err + } + + if err = yaml.Unmarshal(bytes, config); err != nil { + return err + } + + var lines []string + for _, line := range strings.Split(config.License, "\n") { + if len(line) > 0 { + lines = append(lines, strings.Trim(line, header.CommentChars)) + } + } + config.License = strings.Join(lines, "\n") + + logger.Log.Infoln("License header is:", config.License) + + return nil +} diff --git a/commands/header/header.go b/commands/header/header.go new file mode 100644 index 0000000..5879a40 --- /dev/null +++ b/commands/header/header.go @@ -0,0 +1,15 @@ +package header + +import ( + "github.com/spf13/cobra" +) + +var Header = &cobra.Command{ + Use: "header", + Long: "`header` command walks the specified paths recursively and checks if the specified files have the license header in the config file.", + Aliases: []string{"h"}, +} + +func init() { + Header.AddCommand(CheckCommand) +} diff --git a/go.mod b/go.mod index ebaf77e..1ac37cf 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,8 @@ module license-checker go 1.13 require ( - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/magiconair/properties v1.8.4 // indirect - github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.3.3 // indirect - github.com/pelletier/go-toml v1.8.1 // indirect - github.com/spf13/afero v1.4.1 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/bmatcuk/doublestar/v2 v2.0.4 + github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.1 - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/viper v1.7.1 - golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7 // indirect - golang.org/x/text v0.3.4 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/go.sum b/go.sum index cd8c883..fda907d 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,9 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= +github.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI= +github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -38,8 +41,6 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -102,15 +103,12 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= -github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -125,8 +123,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -134,11 +130,8 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -157,36 +150,29 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.4.1 h1:asw9sl74539yqavKaglDM5hFpdJVK0Y5Dr/JOgQ89nQ= -github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -202,7 +188,6 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -257,16 +242,12 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7 h1:s330+6z/Ko3J0o6rvOcwXe5nzs7UT9tLKHoOXYn6uE0= -golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -314,17 +295,14 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/logger/log.go b/internal/logger/log.go new file mode 100644 index 0000000..4803978 --- /dev/null +++ b/internal/logger/log.go @@ -0,0 +1,38 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logger + +import ( + "os" + + "github.com/sirupsen/logrus" +) + +var Log *logrus.Logger + +func init() { + if Log == nil { + Log = logrus.New() + } + Log.Level = logrus.DebugLevel + Log.SetOutput(os.Stdout) + Log.SetFormatter(&logrus.TextFormatter{ + DisableTimestamp: true, + DisableLevelTruncation: true, + }) +} diff --git a/main.go b/main.go index e9f9be6..3f262a7 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,15 @@ limitations under the License. */ package main -import "license-checker/cmd" +import ( + "license-checker/cmd" + "license-checker/internal/logger" + "os" +) func main() { - cmd.Execute() + if err := cmd.Execute(); err != nil { + logger.Log.Errorln(err) + os.Exit(1) + } } diff --git a/pkg/header/check.go b/pkg/header/check.go new file mode 100644 index 0000000..aef1c8e --- /dev/null +++ b/pkg/header/check.go @@ -0,0 +1,124 @@ +package header + +import ( + "bufio" + "fmt" + "github.com/bmatcuk/doublestar/v2" + "license-checker/internal/logger" + "os" + "path/filepath" + "strings" +) + +const CommentChars = "/*#- " + +// Check checks the license headers of the specified paths/globs. +func Check(config *Config) error { + var result Result + + for _, pattern := range config.Paths { + if err := checkPattern(pattern, &result, config); err != nil { + return err + } + } + + if len(result.Failure) > 0 { + return fmt.Errorf( + "The following files don't have a valid license header: \n%v", + strings.Join(result.Failure, "\n"), + ) + } + + return nil +} + +func checkPattern(pattern string, result *Result, config *Config) error { + paths, err := doublestar.Glob(pattern) + + if err != nil { + return err + } + + logger.Log.Infoln("Checking matched paths:", paths) + + for _, path := range paths { + logger.Log.Debugln("Checking path:", path) + + if err = checkPath(path, result, config); err != nil { + return err + } + } + + return nil +} + +func checkPath(path string, result *Result, config *Config) error { + pathInfo, err := os.Stat(path) + + if err != nil { + return err + } + + switch mode := pathInfo.Mode(); { + case mode.IsDir(): + if err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + if err := checkPath(p, result, config); err != nil { + return err + } + return nil + }); err != nil { + return err + } + case mode.IsRegular(): + return checkFile(path, result, config) + } + return nil +} + +func checkFile(file string, result *Result, config *Config) error { + skip := false + for _, ignorePattern := range config.PathsIgnore { + logger.Log.Debugln("Checking ignore pattern:", ignorePattern) + + if ignored, err := doublestar.Match(ignorePattern, file); ignored || err != nil { + logger.Log.Infoln("Ignoring path:", file) + skip = ignored + break + } + } + if skip { + return nil + } + + logger.Log.Debugln("Checking file:", file) + + reader, err := os.Open(file) + + if err != nil { + return nil + } + + var lines []string + + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := strings.Trim(scanner.Text(), CommentChars) + if len(line) > 0 { + lines = append(lines, line) + } + } + + if content := strings.Join(lines, " "); !strings.Contains(content, config.License) { + logger.Log.Debugln("Content is:", content) + logger.Log.Debugln("License is:", config.License) + + result.Failure = append(result.Failure, file) + } else { + result.Success = append(result.Success, file) + } + + return nil +} diff --git a/pkg/header/model.go b/pkg/header/model.go new file mode 100644 index 0000000..8401cae --- /dev/null +++ b/pkg/header/model.go @@ -0,0 +1,12 @@ +package header + +type Config struct { + License string `yaml:"license"` + Paths []string `yaml:"paths"` + PathsIgnore []string `yaml:"paths-ignore"` +} + +type Result struct { + Success []string `yaml:"success"` + Failure []string `yaml:"failure"` +} diff --git a/util/str.go b/pkg/util/str.go similarity index 100% rename from util/str.go rename to pkg/util/str.go diff --git a/util/str_test.go b/pkg/util/str_test.go similarity index 100% rename from util/str_test.go rename to pkg/util/str_test.go diff --git a/test/include_test/without_license/testcase.md b/test/include_test/without_license/testcase.md new file mode 100644 index 0000000..95d8ce5 --- /dev/null +++ b/test/include_test/without_license/testcase.md @@ -0,0 +1,19 @@ +<!-- +Esse enim dolore adipisicing in cillum eiusmod excepteur quis nisi sit dolor anim anim id id nostrud nostrud tempor. + Elit sit enim cillum adipisicing non magna aute nostrud ullamco dolor dolore consequat ut ea occaecat veniam incididunt + occaecat consectetur eiusmod sint eiusmod aute eu duis fugiat dolore in laboris enim eiusmod aliquip nisi aliqua irure + nulla proident ut ut aliqua et in ad deserunt nisi anim non dolore ut commodo excepteur ut eiusmod ex exercitation + in sunt cillum ullamco dolor magna ex proident elit in mollit incididunt excepteur dolor pariatur consectetur + exercitation qui ut cupidatat dolor do esse tempor commodo magna ad in duis voluptate est nisi in pariatur + duis mollit eiusmod est magna in velit aute anim ullamco nostrud adipisicing cupidatat non fugiat proident + commodo minim sint minim pariatur nostrud sit mollit cillum dolor minim quis nisi id officia veniam + mollit tempor exercitation dolore duis cillum sunt esse nostrud deserunt amet ad quis reprehenderit + do in proident est incididunt voluptate et ullamco nisi dolore commodo mollit elit sint aute in occaecat + excepteur do pariatur laborum occaecat ad et nisi ut est in consequat commodo consectetur consequat ullamco m + ollit aute dolor velit do deserunt eu velit reprehenderit incididunt commodo duis anim dolor velit nostrud eu + irure dolor mollit labore sunt consectetur ullamco sed consectetur sed sint consectetur eu fugiat amet nostrud + elit reprehenderit sunt ullamco fugiat eiusmod elit est est in duis quis occaecat proident aliquip adipisicing + eu sint pariatur ullamco adipisicing cupidatat sed ut pariatur ut in amet deserunt laboris consectetur in + consectetur mollit voluptate magna ut ullamco pariatur proident esse commodo consectetur minim in do eu + consequat ea eiusmod proident incididunt ut qui sunt consequat officia amet amet amet dolore fugiat ex. +-->
