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 a952c24 feature: support resolving pom.xml for maven (#50)
a952c24 is described below
commit a952c24f7685ec42d9d817e9c01affc310219279
Author: MoGuGuai-hzr <[email protected]>
AuthorDate: Tue Aug 3 19:45:45 2021 +0800
feature: support resolving pom.xml for maven (#50)
---
go.mod | 1 +
go.sum | 5 +
pkg/deps/golang.go | 3 +-
pkg/deps/maven.go | 607 +++++++++++++++++++++++++++++++++++++++++++++++++
pkg/deps/maven_test.go | 127 +++++++++++
pkg/deps/resolve.go | 4 +-
6 files changed, 744 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index cd73717..7cad5c5 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ 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/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/tools v0.1.5
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
diff --git a/go.sum b/go.sum
index 7b23c80..2caadf8 100644
--- a/go.sum
+++ b/go.sum
@@ -355,6 +355,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod
h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod
h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -382,6 +384,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod
h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
@@ -391,6 +394,8 @@ golang.org/x/text
v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/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
h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/pkg/deps/golang.go b/pkg/deps/golang.go
index 62de0f8..13a04d5 100644
--- a/pkg/deps/golang.go
+++ b/pkg/deps/golang.go
@@ -98,11 +98,10 @@ func (resolver *GoModResolver) ResolvePackages(modules
[]*packages.Module, repor
})
}
}
-
return nil
}
-var possibleLicenseFileName =
regexp.MustCompile(`(?i)^LICENSE|LICENCE(\.txt)?|COPYING|COPYING(\.txt)?$`)
+var possibleLicenseFileName =
regexp.MustCompile(`(?i)^LICENSE|LICENCE(\.txt)?|COPYING(\.txt)?$`)
func (resolver *GoModResolver) ResolvePackageLicense(module *packages.Module,
report *Report) error {
dir := module.Dir
diff --git a/pkg/deps/maven.go b/pkg/deps/maven.go
new file mode 100644
index 0000000..4a6c476
--- /dev/null
+++ b/pkg/deps/maven.go
@@ -0,0 +1,607 @@
+//
+// 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 (
+ "archive/zip"
+ "bufio"
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "golang.org/x/net/html/charset"
+
+ "github.com/apache/skywalking-eyes/license-eye/internal/logger"
+ "github.com/apache/skywalking-eyes/license-eye/pkg/license"
+)
+
+type MavenPomResolver struct {
+ maven string
+ repo string
+}
+
+// CanResolve determine whether the file can be resolve by name of the file
+func (resolver *MavenPomResolver) CanResolve(mavenPomFile string) bool {
+ base := filepath.Base(mavenPomFile)
+ logger.Log.Debugln("Base name:", base)
+ return base == "pom.xml"
+}
+
+// Resolve resolves licenses of all dependencies declared in the pom.xml file.
+func (resolver *MavenPomResolver) Resolve(mavenPomFile string, report *Report)
error {
+ if err := os.Chdir(filepath.Dir(mavenPomFile)); err != nil {
+ return err
+ }
+
+ if err := resolver.CheckMVN(); err != nil {
+ return err
+ }
+
+ deps, err := resolver.LoadDependencies()
+ if err != nil {
+ // attempt to download dependencies
+ if err = resolver.DownloadDeps(); err != nil {
+ return fmt.Errorf("dependencies download error")
+ }
+ // load again
+ deps, err = resolver.LoadDependencies()
+ if err != nil {
+ return err
+ }
+ }
+
+ if err := resolver.ResolveDependencies(deps, report); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// CheckMVN check available maven tools, find local repositories and download
all dependencies
+func (resolver *MavenPomResolver) CheckMVN() error {
+ if err := resolver.FindMaven("./mvnw"); err == nil {
+ logger.Log.Debugln("mvnw is found, will use mvnw by default")
+ } else if err := resolver.FindMaven("mvn"); err != nil {
+ return fmt.Errorf("neither found mvnw nor mvn")
+ }
+
+ if err := resolver.FindLocalRepository(); err != nil {
+ return fmt.Errorf("can not find the local repository: %v", err)
+ }
+
+ return nil
+}
+
+func (resolver *MavenPomResolver) FindMaven(execName string) error {
+ if _, err := exec.Command(execName, "--version").Output(); err != nil {
+ return err
+ }
+
+ resolver.maven = execName
+ return nil
+}
+
+func (resolver *MavenPomResolver) FindLocalRepository() error {
+ output, err := exec.Command(resolver.maven, "help:evaluate",
"-Dexpression=settings.localRepository", "-q", "-DforceStdout").Output() //
#nosec G204
+ if err != nil {
+ return err
+ }
+
+ resolver.repo = string(output)
+ return nil
+}
+
+func (resolver *MavenPomResolver) DownloadDeps() error {
+ cmd := exec.Command(resolver.maven, "dependency:resolve") // #nosec G204
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ err := cmd.Run()
+ if err == nil {
+ return nil
+ }
+ // the failure may be caused by the lack of sub modules, try to install
it
+ install := exec.Command(resolver.maven, "clean", "install",
"-Dcheckstyle.skip=true", "-Drat.skip=true", "-Dmaven.test.skip=true") //
#nosec G204
+ install.Stdout = os.Stdout
+ install.Stderr = os.Stderr
+
+ return install.Run()
+}
+
+func (resolver *MavenPomResolver) LoadDependencies() ([]*Dependency, error) {
+ buf := bytes.NewBuffer(nil)
+
+ cmd := exec.Command(resolver.maven, "dependency:tree") // #nosec G204
+ cmd.Stdout = bufio.NewWriter(buf)
+ cmd.Stderr = os.Stderr
+
+ logger.Log.Debugf("Run command: ă%vă, please wait", cmd.String())
+ err := cmd.Run()
+ if err != nil {
+ return nil, err
+ }
+
+ deps := LoadDependencies(buf.Bytes())
+ return deps, nil
+}
+
+// ResolveDependencies resolves the licenses of the given dependencies
+func (resolver *MavenPomResolver) ResolveDependencies(deps []*Dependency,
report *Report) error {
+ for _, dep := range deps {
+ state := NotFound
+ err := resolver.ResolveLicense(&state, dep, report)
+ if err != nil {
+ logger.Log.Warnf("Failed to resolve the license of
<%s>: %v\n", dep.Jar(), state.String())
+ report.Skip(&Result{
+ Dependency: dep.Jar(),
+ LicenseSpdxID: Unknown,
+ })
+ }
+ }
+ return nil
+}
+
+// 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 {
+ err := resolver.ResolveLicenseFromJar(state, dep, report)
+ if err == nil {
+ return nil
+ }
+
+ return resolver.ResolveLicenseFromPom(state, dep, 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) {
+ pomFile := filepath.Join(resolver.repo, dep.Path(), dep.Pom())
+
+ pom, err := resolver.ReadLicensesFromPom(pomFile)
+ if err != nil {
+ return err
+ } else if pom != nil && len(pom.Licenses) != 0 {
+ report.Resolve(&Result{
+ Dependency: dep.Jar(),
+ LicenseFilePath: dep.Path(),
+ LicenseContent: pom.Raw(),
+ LicenseSpdxID: pom.AllLicenses(),
+ })
+
+ return nil
+ }
+
+ headerComments, err := resolver.ReadHeaderCommentsFromPom(pomFile)
+ if err != nil {
+ return err
+ } else if headerComments != "" {
+ *state |= FoundLicenseInPomHeader
+ return resolver.IdentifyLicense(dep, headerComments, report)
+ }
+
+ return fmt.Errorf("not found in pom file")
+}
+
+func (resolver *MavenPomResolver) ReadLicensesFromPom(pomFile string)
(*PomFile, error) {
+ file, err := os.Open(pomFile)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ dec := xml.NewDecoder(file)
+ dec.CharsetReader = charset.NewReaderLabel
+
+ pom := new(PomFile)
+ err = dec.Decode(pom)
+ if err != nil {
+ return nil, err
+ }
+
+ return pom, nil
+}
+
+func (resolver *MavenPomResolver) ReadHeaderCommentsFromPom(pomFile string)
(string, error) {
+ file, err := os.Open(pomFile)
+ if err != nil {
+ return "", err
+ }
+ defer file.Close()
+
+ var comments string
+
+ dec := xml.NewDecoder(file)
+ dec.CharsetReader = charset.NewReaderLabel
+loop:
+ for {
+ tok, err := dec.Token()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return "", err
+ }
+
+ switch tok := tok.(type) {
+ // search header only
+ case xml.StartElement:
+ break loop
+ case xml.Comment:
+ comments += string(tok.Copy())
+ }
+ }
+
+ if SeemLicense(comments) {
+ return comments, nil
+ }
+
+ return "", nil
+}
+
+var (
+ reMaybeLicense =
regexp.MustCompile(`(?i)licen[sc]e|copyright|copying`)
+ reHaveManifestFile =
regexp.MustCompile(`(?i)^(\S*/)?manifest\.MF$`)
+ reSearchLicenseInManifestFile =
regexp.MustCompile(`(?im)^.*?licen[cs]e.*?(http.+)`)
+)
+
+// SeemLicense determine whether the content of the file may be a license file
+func SeemLicense(content string) bool {
+ return reMaybeLicense.MatchString(content)
+}
+
+// ResolveLicenseFromJar search for the license in the jar package, and it may
appear in MANIFEST.MF, LICENSE.txt, NOTICE.txt
+func (resolver *MavenPomResolver) ResolveLicenseFromJar(state *State, dep
*Dependency, report *Report) (err error) {
+ jarPath := filepath.Join(resolver.repo, dep.Path(), dep.Jar())
+ compressedJar, err := zip.OpenReader(jarPath)
+ if err != nil {
+ return err
+ }
+ defer compressedJar.Close()
+
+ var manifestFile *zip.File
+
+ // traverse all files in jar
+ for _, compressedFile := range compressedJar.File {
+ archiveFile := compressedFile.Name
+ switch {
+ case reHaveManifestFile.MatchString(archiveFile):
+ manifestFile = compressedFile
+
+ case possibleLicenseFileName.MatchString(archiveFile):
+ *state |= FoundLicenseInJarLicenseFile
+ buf, err := resolver.ReadFileFromZip(compressedFile)
+ if err != nil {
+ return err
+ }
+
+ return resolver.IdentifyLicense(dep, buf.String(),
report)
+ }
+ }
+
+ if manifestFile != nil {
+ buf, err := resolver.ReadFileFromZip(manifestFile)
+ if err != nil {
+ return err
+ }
+ norm := regexp.MustCompile(`(?im)[\r\n]+ +`)
+ content := norm.ReplaceAllString(buf.String(), "")
+
+ r := reSearchLicenseInManifestFile.FindStringSubmatch(content)
+ if len(r) != 0 {
+ report.Resolve(&Result{
+ Dependency: dep.Jar(),
+ LicenseFilePath: dep.Path(),
+ LicenseContent: r[1],
+ LicenseSpdxID: r[1],
+ })
+ return nil
+ }
+ }
+
+ return fmt.Errorf("cannot find license content")
+}
+
+func (resolver *MavenPomResolver) ReadFileFromZip(archiveFile *zip.File)
(*bytes.Buffer, error) {
+ file, err := archiveFile.Open()
+ if err != nil {
+ return nil, err
+ }
+
+ buf := bytes.NewBuffer(nil)
+ w := bufio.NewWriter(buf)
+ _, err = io.CopyN(w, file, int64(archiveFile.UncompressedSize64))
+ if err != nil {
+ return nil, err
+ }
+
+ w.Flush()
+ file.Close()
+ return buf, nil
+}
+
+func (resolver *MavenPomResolver) IdentifyLicense(dep *Dependency, content
string, report *Report) error {
+ identifier, err := license.Identify(dep.Path(), content)
+ if err != nil {
+ return err
+ }
+
+ report.Resolve(&Result{
+ Dependency: dep.Jar(),
+ LicenseFilePath: dep.Path(),
+ LicenseContent: content,
+ LicenseSpdxID: identifier,
+ })
+ return nil
+}
+
+func LoadDependencies(data []byte) []*Dependency {
+ depsTree := LoadDependenciesTree(data)
+
+ cnt := 0
+ for _, dep := range depsTree {
+ cnt += dep.Count()
+ }
+
+ deps := make([]*Dependency, 0, cnt)
+
+ queue := []*Dependency{}
+ for _, depTree := range depsTree {
+ queue = append(queue, depTree)
+ for len(queue) > 0 {
+ dep := queue[0]
+
+ queue = queue[1:]
+ queue = append(queue, dep.TransitiveDeps...)
+
+ deps = append(deps, dep.Clone())
+ }
+ }
+ return deps
+}
+
+func LoadDependenciesTree(data []byte) []*Dependency {
+ type Elem struct {
+ *Dependency
+ level int
+ }
+
+ stack := []Elem{}
+ unique := make(map[string]struct{})
+
+ reFind := regexp.MustCompile(`(?im)^.*? ([\| ]*)(\+\-|\\\-)
(\b.+):(\b.+):(\b.+):(\b.+):(\b.+)$`)
+ rawDeps := reFind.FindAllSubmatch(data, -1)
+
+ deps := make([]*Dependency, 0, len(rawDeps))
+ for _, rawDep := range rawDeps {
+ gid := strings.Split(string(rawDep[3]), ".")
+ dep := &Dependency{
+ GroupID: gid,
+ ArtifactID: string(rawDep[4]),
+ Packaging: string(rawDep[5]),
+ Version: string(rawDep[6]),
+ Scope: string(rawDep[7]),
+ }
+
+ if _, have := unique[dep.Path()]; have {
+ continue
+ }
+
+ if dep.Scope == "test" || dep.Scope == "provided" || dep.Scope
== "system" {
+ continue
+ }
+
+ unique[dep.Path()] = struct{}{}
+
+ level := len(rawDep[1]) / 3
+ dependence := string(rawDep[2])
+
+ if level == 0 {
+ deps = append(deps, dep)
+
+ if len(stack) != 0 {
+ stack = stack[:0]
+ }
+
+ stack = append(stack, Elem{dep, level})
+ continue
+ }
+
+ tail := stack[len(stack)-1]
+
+ if level == tail.level {
+ stack[len(stack)-1] = Elem{dep, level}
+ stack[len(stack)-2].TransitiveDeps =
append(stack[len(stack)-2].TransitiveDeps, dep)
+ } else {
+ stack = append(stack, Elem{dep, level})
+ tail.TransitiveDeps = append(tail.TransitiveDeps, dep)
+ }
+
+ if dependence == `\-` {
+ stack = stack[:len(stack)-1]
+ }
+ }
+ return deps
+}
+
+const (
+ FoundLicenseInPomHeader State = 1 << iota
+ FoundLicenseInJarLicenseFile
+ FoundLicenseInJarManifestFile
+ NotFound State = 0
+)
+
+type State int
+
+func (s *State) String() string {
+ if *s == 0 {
+ return "no possible license found"
+ }
+
+ var m []string
+
+ if *s&FoundLicenseInPomHeader != 0 {
+ m = append(m, "failed to resolve license found in pom header")
+ }
+ if *s&FoundLicenseInJarLicenseFile != 0 {
+ m = append(m, "failed to resolve license file found in jar")
+ }
+ if *s&FoundLicenseInJarManifestFile != 0 {
+ m = append(m, "failed to resolve license content from manifest
file found in jar")
+ }
+
+ return strings.Join(m, "ď˝")
+}
+
+type Dependency struct {
+ GroupID []string
+ ArtifactID, Version, Packaging, Scope string
+ TransitiveDeps []*Dependency
+}
+
+func (dep *Dependency) Clone() *Dependency {
+ return &Dependency{
+ GroupID: dep.GroupID,
+ ArtifactID: dep.ArtifactID,
+ Version: dep.Version,
+ Packaging: dep.Packaging,
+ Scope: dep.Scope,
+ }
+}
+
+func (dep *Dependency) Count() int {
+ cnt := 1
+ for _, tDep := range dep.TransitiveDeps {
+ cnt += tDep.Count()
+ }
+ return cnt
+}
+
+func (dep *Dependency) Path() string {
+ return fmt.Sprintf("%v/%v/%v", strings.Join(dep.GroupID, "/"),
dep.ArtifactID, dep.Version)
+}
+
+func (dep *Dependency) Pom() string {
+ return fmt.Sprintf("%v-%v.pom", dep.ArtifactID, dep.Version)
+}
+
+func (dep *Dependency) Jar() string {
+ return fmt.Sprintf("%v-%v.jar", dep.ArtifactID, dep.Version)
+}
+
+func (dep *Dependency) String() string {
+ buf := bytes.NewBuffer(nil)
+ w := bufio.NewWriter(buf)
+
+ _, err := w.WriteString(fmt.Sprintf("%v -> %v : %v %v", dep.GroupID,
dep.ArtifactID, dep.Version, dep.Scope))
+ if err != nil {
+ logger.Log.Error(err)
+ }
+
+ for _, tDep := range dep.TransitiveDeps {
+ _, err = w.WriteString(fmt.Sprintf("\n\t%v", tDep))
+ if err != nil {
+ logger.Log.Error(err)
+ }
+ }
+
+ w.Flush()
+ return buf.String()
+}
+
+// TempDirGenerator Create and destroy one temporary directory
+type TempDirGenerator interface {
+ Create() (string, error)
+ Destroy()
+}
+
+func NewTempDirGenerator() TempDirGenerator {
+ return new(TempDir)
+}
+
+// TempDir an implementation of the TempDirGenerator
+type TempDir struct {
+ dir string
+}
+
+func (t *TempDir) Create() (string, error) {
+ tmpDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ return "", err
+ }
+ t.dir = tmpDir
+ return tmpDir, nil
+}
+
+func (t *TempDir) Destroy() {
+ if t.dir == "" {
+ logger.Log.Errorf("the temporary directory does not exist")
+ return
+ }
+
+ err := os.RemoveAll(t.dir)
+ if err != nil {
+ logger.Log.Errorln(err)
+ }
+}
+
+// PomFile is used to extract license from the pom.xml file
+type PomFile struct {
+ XMLName xml.Name `xml:"project"`
+ Licenses []*XMLLicense `xml:"licenses>license,omitempty"`
+}
+
+// AllLicenses return all licenses found in pom.xml file
+func (pom *PomFile) AllLicenses() string {
+ licenses := []string{}
+ for _, l := range pom.Licenses {
+ licenses = append(licenses, l.Item())
+ }
+ return strings.Join(licenses, ", ")
+}
+
+// Raw return raw data
+func (pom *PomFile) Raw() string {
+ contents := []string{}
+ for _, l := range pom.Licenses {
+ contents = append(contents, l.Raw())
+ }
+ return strings.Join(contents, "\n")
+}
+
+type XMLLicense struct {
+ Name string `xml:"name,omitempty"`
+ URL string `xml:"url,omitempty"`
+ Distribution string `xml:"distribution,omitempty"`
+ Comments string `xml:"comments,omitempty"`
+}
+
+func (l *XMLLicense) Item() string {
+ return GetLicenseFromURL(l.URL)
+}
+
+func (l *XMLLicense) Raw() string {
+ return fmt.Sprintf(`License: {Name: %s, URL: %s, Distribution: %s,
Comments: %s, }`, l.Name, l.URL, l.Distribution, l.Comments)
+}
+
+func GetLicenseFromURL(url string) string {
+ return url
+}
diff --git a/pkg/deps/maven_test.go b/pkg/deps/maven_test.go
new file mode 100644
index 0000000..d63d519
--- /dev/null
+++ b/pkg/deps/maven_test.go
@@ -0,0 +1,127 @@
+//
+// 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_test
+
+import (
+ "bufio"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/apache/skywalking-eyes/license-eye/pkg/deps"
+)
+
+func TestCanResolve(t *testing.T) {
+ resolver := new(deps.MavenPomResolver)
+ for _, test := range []struct {
+ fileName string
+ exp bool
+ }{
+ {"pom.xml", true},
+ {"POM.XML", false},
+ {"log4j-1.2.12.pom", false},
+ {".pom", false},
+ } {
+ b := resolver.CanResolve(test.fileName)
+ if b != test.exp {
+ t.Errorf("MavenPomResolver.CanResolve(\"%v\") = %v,
want %v", test.fileName, b, test.exp)
+ }
+ }
+}
+
+func dump(fileName, content string) error {
+ file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ write := bufio.NewWriter(file)
+ _, err = write.WriteString(content)
+ if err != nil {
+ return err
+ }
+
+ write.Flush()
+ return nil
+}
+
+func TestResolve(t *testing.T) {
+ resolver := new(deps.MavenPomResolver)
+
+ tempDir := deps.NewTempDirGenerator()
+ path, err := tempDir.Create()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer tempDir.Destroy()
+
+ pomFile := filepath.Join(path, "pom.xml")
+
+ for _, test := range []struct {
+ pomContent string
+ cnt int
+ }{
+ {`<?xml version="1.0" encoding="UTF-8"?>
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>apache</groupId>
+ <artifactId>skywalking-eyes</artifactId>
+ <version>1.0</version>
+
+ <dependencies>
+ <!-- https://mvnrepository.com/artifact/junit/junit -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ </dependency>
+ <!--
https://mvnrepository.com/artifact/commons-logging/commons-logging -->
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <!--
https://mvnrepository.com/artifact/org.apache.skywalking/skywalking-sharing-server-plugin
-->
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+
<artifactId>skywalking-sharing-server-plugin</artifactId>
+ <version>8.6.0</version>
+ </dependency>
+ </dependencies>
+ </project>`, 107},
+ } {
+ dump(pomFile, test.pomContent)
+
+ if resolver.CanResolve(pomFile) {
+ report := deps.Report{}
+ if err := resolver.Resolve(pomFile, &report); err !=
nil {
+ t.Error(err)
+ return
+ }
+
+ if len(report.Resolved)+len(report.Skipped) != test.cnt
{
+ t.Errorf("the expected number of jar packages
is: %d, but actually: %d. result:\n%v", test.cnt,
len(report.Resolved)+len(report.Skipped), report.String())
+ }
+
+ }
+ }
+}
diff --git a/pkg/deps/resolve.go b/pkg/deps/resolve.go
index 247fa1e..8c807d4 100644
--- a/pkg/deps/resolve.go
+++ b/pkg/deps/resolve.go
@@ -29,9 +29,11 @@ type Resolver interface {
var Resolvers = []Resolver{
new(GoModResolver),
new(NpmResolver),
+ new(MavenPomResolver),
}
func Resolve(config *ConfigDeps, report *Report) error {
+resolveFile:
for _, file := range config.Files {
for _, resolver := range Resolvers {
if !resolver.CanResolve(file) {
@@ -40,7 +42,7 @@ func Resolve(config *ConfigDeps, report *Report) error {
if err := resolver.Resolve(file, report); err != nil {
return err
}
- return nil
+ continue resolveFile
}
return fmt.Errorf("unable to find a resolver to resolve
dependency declaration file: %v", file)
}