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
The following commit(s) were added to refs/heads/main by this push:
new 78aa037 Provide `--summary` flag to generate the license summary file
(#103)
78aa037 is described below
commit 78aa037689d813591dd6cc2e9e1b6cf562c6197a
Author: mrproliu <[email protected]>
AuthorDate: Sat May 7 13:48:09 2022 +0800
Provide `--summary` flag to generate the license summary file (#103)
---
README.md | 310 +++++++++++++++++++++++++++++++++++++++++++++-
commands/deps_resolve.go | 55 ++++++--
pkg/deps/config.go | 9 +-
pkg/deps/golang.go | 31 +++--
pkg/deps/jar.go | 20 +--
pkg/deps/jar_test.go | 2 +-
pkg/deps/maven.go | 22 ++--
pkg/deps/maven_test.go | 2 +-
pkg/deps/npm.go | 27 ++--
pkg/deps/npm_test.go | 2 +-
pkg/deps/resolve.go | 4 +-
pkg/deps/summary.go | 106 ++++++++++++++++
pkg/license/identifier.go | 13 +-
13 files changed, 553 insertions(+), 50 deletions(-)
diff --git a/README.md b/README.md
index f2bb97c..44da58d 100644
--- a/README.md
+++ b/README.md
@@ -157,11 +157,15 @@ INFO Totally checked 20 files, valid: 10, invalid: 10,
ignored: 0, fixed: 10
This command serves as assistance for human beings to audit the dependencies
license, it's exit code is always 0.
-You can also use the `--output` or `-o` to save the dependencies' `LICENSE`
files to a specified directory so that
-you can put them in distribution package if needed.
+We also support two flags:
+
+|Flag name|Short name|Description|
+|---------|----------|-----------|
+|`--output`|`-o`|Save the dependencies' `LICENSE` files to a specified
directory so that you can put them in distribution package if needed.|
+|`--summary`|`-s`|Based on the template, aggregate all dependency information
and generate a `LICENSE` file.|
```bash
-license-eye -c test/testdata/.licenserc_for_test_check.yaml dep resolve -o
./dependencies/licenses
+license-eye -c test/testdata/.licenserc_for_test_check.yaml dep resolve -o
./dependencies/licenses -s LICENSE.tpl
```
<details>
@@ -359,6 +363,298 @@ gopkg.in/check.v1
</details>
+##### Summary Template
+
+The summary is a template to generate the summary of dependencies' licenses
based on the [Golang Template](https://pkg.go.dev/text/template). It includes
these variables:
+
+|Name|Type|Example|Description|
+|----|----|-------|-----------|
+|LicenseContent|string|`{{.LicenseContent}}`|The project license content, it's
the license of `header.license.spdx-id` (if set), otherwise it's the
`header.license.content`. |
+|Groups|list structure|`{{ range .Groups }}`|The dependency groups, all
licenses are grouped by the same license [SPDX ID](https://spdx.org/licenses/).
|
+|Groups.LicenseID|string|`{{.LicenseID}}`|The [SPDX
ID](https://spdx.org/licenses/) of dependency. |
+|Groups.Deps|list structure|`{{ range .Deps }}`|All dependencies with the same
[SPDX ID](https://spdx.org/licenses/). |
+|Groups.Deps.Name|string|`{{.Name}}`|The name of the dependency. |
+|Groups.Deps.Version|string|`{{.Version}}`|The version of the dependency. |
+|Groups.Deps.LicenseID|string|`{{.LicenseID}}`|The [SPDX
ID](https://spdx.org/licenses/) of the dependency license. |
+
+<details>
+<summary>Summary Template Generate</summary>
+
+Summary template content:
+```
+{{.LicenseContent }}
+{{ range .Groups }}
+========================================================================
+{{.LicenseID}} licenses
+========================================================================
+{{range .Deps}}
+ {{.Name}} {{.Version}} {{.LicenseID}}
+{{- end }}
+{{ end }}
+```
+
+Generate LICENSE file content:
+```
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+
+========================================================================
+MIT licenses
+========================================================================
+
+ github.com/BurntSushi/toml v0.3.1 MIT
+ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf MIT
+ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e MIT
+ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da MIT
+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 MIT
+ github.com/beorn7/perks v1.0.0 MIT
+ github.com/bgentry/speakeasy v0.1.0 MIT
+ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c MIT
+ github.com/bmatcuk/doublestar/v2 v2.0.4 MIT
+
+========================================================================
+ISC licenses
+========================================================================
+
+ github.com/davecgh/go-spew v1.1.1 ISC
+
+========================================================================
+BSD-2-Clause licenses
+========================================================================
+
+ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1
BSD-2-Clause
+ github.com/gorilla/websocket v1.4.2 BSD-2-Clause
+ github.com/pkg/errors v0.8.1 BSD-2-Clause
+ github.com/russross/blackfriday/v2 v2.0.1 BSD-2-Clause
+
+========================================================================
+MPL-2.0-no-copyleft-exception licenses
+========================================================================
+
+ github.com/hashicorp/consul/api v1.1.0 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/consul/sdk v0.1.1 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-cleanhttp v0.5.1 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-immutable-radix v1.0.0
MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-multierror v1.0.0 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-rootcerts v1.0.0 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-sockaddr v1.0.0 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/go-uuid v1.0.1 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/golang-lru v0.5.1 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/logutils v1.0.0 MPL-2.0-no-copyleft-exception
+ github.com/hashicorp/memberlist v0.1.3 MPL-2.0-no-copyleft-exception
+
+========================================================================
+MPL-2.0 licenses
+========================================================================
+
+ github.com/hashicorp/errwrap v1.0.0 MPL-2.0
+ github.com/hashicorp/hcl v1.0.0 MPL-2.0
+ github.com/hashicorp/serf v0.8.2 MPL-2.0
+ github.com/mitchellh/cli v1.0.0 MPL-2.0
+ github.com/mitchellh/gox v0.4.0 MPL-2.0
+
+========================================================================
+MIT and Apache licenses
+========================================================================
+
+ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 MIT and Apache
+
+========================================================================
+Apache-2.0 licenses
+========================================================================
+
+ cloud.google.com/go v0.46.3 Apache-2.0
+ cloud.google.com/go/bigquery v1.0.1 Apache-2.0
+ cloud.google.com/go/datastore v1.0.0 Apache-2.0
+ cloud.google.com/go/firestore v1.1.0 Apache-2.0
+ cloud.google.com/go/pubsub v1.0.1 Apache-2.0
+ cloud.google.com/go/storage v1.0.0 Apache-2.0
+
+========================================================================
+BSD-3-Clause licenses
+========================================================================
+
+ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9
BSD-3-Clause
+ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 BSD-3-Clause
+ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
BSD-3-Clause
+ github.com/fsnotify/fsnotify v1.4.7 BSD-3-Clause
+```
+
+</details>
+
#### Check Dependencies' licenses
This command can be used to perform automatic license compatibility check,
when there is incompatible licenses found,
@@ -468,6 +764,10 @@ header: # <1>
dependency: # <15>
files: # <16>
- go.mod
+ license: # <17>
+ - name: dependency-name # <18>
+ version: dependency-version # <19>
+ license: Apache-2.0 # <20>
```
1. The `header` section is configurations for source codes license header.
@@ -486,6 +786,10 @@ dependency: # <15>
14. The `comment_style_id` set the license header comment style, it's the `id`
at the `styles.yaml`.
15. The `dependency` section is configurations for resolving dependencies'
licenses.
16. The `files` are the files that declare the dependencies of a project,
typically, `go.mod` in Go project, `pom.xml` in maven project, and
`package.json` in NodeJS project. If it's a relative path, it's relative to the
`.licenserc.yaml`.
+17. Declare the licenses which cannot be identified by this tool.
+18. The `name` of the dependency, The name is different for different
projects, `PackagePath` in Go project, `GroupID:ArtifactID` in maven project,
`PackageName` in NodeJS project.
+19. The `version` of the dependency, it's locked, preventing license changed
between different versions.
+20. The [SPDX ID](https://spdx.org/licenses/) of the dependency license.
**NOTE**: When the `SPDX-ID` is Apache-2.0 and the owner is Apache Software
foundation, the content would be [a dedicated
license](https://www.apache.org/legal/src-headers.html#headers) specified by
the ASF, otherwise, the license would be [the standard
one](https://www.apache.org/foundation/license-faq.html#Apply-My-Software).
diff --git a/commands/deps_resolve.go b/commands/deps_resolve.go
index d242eeb..4c8e197 100644
--- a/commands/deps_resolve.go
+++ b/commands/deps_resolve.go
@@ -23,6 +23,7 @@ import (
"path/filepath"
"regexp"
"strings"
+ "text/template"
"github.com/spf13/cobra"
@@ -31,10 +32,15 @@ import (
)
var outDir string
+var summaryTplPath string
+var summaryTpl *template.Template
func init() {
DepsResolveCommand.PersistentFlags().StringVarP(&outDir, "output", "o",
"",
"the directory to output the resolved dependencies' licenses,
if not set the dependencies' licenses won't be saved")
+ DepsResolveCommand.PersistentFlags().StringVarP(&summaryTplPath,
"summary", "s", "",
+ "the template file to write the summary of dependencies'
licenses, a new file named \"LICENSE\" will be "+
+ "created in the same directory as the template file, to
save the final summary.")
}
var fileNamePattern = regexp.MustCompile(`[^a-zA-Z0-9\\.\-]`)
@@ -44,16 +50,27 @@ var DepsResolveCommand = &cobra.Command{
Aliases: []string{"r"},
Long: "resolves all dependencies of a module and their transitive
dependencies",
PreRunE: func(cmd *cobra.Command, args []string) error {
- if outDir == "" {
- return nil
- }
- absPath, err := filepath.Abs(outDir)
- if err != nil {
- return err
+ if outDir != "" {
+ absPath, err := filepath.Abs(outDir)
+ if err != nil {
+ return err
+ }
+ outDir = absPath
+ if err := os.MkdirAll(outDir, 0o700); err != nil &&
!os.IsExist(err) {
+ return err
+ }
}
- outDir = absPath
- if err := os.MkdirAll(outDir, 0o700); err != nil &&
!os.IsExist(err) {
- return err
+ if summaryTplPath != "" {
+ absPath, err := filepath.Abs(summaryTplPath)
+ if err != nil {
+ return err
+ }
+ summaryTplPath = absPath
+ tpl, err := deps.ParseTemplate(summaryTplPath)
+ if err != nil {
+ return err
+ }
+ summaryTpl = tpl
}
return nil
},
@@ -64,6 +81,12 @@ var DepsResolveCommand = &cobra.Command{
return err
}
+ if summaryTpl != nil {
+ if err := writeSummary(&report); err != nil {
+ return err
+ }
+ }
+
if outDir != "" {
for _, result := range report.Resolved {
writeLicense(result)
@@ -102,3 +125,17 @@ func writeLicense(result *deps.Result) {
return
}
}
+
+func writeSummary(rep *deps.Report) error {
+ file, err := os.Create(filepath.Join(filepath.Dir(summaryTplPath),
"LICENSE"))
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ summary, err := deps.GenerateSummary(summaryTpl, &Config.Header, rep)
+ if err != nil {
+ return err
+ }
+ _, err = file.WriteString(summary)
+ return err
+}
diff --git a/pkg/deps/config.go b/pkg/deps/config.go
index 0633b96..64c0a53 100644
--- a/pkg/deps/config.go
+++ b/pkg/deps/config.go
@@ -23,7 +23,14 @@ import (
)
type ConfigDeps struct {
- Files []string `yaml:"files"`
+ Files []string `yaml:"files"`
+ License []*ConfigDepLicense `yaml:"licenses"`
+}
+
+type ConfigDepLicense struct {
+ Name string `yaml:"name"`
+ Version string `yaml:"version"`
+ License string `yaml:"license"`
}
func (config *ConfigDeps) Finalize(configFile string) error {
diff --git a/pkg/deps/golang.go b/pkg/deps/golang.go
index 063fbf3..a65c20c 100644
--- a/pkg/deps/golang.go
+++ b/pkg/deps/golang.go
@@ -45,7 +45,7 @@ func (resolver *GoModResolver) CanResolve(file string) bool {
}
// Resolve resolves licenses of all dependencies declared in the go.mod file.
-func (resolver *GoModResolver) Resolve(goModFile string, report *Report) error
{
+func (resolver *GoModResolver) Resolve(goModFile string, licenses
[]*ConfigDepLicense, report *Report) error {
if err := os.Chdir(filepath.Dir(goModFile)); err != nil {
return err
}
@@ -78,13 +78,19 @@ func (resolver *GoModResolver) Resolve(goModFile string,
report *Report) error {
logger.Log.Debugln("Module size:", len(modules))
- return resolver.ResolvePackages(modules, report)
+ return resolver.ResolvePackages(modules, licenses, report)
}
// ResolvePackages resolves the licenses of the given packages.
-func (resolver *GoModResolver) ResolvePackages(modules []*packages.Module,
report *Report) error {
+func (resolver *GoModResolver) ResolvePackages(modules []*packages.Module,
licenses []*ConfigDepLicense, report *Report) error {
for _, module := range modules {
- err := resolver.ResolvePackageLicense(module, report)
+ var decalreLicense *ConfigDepLicense
+ for _, l := range licenses {
+ if l.Name == module.Path && l.Version == module.Version
{
+ decalreLicense = l
+ }
+ }
+ err := resolver.ResolvePackageLicense(module, decalreLicense,
report)
if err != nil {
logger.Log.Warnf("Failed to resolve the license of
<%s>: %v\n", module.Path, err)
report.Skip(&Result{
@@ -99,7 +105,7 @@ func (resolver *GoModResolver) ResolvePackages(modules
[]*packages.Module, repor
var possibleLicenseFileName =
regexp.MustCompile(`(?i)^LICENSE|LICENCE(\.txt)?|COPYING(\.txt)?$`)
-func (resolver *GoModResolver) ResolvePackageLicense(module *packages.Module,
report *Report) error {
+func (resolver *GoModResolver) ResolvePackageLicense(module *packages.Module,
declareLicense *ConfigDepLicense, report *Report) error {
dir := module.Dir
for {
@@ -117,15 +123,22 @@ func (resolver *GoModResolver)
ResolvePackageLicense(module *packages.Module, re
if err != nil {
return err
}
- identifier, err := license.Identify(module.Path,
string(content))
- if err != nil {
- return err
+ var licenseID string
+ if declareLicense != nil {
+ licenseID = declareLicense.License
+ } else {
+ identifier, err :=
license.Identify(module.Path, string(content))
+ if err != nil {
+ return err
+ }
+ licenseID = identifier
}
+
report.Resolve(&Result{
Dependency: module.Path,
LicenseFilePath: licenseFilePath,
LicenseContent: string(content),
- LicenseSpdxID: identifier,
+ LicenseSpdxID: licenseID,
Version: module.Version,
})
return nil
diff --git a/pkg/deps/jar.go b/pkg/deps/jar.go
index fada99c..0121da7 100644
--- a/pkg/deps/jar.go
+++ b/pkg/deps/jar.go
@@ -37,7 +37,7 @@ func (resolver *JarResolver) CanResolve(jarFile string) bool {
return filepath.Ext(jarFile) == ".jar"
}
-func (resolver *JarResolver) Resolve(jarFile string, report *Report) error {
+func (resolver *JarResolver) Resolve(jarFile string, licenses
[]*ConfigDepLicense, report *Report) error {
state := NotFound
if err := resolver.ResolveJar(&state, jarFile, Unknown, report); err !=
nil {
dep := filepath.Base(jarFile)
@@ -76,7 +76,7 @@ func (resolver *JarResolver) ResolveJar(state *State,
jarFile, version string, r
return err
}
- return resolver.IdentifyLicense(jarFile, dep,
buf.String(), version, report)
+ return resolver.IdentifyLicense(jarFile, dep,
buf.String(), version, nil, report)
}
}
@@ -122,17 +122,23 @@ func (resolver *JarResolver) ReadFileFromZip(archiveFile
*zip.File) (*bytes.Buff
return buf, nil
}
-func (resolver *JarResolver) IdentifyLicense(path, dep, content, version
string, report *Report) error {
- identifier, err := license.Identify(path, content)
- if err != nil {
- return err
+func (resolver *JarResolver) IdentifyLicense(path, dep, content, version
string, declareLicense *ConfigDepLicense, report *Report) error {
+ var licenseID string
+ if declareLicense != nil {
+ licenseID = declareLicense.License
+ } else {
+ identifier, err := license.Identify(path, content)
+ if err != nil {
+ return err
+ }
+ licenseID = identifier
}
report.Resolve(&Result{
Dependency: dep,
LicenseFilePath: path,
LicenseContent: content,
- LicenseSpdxID: identifier,
+ LicenseSpdxID: licenseID,
Version: version,
})
return nil
diff --git a/pkg/deps/jar_test.go b/pkg/deps/jar_test.go
index de31b0b..6215685 100644
--- a/pkg/deps/jar_test.go
+++ b/pkg/deps/jar_test.go
@@ -132,7 +132,7 @@ func TestResolveJar(t *testing.T) {
report := deps.Report{}
for _, jar := range jars {
if resolver.CanResolve(jar) {
- if err := resolver.Resolve(jar, &report); err
!= nil {
+ if err := resolver.Resolve(jar, nil, &report);
err != nil {
t.Error(err)
return
}
diff --git a/pkg/deps/maven.go b/pkg/deps/maven.go
index 3274074..28d1ce3 100644
--- a/pkg/deps/maven.go
+++ b/pkg/deps/maven.go
@@ -48,7 +48,7 @@ func (resolver *MavenPomResolver) CanResolve(mavenPomFile
string) bool {
}
// Resolve resolves licenses of all dependencies declared in the pom.xml file.
-func (resolver *MavenPomResolver) Resolve(mavenPomFile string, report *Report)
error {
+func (resolver *MavenPomResolver) Resolve(mavenPomFile string, licenses
[]*ConfigDepLicense, report *Report) error {
if err := os.Chdir(filepath.Dir(mavenPomFile)); err != nil {
return err
}
@@ -70,7 +70,7 @@ func (resolver *MavenPomResolver) Resolve(mavenPomFile
string, report *Report) e
}
}
- return resolver.ResolveDependencies(deps, report)
+ return resolver.ResolveDependencies(deps, licenses, report)
}
// CheckMVN check available maven tools, find local repositories and download
all dependencies
@@ -142,10 +142,16 @@ func (resolver *MavenPomResolver) LoadDependencies()
([]*Dependency, error) {
}
// ResolveDependencies resolves the licenses of the given dependencies
-func (resolver *MavenPomResolver) ResolveDependencies(deps []*Dependency,
report *Report) error {
+func (resolver *MavenPomResolver) ResolveDependencies(deps []*Dependency,
licenses []*ConfigDepLicense, report *Report) error {
for _, dep := range deps {
state := NotFound
- err := resolver.ResolveLicense(&state, dep, report)
+ var declareLicense *ConfigDepLicense
+ for _, l := range licenses {
+ if l.Name == fmt.Sprintf("%s:%s", dep.GroupID,
dep.ArtifactID) && l.Version == dep.Version {
+ declareLicense = l
+ }
+ }
+ err := resolver.ResolveLicense(&state, dep, declareLicense,
report)
if err != nil {
logger.Log.Warnf("Failed to resolve the license of
<%s>: %v\n", dep.Jar(), state.String())
report.Skip(&Result{
@@ -159,17 +165,17 @@ func (resolver *MavenPomResolver)
ResolveDependencies(deps []*Dependency, report
}
// ResolveLicense search all possible locations of the license, such as pom
file, jar package
-func (resolver *MavenPomResolver) ResolveLicense(state *State, dep
*Dependency, report *Report) error {
+func (resolver *MavenPomResolver) ResolveLicense(state *State, dep
*Dependency, declareLicense *ConfigDepLicense, report *Report) error {
err := resolver.ResolveJar(state, filepath.Join(resolver.repo,
dep.Path(), dep.Jar()), dep.Version, report)
if err == nil {
return nil
}
- return resolver.ResolveLicenseFromPom(state, dep, report)
+ return resolver.ResolveLicenseFromPom(state, dep, declareLicense,
report)
}
// ResolveLicenseFromPom search for license in the pom file, which may appear
in the header comments or in license element of xml
-func (resolver *MavenPomResolver) ResolveLicenseFromPom(state *State, dep
*Dependency, report *Report) (err error) {
+func (resolver *MavenPomResolver) ResolveLicenseFromPom(state *State, dep
*Dependency, declareLicense *ConfigDepLicense, report *Report) (err error) {
pomFile := filepath.Join(resolver.repo, dep.Path(), dep.Pom())
pom, err := resolver.ReadLicensesFromPom(pomFile)
@@ -192,7 +198,7 @@ func (resolver *MavenPomResolver)
ResolveLicenseFromPom(state *State, dep *Depen
return err
} else if headerComments != "" {
*state |= FoundLicenseInPomHeader
- return resolver.IdentifyLicense(pomFile, dep.Jar(),
headerComments, dep.Version, report)
+ return resolver.IdentifyLicense(pomFile, dep.Jar(),
headerComments, dep.Version, declareLicense, report)
}
return fmt.Errorf("not found in pom file")
diff --git a/pkg/deps/maven_test.go b/pkg/deps/maven_test.go
index 5c4b797..75c6ae0 100644
--- a/pkg/deps/maven_test.go
+++ b/pkg/deps/maven_test.go
@@ -113,7 +113,7 @@ func TestResolveMaven(t *testing.T) {
if resolver.CanResolve(pomFile) {
report := deps.Report{}
- if err := resolver.Resolve(pomFile, &report); err !=
nil {
+ if err := resolver.Resolve(pomFile, nil, &report); err
!= nil {
t.Error(err)
return
}
diff --git a/pkg/deps/npm.go b/pkg/deps/npm.go
index 77e814e..cbecd4e 100644
--- a/pkg/deps/npm.go
+++ b/pkg/deps/npm.go
@@ -63,7 +63,7 @@ func (resolver *NpmResolver) CanResolve(file string) bool {
}
// Resolve resolves licenses of all dependencies declared in the package.json
file.
-func (resolver *NpmResolver) Resolve(pkgFile string, report *Report) error {
+func (resolver *NpmResolver) Resolve(pkgFile string, licenses
[]*ConfigDepLicense, report *Report) error {
workDir := filepath.Dir(pkgFile)
if err := os.Chdir(workDir); err != nil {
return err
@@ -85,7 +85,7 @@ func (resolver *NpmResolver) Resolve(pkgFile string, report
*Report) error {
// Walk through each package's root directory to resolve licenses
// Resolve from a package's package.json file or its license file
for _, pkg := range pkgs {
- if result := resolver.ResolvePackageLicense(pkg.Name,
pkg.Path); result.LicenseSpdxID != "" {
+ if result := resolver.ResolvePackageLicense(pkg.Name, pkg.Path,
licenses); result.LicenseSpdxID != "" {
report.Resolve(result)
} else {
result.LicenseSpdxID = Unknown
@@ -185,17 +185,17 @@ func (resolver *NpmResolver) GetInstalledPkgs(pkgDir
string) []*Package {
// First, try to find and parse the package's package.json file to check the
license file
// If the previous step fails, then try to identify the package's LICENSE file
// It's a necessary procedure to check the LICENSE file, because the resolver
needs to record the license content
-func (resolver *NpmResolver) ResolvePackageLicense(pkgName, pkgPath string)
*Result {
+func (resolver *NpmResolver) ResolvePackageLicense(pkgName, pkgPath string,
licenses []*ConfigDepLicense) *Result {
result := &Result{
Dependency: pkgName,
}
// resolve from the package.json file
- if err := resolver.ResolvePkgFile(result, pkgPath); err != nil {
+ if err := resolver.ResolvePkgFile(result, pkgPath, licenses); err !=
nil {
result.ResolveErrors = append(result.ResolveErrors, err)
}
// resolve from the LICENSE file
- if err := resolver.ResolveLcsFile(result, pkgPath); err != nil {
+ if err := resolver.ResolveLcsFile(result, pkgPath, licenses); err !=
nil {
result.ResolveErrors = append(result.ResolveErrors, err)
}
@@ -203,7 +203,7 @@ func (resolver *NpmResolver) ResolvePackageLicense(pkgName,
pkgPath string) *Res
}
// ResolvePkgFile tries to find and parse the package.json file to capture the
license field
-func (resolver *NpmResolver) ResolvePkgFile(result *Result, pkgPath string)
error {
+func (resolver *NpmResolver) ResolvePkgFile(result *Result, pkgPath string,
licenses []*ConfigDepLicense) error {
expectedPkgFile := filepath.Join(pkgPath, PkgFileName)
packageInfo, err := resolver.ParsePkgFile(expectedPkgFile)
if err != nil {
@@ -211,6 +211,13 @@ func (resolver *NpmResolver) ResolvePkgFile(result
*Result, pkgPath string) erro
}
result.Version = packageInfo.Version
+ for _, l := range licenses {
+ if l.Name == packageInfo.Name && l.Version ==
packageInfo.Version {
+ result.LicenseSpdxID = l.License
+ return nil
+ }
+ }
+
if lcs, ok := resolver.ResolveLicenseField(packageInfo.License); ok {
result.LicenseSpdxID = lcs
return nil
@@ -259,7 +266,7 @@ func (resolver *NpmResolver) ResolveLicensesField(licenses
[]Lcs) (string, bool)
}
// ResolveLcsFile tries to find the license file to identify the license
-func (resolver *NpmResolver) ResolveLcsFile(result *Result, pkgPath string)
error {
+func (resolver *NpmResolver) ResolveLcsFile(result *Result, pkgPath string,
licenses []*ConfigDepLicense) error {
depFiles, err := os.ReadDir(pkgPath)
if err != nil {
return err
@@ -278,6 +285,12 @@ func (resolver *NpmResolver) ResolveLcsFile(result
*Result, pkgPath string) erro
if result.LicenseSpdxID != "" {
return nil
}
+ for _, l := range licenses {
+ if l.Name == info.Name() && l.Version == result.Version
{
+ result.LicenseSpdxID = l.License
+ return nil
+ }
+ }
identifier, err := license.Identify(result.Dependency,
string(content))
if err != nil {
return err
diff --git a/pkg/deps/npm_test.go b/pkg/deps/npm_test.go
index f30cba5..5d76110 100644
--- a/pkg/deps/npm_test.go
+++ b/pkg/deps/npm_test.go
@@ -95,7 +95,7 @@ func TestResolvePkgFile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- err = resolver.ResolvePkgFile(result, f.Name())
+ err = resolver.ResolvePkgFile(result, f.Name(), nil)
if result.LicenseSpdxID != data.result && (err != nil) ==
data.hasErr {
t.Fail()
}
diff --git a/pkg/deps/resolve.go b/pkg/deps/resolve.go
index 3c0d3b4..325b9d0 100644
--- a/pkg/deps/resolve.go
+++ b/pkg/deps/resolve.go
@@ -23,7 +23,7 @@ import (
type Resolver interface {
CanResolve(string) bool
- Resolve(string, *Report) error
+ Resolve(string, []*ConfigDepLicense, *Report) error
}
var Resolvers = []Resolver{
@@ -39,7 +39,7 @@ resolveFile:
if !resolver.CanResolve(file) {
continue
}
- if err := resolver.Resolve(file, report); err != nil {
+ if err := resolver.Resolve(file, config.License,
report); err != nil {
return err
}
continue resolveFile
diff --git a/pkg/deps/summary.go b/pkg/deps/summary.go
new file mode 100644
index 0000000..d14acb7
--- /dev/null
+++ b/pkg/deps/summary.go
@@ -0,0 +1,106 @@
+// 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.
+
+package deps
+
+import (
+ "bytes"
+ "os"
+ "text/template"
+
+ "github.com/apache/skywalking-eyes/pkg/header"
+ "github.com/apache/skywalking-eyes/pkg/license"
+)
+
+type SummaryRenderContext struct {
+ LicenseContent string // Current project license
content
+ Groups []*SummaryRenderLicenseGroup // All dependency license
groups
+}
+
+type SummaryRenderLicenseGroup struct {
+ LicenseID string // Aggregate all same license ID
dependencies
+ Deps []*SummaryRenderLicense // Same license ID dependencies
+}
+
+type SummaryRenderLicense struct {
+ Name string // Dependency name
+ Version string // Dependency version
+ LicenseID string // License ID
+}
+
+func ParseTemplate(path string) (*template.Template, error) {
+ tpl, err := os.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ return template.New("summary").Parse(string(tpl))
+}
+
+// GenerateSummary generate the summary content by template, license config
and dependency report
+func GenerateSummary(tpl *template.Template, head *header.ConfigHeader, rep
*Report) (string, error) {
+ var r bytes.Buffer
+ context, err := generateSummaryRenderContext(head, rep)
+ if err != nil {
+ return "", err
+ }
+ if err := tpl.Execute(&r, context); err != nil {
+ return "", err
+ }
+ return r.String(), nil
+}
+
+func generateSummaryRenderContext(head *header.ConfigHeader, rep *Report)
(*SummaryRenderContext, error) {
+ // the license id of the project
+ var headerContent string
+ if head.License.SpdxID != "" {
+ c, err := license.GetLicenseContent(head.License.SpdxID)
+ if err != nil {
+ return nil, err
+ }
+ headerContent = c
+ }
+ if headerContent == "" {
+ headerContent = head.GetLicenseContent()
+ }
+
+ groups := make(map[string]*SummaryRenderLicenseGroup)
+ for _, r := range rep.Resolved {
+ group := groups[r.LicenseSpdxID]
+ if group == nil {
+ group = &SummaryRenderLicenseGroup{
+ LicenseID: r.LicenseSpdxID,
+ Deps: make([]*SummaryRenderLicense, 0),
+ }
+ groups[r.LicenseSpdxID] = group
+ }
+
+ group.Deps = append(group.Deps, &SummaryRenderLicense{
+ Name: r.Dependency,
+ Version: r.Version,
+ LicenseID: r.LicenseSpdxID,
+ })
+ }
+
+ groupArray := make([]*SummaryRenderLicenseGroup, 0)
+ for _, g := range groups {
+ groupArray = append(groupArray, g)
+ }
+ return &SummaryRenderContext{
+ LicenseContent: headerContent,
+ Groups: groupArray,
+ }, nil
+}
diff --git a/pkg/license/identifier.go b/pkg/license/identifier.go
index 7a09a15..c23ea75 100644
--- a/pkg/license/identifier.go
+++ b/pkg/license/identifier.go
@@ -29,8 +29,10 @@ import (
"github.com/apache/skywalking-eyes/internal/logger"
)
+var licenseTemplatesDir = "lcs-templates"
+
var templatesDirs = []string{
- "lcs-templates",
+ licenseTemplatesDir,
// Some projects simply use the header text as their LICENSE content...
"header-templates",
}
@@ -102,3 +104,12 @@ func Identify(pkgPath, content string) (string, error) {
return "", fmt.Errorf("cannot identify license content")
}
}
+
+// GetLicenseContent from license id
+func GetLicenseContent(spdxID string) (string, error) {
+ res, err := assets.Asset(filepath.Join(licenseTemplatesDir,
spdxID+".txt"))
+ if err != nil {
+ return "", err
+ }
+ return string(res), nil
+}