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 63d8963  Enhance NPM dependency resolver to resolve deprecated license 
styles (#52)
63d8963 is described below

commit 63d89639812f1a94bd45d9329d0f936ec4769a37
Author: Youhan Wu <[email protected]>
AuthorDate: Sat Aug 7 17:29:22 2021 +0800

    Enhance NPM dependency resolver to resolve deprecated license styles (#52)
---
 pkg/deps/npm.go      |  61 +++++++++++++++++++++++++---
 pkg/deps/npm_test.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+), 6 deletions(-)

diff --git a/pkg/deps/npm.go b/pkg/deps/npm.go
index 05753a2..fc51930 100644
--- a/pkg/deps/npm.go
+++ b/pkg/deps/npm.go
@@ -27,6 +27,7 @@ import (
        "os"
        "os/exec"
        "path/filepath"
+       "strings"
        "time"
 
        "github.com/apache/skywalking-eyes/license-eye/internal/logger"
@@ -37,11 +38,19 @@ type NpmResolver struct {
        Resolver
 }
 
+// Lcs represents the license style in package.json
+type Lcs struct {
+       Type string `json:"type"`
+       URL  string `json:"url"`
+}
+
 // Package represents package.json
+// License field has inconsistent styles, so we just store the byte array here 
to postpone unmarshalling
 type Package struct {
-       Name    string `json:"name"`
-       License string `json:"license"`
-       Path    string
+       Name     string          `json:"name"`
+       License  json.RawMessage `json:"license"`
+       Licenses []Lcs           `json:"licenses"`
+       Path     string          `json:"-"`
 }
 
 const PkgFileName = "package.json"
@@ -206,10 +215,50 @@ func (resolver *NpmResolver) ResolvePkgFile(pkgFile 
string) (string, error) {
        if err != nil {
                return "", err
        }
-       if packageInfo.License == "" {
-               return "", fmt.Errorf("cannot capture the license field")
+
+       if lcs, ok := resolver.ResolveLicenseField(packageInfo.License); ok {
+               return lcs, nil
+       }
+
+       if lcs, ok := resolver.ResolveLicensesField(packageInfo.Licenses); ok {
+               return lcs, nil
+       }
+
+       return "", fmt.Errorf(`cannot parse the "license"/"licenses" field`)
+}
+
+// ResolveLicenseField parses and validates the "license" field in 
package.json file
+func (resolver *NpmResolver) ResolveLicenseField(rawData []byte) (string, 
bool) {
+       if len(rawData) > 0 {
+               switch rawData[0] {
+               case '"':
+                       var lcs string
+                       _ = json.Unmarshal(rawData, &lcs)
+                       if lcs != "" {
+                               return lcs, true
+                       }
+               case '{':
+                       var lcs Lcs
+                       _ = json.Unmarshal(rawData, &lcs)
+                       if t := lcs.Type; t != "" {
+                               return t, true
+                       }
+               }
+       }
+       return "", false
+}
+
+// ResolveLicensesField parses and validates the "licenses" field in 
package.json file
+// Additionally, the output is converted into the SPDX license expression 
syntax version 2.0 string, like "ISC OR GPL-3.0"
+func (resolver *NpmResolver) ResolveLicensesField(licenses []Lcs) (string, 
bool) {
+       var lcs []string
+       for _, l := range licenses {
+               lcs = append(lcs, l.Type)
+       }
+       if len(lcs) == 0 {
+               return "", false
        }
-       return packageInfo.License, nil
+       return strings.Join(lcs, " OR "), true
 }
 
 // ResolveLcsFile tries to find the license file to identify the license
diff --git a/pkg/deps/npm_test.go b/pkg/deps/npm_test.go
new file mode 100644
index 0000000..ba11961
--- /dev/null
+++ b/pkg/deps/npm_test.go
@@ -0,0 +1,109 @@
+//
+// 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 (
+       "github.com/apache/skywalking-eyes/license-eye/pkg/deps"
+       "io/ioutil"
+       "os"
+       "testing"
+)
+
+var lcsString = `
+{
+  "license": "ISC"
+}`
+var lcsStruct = `
+{
+  "license": {
+    "type" : "ISC",
+    "url" : "https://opensource.org/licenses/ISC";
+  }
+}`
+var lcss = `
+{
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://www.opensource.org/licenses/mit-license.php";
+    },
+    {
+      "type": "Apache-2.0",
+      "url": "https://opensource.org/licenses/apache2.0.php";
+    }
+  ]
+}`
+var lcsStringEmpty = `
+{
+  "license": ""
+}`
+var lcsStructEmpty = `
+{
+  "license": {
+  }
+}`
+var lcssEmpty = `
+{
+  "licenses": [
+  ]
+}`
+var lcssInvalid = `
+{
+  "licenses": {
+  }
+}`
+
+var TestData = []struct {
+       data   string
+       result string
+       hasErr bool
+}{
+       {lcsString, "ISC", false},
+       {lcsStruct, "ISC", false},
+       {lcss, "MIT OR Apache-2.0", false},
+       {lcsStringEmpty, "", true},
+       {lcsStructEmpty, "", true},
+       {lcssEmpty, "", true},
+       {lcssInvalid, "", true},
+}
+
+func TestResolvePkgFile(t *testing.T) {
+       dir, err := ioutil.TempDir(os.TempDir(), "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(dir)
+
+       resolver := new(deps.NpmResolver)
+       for _, data := range TestData {
+               f, err := ioutil.TempFile(dir, "*.json")
+               if err != nil {
+                       t.Fatal(err)
+               }
+               _, err = f.WriteString(data.data)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               lcs, err := resolver.ResolvePkgFile(f.Name())
+               if lcs != data.result && (err != nil) == data.hasErr {
+                       t.Fail()
+               }
+               _ = f.Close()
+       }
+}

Reply via email to