This is an automated email from the ASF dual-hosted git repository. kezhenxu94 pushed a commit to branch deps in repository https://gitbox.apache.org/repos/asf/skywalking-eyes.git
commit 813228043565b242bebda8ecc6ec8a35b54f1a0d Author: kezhenxu94 <[email protected]> AuthorDate: Sat Dec 26 22:57:45 2020 +0800 Resolve dependencies' license files --- .github/workflows/license-eye-check.yaml | 2 - .licenserc.yaml | 4 + license-eye/commands/{header/header.go => deps.go} | 23 ++-- .../config/config.go => commands/deps_resolve.go} | 42 +++--- license-eye/commands/{header => }/header.go | 8 +- .../commands/{header/check.go => header_check.go} | 17 +-- .../commands/{header/fix.go => header_fix.go} | 15 +-- license-eye/commands/root.go | 25 ++-- license-eye/go.mod | 2 + license-eye/go.sum | 11 ++ license-eye/pkg/config/config.go | 6 + license-eye/pkg/{config => deps}/config.go | 29 ++-- license-eye/pkg/deps/golang.go | 149 +++++++++++++++++++++ .../header/header.go => pkg/deps/resolve.go} | 40 +++--- .../pkg/{config/config.go => deps/result.go} | 46 +++---- license-eye/pkg/header/check.go | 9 +- license-eye/pkg/header/check_test.go | 9 +- license-eye/pkg/header/fix.go | 7 +- license-eye/pkg/{ => header}/result.go | 2 +- license-eye/pkg/review/header.go | 11 +- 20 files changed, 301 insertions(+), 156 deletions(-) diff --git a/.github/workflows/license-eye-check.yaml b/.github/workflows/license-eye-check.yaml index 869db6d..2bcfd23 100644 --- a/.github/workflows/license-eye-check.yaml +++ b/.github/workflows/license-eye-check.yaml @@ -44,8 +44,6 @@ jobs: - name: License Check run: make license - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Test run: make test diff --git a/.licenserc.yaml b/.licenserc.yaml index 5b6a539..0e5aab6 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -74,3 +74,7 @@ header: # `header` section is configurations for source codes license header. - '**/assets/assets.gen.go' comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`. + +dependency: + files: + - license-eye/go.mod diff --git a/license-eye/commands/header/header.go b/license-eye/commands/deps.go similarity index 61% copy from license-eye/commands/header/header.go copy to license-eye/commands/deps.go index 8786217..87aadfd 100644 --- a/license-eye/commands/header/header.go +++ b/license-eye/commands/deps.go @@ -1,3 +1,4 @@ +// // 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 @@ -14,27 +15,19 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -// -package header +package commands import ( "github.com/spf13/cobra" ) -var ( - // cfgFile is the config path to the config file of header command. - cfgFile string -) - -var Header = &cobra.Command{ - Use: "header", - Aliases: []string{"h"}, - Short: "License header related commands; e.g. check, fix, etc.", - Long: "`header` command walks the specified paths recursively and checks if the specified files have the license header in the config file.", +var Deps = &cobra.Command{ + Use: "deps", + Aliases: []string{"d"}, + Short: "Dependencies related commands; e.g. check, etc.", + Long: "deps command checks all dependencies of a module and their transitive dependencies.", } func init() { - Header.PersistentFlags().StringVarP(&cfgFile, "config", "c", ".licenserc.yaml", "the config file") - Header.AddCommand(CheckCommand) - Header.AddCommand(FixCommand) + Deps.AddCommand(ResolveCommand) } diff --git a/license-eye/pkg/config/config.go b/license-eye/commands/deps_resolve.go similarity index 57% copy from license-eye/pkg/config/config.go copy to license-eye/commands/deps_resolve.go index 3b7c417..96ca6ca 100644 --- a/license-eye/pkg/config/config.go +++ b/license-eye/commands/deps_resolve.go @@ -1,3 +1,4 @@ +// // 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 @@ -14,35 +15,32 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -// -package config +package commands import ( - "io/ioutil" - "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg/header" - - "gopkg.in/yaml.v3" + "github.com/apache/skywalking-eyes/license-eye/pkg/deps" + "github.com/spf13/cobra" ) -type Config struct { - Header header.ConfigHeader `yaml:"header"` -} +var ResolveCommand = &cobra.Command{ + Use: "resolve", + Aliases: []string{"r"}, + Long: "resolves all dependencies of a go.mod file and their transitive dependencies", + RunE: func(cmd *cobra.Command, args []string) error { + report := deps.Report{} -// Parse reads and parses the header check configurations in config file. -func (config *Config) Parse(file string) error { - logger.Log.Infoln("Loading configuration from file:", file) + if err := deps.Resolve(&Config.Deps, &report); err != nil { + return err + } - if bytes, err := ioutil.ReadFile(file); err != nil { - return err - } else if err := yaml.Unmarshal(bytes, config); err != nil { - return err - } + for _, result := range report.Resolved { + logger.Log.Debugln("Resolved:", result.Dependency) + logger.Log.Debugln("License:", result.LicenseFilePath) + } - if err := config.Header.Finalize(); err != nil { - return err - } + logger.Log.Debugln("Skipped:", len(report.Skipped)) - return nil + return nil + }, } diff --git a/license-eye/commands/header/header.go b/license-eye/commands/header.go similarity index 85% copy from license-eye/commands/header/header.go copy to license-eye/commands/header.go index 8786217..aec10de 100644 --- a/license-eye/commands/header/header.go +++ b/license-eye/commands/header.go @@ -15,17 +15,12 @@ // specific language governing permissions and limitations // under the License. // -package header +package commands import ( "github.com/spf13/cobra" ) -var ( - // cfgFile is the config path to the config file of header command. - cfgFile string -) - var Header = &cobra.Command{ Use: "header", Aliases: []string{"h"}, @@ -34,7 +29,6 @@ var Header = &cobra.Command{ } func init() { - Header.PersistentFlags().StringVarP(&cfgFile, "config", "c", ".licenserc.yaml", "the config file") Header.AddCommand(CheckCommand) Header.AddCommand(FixCommand) } diff --git a/license-eye/commands/header/check.go b/license-eye/commands/header_check.go similarity index 80% rename from license-eye/commands/header/check.go rename to license-eye/commands/header_check.go index 6f9e76f..e721eb1 100644 --- a/license-eye/commands/header/check.go +++ b/license-eye/commands/header_check.go @@ -15,12 +15,10 @@ // specific language governing permissions and limitations // under the License. // -package header +package commands import ( "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg" - "github.com/apache/skywalking-eyes/license-eye/pkg/config" "github.com/apache/skywalking-eyes/license-eye/pkg/header" "github.com/apache/skywalking-eyes/license-eye/pkg/review" @@ -32,26 +30,21 @@ var CheckCommand = &cobra.Command{ Aliases: []string{"c"}, Long: "check command walks the specified paths recursively and checks if the specified files have the license header in the config file.", RunE: func(cmd *cobra.Command, args []string) error { - var config config.Config - var result pkg.Result - - if err := config.Parse(cfgFile); err != nil { - return err - } + var result header.Result if len(args) > 0 { logger.Log.Debugln("Overriding paths with command line args.") - config.Header.Paths = args + Config.Header.Paths = args } - if err := header.Check(&config.Header, &result); err != nil { + if err := header.Check(&Config.Header, &result); err != nil { return err } logger.Log.Infoln(result.String()) if result.HasFailure() { - if err := review.Header(&result, &config); err != nil { + if err := review.Header(&result, &Config); err != nil { logger.Log.Warnln("Failed to create review comments", err) } return result.Error() diff --git a/license-eye/commands/header/fix.go b/license-eye/commands/header_fix.go similarity index 80% rename from license-eye/commands/header/fix.go rename to license-eye/commands/header_fix.go index 4fbba48..4f1e50f 100644 --- a/license-eye/commands/header/fix.go +++ b/license-eye/commands/header_fix.go @@ -16,15 +16,13 @@ // specific language governing permissions and limitations // under the License. // -package header +package commands import ( "fmt" "strings" "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg" - "github.com/apache/skywalking-eyes/license-eye/pkg/config" "github.com/apache/skywalking-eyes/license-eye/pkg/header" "github.com/spf13/cobra" ) @@ -34,20 +32,15 @@ var FixCommand = &cobra.Command{ Aliases: []string{"f"}, Long: "fix command walks the specified paths recursively and fix the license header if the specified files don't have the license header.", RunE: func(cmd *cobra.Command, args []string) error { - var config config.Config - var result pkg.Result + var result header.Result - if err := config.Parse(cfgFile); err != nil { - return err - } - - if err := header.Check(&config.Header, &result); err != nil { + if err := header.Check(&Config.Header, &result); err != nil { return err } var errors []string for _, file := range result.Failure { - if err := header.Fix(file, &config.Header, &result); err != nil { + if err := header.Fix(file, &Config.Header, &result); err != nil { errors = append(errors, err.Error()) } } diff --git a/license-eye/commands/root.go b/license-eye/commands/root.go index ab16bf8..fbe6b01 100644 --- a/license-eye/commands/root.go +++ b/license-eye/commands/root.go @@ -18,19 +18,21 @@ package commands import ( - headercommand "github.com/apache/skywalking-eyes/license-eye/commands/header" "github.com/apache/skywalking-eyes/license-eye/internal/logger" + "github.com/apache/skywalking-eyes/license-eye/pkg/config" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( - verbosity string + verbosity string + configFile string + Config config.Config ) -// Root represents the base command when called without any subcommands -var Root = &cobra.Command{ +// root represents the base command when called without any subcommands +var root = &cobra.Command{ Use: "license-eye command [flags]", Long: "A full-featured license guard to check and fix license headers and dependencies' licenses", SilenceUsage: true, @@ -41,17 +43,24 @@ var Root = &cobra.Command{ return err } logger.Log.SetLevel(level) + + if err := Config.Parse(configFile); err != nil { + return err + } + return nil }, Version: version, } // Execute sets flags to the root command appropriately. -// This is called by main.main(). It only needs to happen once to the Root. +// This is called by main.main(). It only needs to happen once to the root. func Execute() error { - Root.PersistentFlags().StringVarP(&verbosity, "verbosity", "v", logrus.InfoLevel.String(), "log level (debug, info, warn, error, fatal, panic") + root.PersistentFlags().StringVarP(&verbosity, "verbosity", "v", logrus.InfoLevel.String(), "log level (debug, info, warn, error, fatal, panic") + root.PersistentFlags().StringVarP(&configFile, "config", "c", ".licenserc.yaml", "the config file") - Root.AddCommand(headercommand.Header) + root.AddCommand(Header) + root.AddCommand(Deps) - return Root.Execute() + return root.Execute() } diff --git a/license-eye/go.mod b/license-eye/go.mod index 096d2fb..c66062d 100644 --- a/license-eye/go.mod +++ b/license-eye/go.mod @@ -7,6 +7,8 @@ require ( github.com/google/go-github/v33 v33.0.0 github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.1 + golang.org/x/mod v0.4.0 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/license-eye/go.sum b/license-eye/go.sum index b1a492f..3143ee6 100644 --- a/license-eye/go.sum +++ b/license-eye/go.sum @@ -16,6 +16,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/skywalking-eyes v0.0.0-20201226021955-d798d4f56aa5 h1:cMn7kz937Lz92NBpMXbawQlizM4qe3LYMil+DWQxznE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -199,6 +200,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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= @@ -216,7 +219,10 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -281,7 +287,12 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/license-eye/pkg/config/config.go b/license-eye/pkg/config/config.go index 3b7c417..6db18be 100644 --- a/license-eye/pkg/config/config.go +++ b/license-eye/pkg/config/config.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "github.com/apache/skywalking-eyes/license-eye/internal/logger" + "github.com/apache/skywalking-eyes/license-eye/pkg/deps" "github.com/apache/skywalking-eyes/license-eye/pkg/header" "gopkg.in/yaml.v3" @@ -28,6 +29,7 @@ import ( type Config struct { Header header.ConfigHeader `yaml:"header"` + Deps deps.ConfigDeps `yaml:"dependency"` } // Parse reads and parses the header check configurations in config file. @@ -44,5 +46,9 @@ func (config *Config) Parse(file string) error { return err } + if err := config.Deps.Finalize(file); err != nil { + return err + } + return nil } diff --git a/license-eye/pkg/config/config.go b/license-eye/pkg/deps/config.go similarity index 58% copy from license-eye/pkg/config/config.go copy to license-eye/pkg/deps/config.go index 3b7c417..b682aa4 100644 --- a/license-eye/pkg/config/config.go +++ b/license-eye/pkg/deps/config.go @@ -1,3 +1,4 @@ +// // 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 @@ -14,34 +15,24 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -// -package config +package deps import ( - "io/ioutil" - - "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg/header" - - "gopkg.in/yaml.v3" + "path/filepath" ) -type Config struct { - Header header.ConfigHeader `yaml:"header"` +type ConfigDeps struct { + Files []string `yaml:"files"` } -// Parse reads and parses the header check configurations in config file. -func (config *Config) Parse(file string) error { - logger.Log.Infoln("Loading configuration from file:", file) - - if bytes, err := ioutil.ReadFile(file); err != nil { - return err - } else if err := yaml.Unmarshal(bytes, config); err != nil { +func (config *ConfigDeps) Finalize(configFile string) error { + configFileAbsPath, err := filepath.Abs(configFile) + if err != nil { return err } - if err := config.Header.Finalize(); err != nil { - return err + for i, file := range config.Files { + config.Files[i] = filepath.Join(filepath.Dir(configFileAbsPath), file) } return nil diff --git a/license-eye/pkg/deps/golang.go b/license-eye/pkg/deps/golang.go new file mode 100644 index 0000000..cea090f --- /dev/null +++ b/license-eye/pkg/deps/golang.go @@ -0,0 +1,149 @@ +// +// 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 deps + +import ( + "go/build" + "io/ioutil" + "path/filepath" + "regexp" + + "github.com/apache/skywalking-eyes/license-eye/internal/logger" + "golang.org/x/mod/modfile" + "golang.org/x/tools/go/packages" +) + +type GoModeResolver struct { + Resolver +} + +func (resolver *GoModeResolver) CanResolve(file string) bool { + base := filepath.Base(file) + logger.Log.Debugln("Base name:", base) + return base == "go.mod" +} + +// Resolve resolves licenses of all dependencies declared in the go.mod file. +func (resolver *GoModeResolver) Resolve(goModFile string, report *Report) error { + content, err := ioutil.ReadFile(goModFile) + if err != nil { + return err + } + + file, err := modfile.Parse(goModFile, content, nil) + if err != nil { + return err + } + + logger.Log.Debugln("Resolving module:", file.Module.Mod) + + requiredPkgNames := make([]string, len(file.Require)) + for i, require := range file.Require { + requiredPkgNames[i] = require.Mod.Path + } + + logger.Log.Debugln("Required packages:", requiredPkgNames) + + if err := resolver.ResolvePackages(requiredPkgNames, report); err != nil { + return err + } + + return nil +} + +// ResolvePackages resolves the licenses of the given packages. +func (resolver *GoModeResolver) ResolvePackages(pkgNames []string, report *Report) error { + requiredPkgs, err := packages.Load(&packages.Config{ + Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps, + }, pkgNames...) + + if err != nil { + return err + } + + packages.Visit(requiredPkgs, func(p *packages.Package) bool { + err := resolver.ResolvePackageLicense(p, report) + if err != nil { + logger.Log.Warnln("Failed to resolve the license of dependency:", p.Name, err) + } + return true + }, nil) + + return nil +} + +var possibleLicenseFileName = regexp.MustCompile(`(?i)^LICENSE|LICENCE(\.txt)?$`) + +func (resolver *GoModeResolver) ResolvePackageLicense(p *packages.Package, report *Report) error { + var filesInPkg []string + if len(p.GoFiles) > 0 { + filesInPkg = p.GoFiles + } else if len(p.CompiledGoFiles) > 0 { + filesInPkg = p.CompiledGoFiles + } else if len(p.OtherFiles) > 0 { + filesInPkg = p.OtherFiles + } + + if len(filesInPkg) == 0 { + return nil + } + + absPath, err := filepath.Abs(filesInPkg[0]) + if err != nil { + return err + } + dir := filepath.Dir(absPath) + + for { + files, err := ioutil.ReadDir(dir) + if err != nil { + return err + } + for _, info := range files { + if !possibleLicenseFileName.MatchString(info.Name()) { + continue + } + licenseFilePath := filepath.Join(dir, info.Name()) + content, err := ioutil.ReadFile(licenseFilePath) + if err != nil { + return err + } + report.Resolve(&Result{ + Dependency: p.PkgPath, + LicenseContent: string(content), + LicenseFilePath: licenseFilePath, + LicenseSpdxID: "", // TODO: identify the SPDX ID + }) + return nil + } + if resolver.shouldStopAt(dir) { + break + } + dir = filepath.Dir(dir) + } + return nil +} + +func (resolver *GoModeResolver) shouldStopAt(dir string) bool { + for _, srcDir := range build.Default.SrcDirs() { + if srcDir == dir { + return true + } + } + return false +} diff --git a/license-eye/commands/header/header.go b/license-eye/pkg/deps/resolve.go similarity index 58% rename from license-eye/commands/header/header.go rename to license-eye/pkg/deps/resolve.go index 8786217..86fbe96 100644 --- a/license-eye/commands/header/header.go +++ b/license-eye/pkg/deps/resolve.go @@ -1,3 +1,4 @@ +// // 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 @@ -14,27 +15,34 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -// -package header +package deps import ( - "github.com/spf13/cobra" + "fmt" ) -var ( - // cfgFile is the config path to the config file of header command. - cfgFile string -) +type Resolver interface { + CanResolve(string) bool + Resolve(string, *Report) error +} -var Header = &cobra.Command{ - Use: "header", - Aliases: []string{"h"}, - Short: "License header related commands; e.g. check, fix, etc.", - Long: "`header` command walks the specified paths recursively and checks if the specified files have the license header in the config file.", +var Resolvers = []Resolver{ + new(GoModeResolver), } -func init() { - Header.PersistentFlags().StringVarP(&cfgFile, "config", "c", ".licenserc.yaml", "the config file") - Header.AddCommand(CheckCommand) - Header.AddCommand(FixCommand) +func Resolve(config *ConfigDeps, report *Report) error { + for _, file := range config.Files { + for _, resolver := range Resolvers { + if !resolver.CanResolve(file) { + continue + } + if err := resolver.Resolve(file, report); err != nil { + return err + } + return nil + } + return fmt.Errorf("unable to find a resolver to resolve dependency declaration file: %v", file) + } + + return nil } diff --git a/license-eye/pkg/config/config.go b/license-eye/pkg/deps/result.go similarity index 54% copy from license-eye/pkg/config/config.go copy to license-eye/pkg/deps/result.go index 3b7c417..bfea534 100644 --- a/license-eye/pkg/config/config.go +++ b/license-eye/pkg/deps/result.go @@ -1,3 +1,4 @@ +// // 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 @@ -14,35 +15,34 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -// -package config - -import ( - "io/ioutil" +package deps - "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg/header" +type SpdxID string - "gopkg.in/yaml.v3" +const ( + Unknown string = "Unknown" ) -type Config struct { - Header header.ConfigHeader `yaml:"header"` +// Result is a single item that represents a resolved dependency license. +type Result struct { + Dependency string + LicenseFilePath string + LicenseContent string + LicenseSpdxID string } -// Parse reads and parses the header check configurations in config file. -func (config *Config) Parse(file string) error { - logger.Log.Infoln("Loading configuration from file:", file) - - if bytes, err := ioutil.ReadFile(file); err != nil { - return err - } else if err := yaml.Unmarshal(bytes, config); err != nil { - return err - } +// Report is a collection of resolved Result. +type Report struct { + Resolved []*Result + Skipped []*Result +} - if err := config.Header.Finalize(); err != nil { - return err - } +// Resolve marks the dependency's license is resolved. +func (report *Report) Resolve(result *Result) { + report.Resolved = append(report.Resolved, result) +} - return nil +// Skip marks the dependency's license is skipped for some reasons. +func (report *Report) Skip(result *Result) { + report.Skipped = append(report.Skipped, result) } diff --git a/license-eye/pkg/header/check.go b/license-eye/pkg/header/check.go index ff59b34..0bdccb5 100644 --- a/license-eye/pkg/header/check.go +++ b/license-eye/pkg/header/check.go @@ -26,7 +26,6 @@ import ( "strings" "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg" lcs "github.com/apache/skywalking-eyes/license-eye/pkg/license" "github.com/bmatcuk/doublestar/v2" @@ -39,7 +38,7 @@ var ( ) // Check checks the license headers of the specified paths/globs. -func Check(config *ConfigHeader, result *pkg.Result) error { +func Check(config *ConfigHeader, result *Result) error { for _, pattern := range config.Paths { if err := checkPattern(pattern, result, config); err != nil { return err @@ -51,7 +50,7 @@ func Check(config *ConfigHeader, result *pkg.Result) error { var seen = make(map[string]bool) -func checkPattern(pattern string, result *pkg.Result, config *ConfigHeader) error { +func checkPattern(pattern string, result *Result, config *ConfigHeader) error { paths, err := doublestar.Glob(pattern) if err != nil { @@ -72,7 +71,7 @@ func checkPattern(pattern string, result *pkg.Result, config *ConfigHeader) erro return nil } -func checkPath(path string, result *pkg.Result, config *ConfigHeader) error { +func checkPath(path string, result *Result, config *ConfigHeader) error { defer func() { seen[path] = true }() if yes, err := config.ShouldIgnore(path); yes || seen[path] || err != nil { @@ -105,7 +104,7 @@ func checkPath(path string, result *pkg.Result, config *ConfigHeader) error { } // CheckFile checks whether or not the file contains the configured license header. -func CheckFile(file string, config *ConfigHeader, result *pkg.Result) error { +func CheckFile(file string, config *ConfigHeader, result *Result) error { if yes, err := config.ShouldIgnore(file); yes || err != nil { if !seen[file] { result.Ignore(file) diff --git a/license-eye/pkg/header/check_test.go b/license-eye/pkg/header/check_test.go index cbd898b..b5e8148 100644 --- a/license-eye/pkg/header/check_test.go +++ b/license-eye/pkg/header/check_test.go @@ -24,7 +24,6 @@ import ( "strings" "testing" - "github.com/apache/skywalking-eyes/license-eye/pkg" "gopkg.in/yaml.v3" ) @@ -49,7 +48,7 @@ func TestCheckFile(t *testing.T) { type args struct { name string file string - result *pkg.Result + result *Result wantErr bool hasFailure bool } @@ -65,7 +64,7 @@ func TestCheckFile(t *testing.T) { cases = append(cases, args{ name: file, file: file, - result: &pkg.Result{}, + result: &Result{}, wantErr: false, hasFailure: false, }) @@ -100,7 +99,7 @@ func TestCheckFileFailure(t *testing.T) { type args struct { name string file string - result *pkg.Result + result *Result wantErr bool hasFailure bool } @@ -116,7 +115,7 @@ func TestCheckFileFailure(t *testing.T) { cases = append(cases, args{ name: file, file: file, - result: &pkg.Result{}, + result: &Result{}, wantErr: false, hasFailure: true, }) diff --git a/license-eye/pkg/header/fix.go b/license-eye/pkg/header/fix.go index fb8413c..7e6d776 100644 --- a/license-eye/pkg/header/fix.go +++ b/license-eye/pkg/header/fix.go @@ -26,13 +26,12 @@ import ( "strings" "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg" "github.com/apache/skywalking-eyes/license-eye/pkg/comments" ) // Fix adds the configured license header to the given file. -func Fix(file string, config *ConfigHeader, result *pkg.Result) error { - var r pkg.Result +func Fix(file string, config *ConfigHeader, result *Result) error { + var r Result if err := CheckFile(file, config, &r); err != nil || !r.HasFailure() { logger.Log.Warnln("Try to fix a valid file, do nothing:", file) return err @@ -51,7 +50,7 @@ func Fix(file string, config *ConfigHeader, result *pkg.Result) error { return nil } -func InsertComment(file string, style *comments.CommentStyle, config *ConfigHeader, result *pkg.Result) error { +func InsertComment(file string, style *comments.CommentStyle, config *ConfigHeader, result *Result) error { stat, err := os.Stat(file) if err != nil { return err diff --git a/license-eye/pkg/result.go b/license-eye/pkg/header/result.go similarity index 99% rename from license-eye/pkg/result.go rename to license-eye/pkg/header/result.go index 9333671..068fe75 100644 --- a/license-eye/pkg/result.go +++ b/license-eye/pkg/header/result.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. // -package pkg +package header import ( "fmt" diff --git a/license-eye/pkg/review/header.go b/license-eye/pkg/review/header.go index 4ad84e7..7c5b790 100644 --- a/license-eye/pkg/review/header.go +++ b/license-eye/pkg/review/header.go @@ -30,7 +30,6 @@ import ( "strings" "github.com/apache/skywalking-eyes/license-eye/internal/logger" - "github.com/apache/skywalking-eyes/license-eye/pkg" comments2 "github.com/apache/skywalking-eyes/license-eye/pkg/comments" config2 "github.com/apache/skywalking-eyes/license-eye/pkg/config" header2 "github.com/apache/skywalking-eyes/license-eye/pkg/header" @@ -59,7 +58,7 @@ var ( ) func init() { - if os.Getenv("GITHUB_TOKEN") == "" { + if os.Getenv("INPUT_GITHUB_TOKEN") == "" { logger.Log.Warnln("GITHUB_TOKEN is not set, license-eye won't comment on the pull request") return } @@ -110,7 +109,7 @@ func init() { } // Header reviews the license header, including suggestions on the pull request and an overview of the checks. -func Header(result *pkg.Result, config *config2.Config) error { +func Header(result *header2.Result, config *config2.Config) error { if !result.HasFailure() || !IsPR() || gh == nil || config.Header.Comment == header2.Never { return nil } @@ -171,7 +170,7 @@ func Header(result *pkg.Result, config *config2.Config) error { return nil } -func tryReview(result *pkg.Result, config *config2.Config, comments []*github.DraftReviewComment) error { +func tryReview(result *header2.Result, config *config2.Config, comments []*github.DraftReviewComment) error { tryBestEffortToComment := func() error { if err := doReview(result, comments); err != nil { logger.Log.Warnln("Failed to create review comment, fallback to a plain comment:", err) @@ -193,7 +192,7 @@ func tryReview(result *pkg.Result, config *config2.Config, comments []*github.Dr return nil } -func doReview(result *pkg.Result, comments []*github.DraftReviewComment) error { +func doReview(result *header2.Result, comments []*github.DraftReviewComment) error { logger.Log.Debugln("Comments:", comments) c := Markdown(result) @@ -257,7 +256,7 @@ func IsPR() bool { } // TODO add fixing guide -func Markdown(result *pkg.Result) string { +func Markdown(result *header2.Result) string { return fmt.Sprintf(` <!-- %s --> [license-eye](https://github.com/apache/skywalking-eyes/tree/main/license-eye) has totally checked %d files.
